Skip to content

Commit

Permalink
Adopt pydantic-settings docs for Pydantic V2
Browse files Browse the repository at this point in the history
  • Loading branch information
hramezani committed Apr 21, 2023
1 parent 0ecac22 commit e6e28da
Show file tree
Hide file tree
Showing 9 changed files with 762 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
[![license](https://img.shields.io/github/license/pydantic/pydantic-settings.svg)](https://github.com/pydantic/pydantic-settings/blob/main/LICENSE)

**Work in Progress**, see https://github.com/pydantic/pydantic/pull/4492

{!docs/index.md!}
63 changes: 63 additions & 0 deletions docs/examples/settings_add_custom_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
from pathlib import Path
from typing import Any, Dict, Tuple, Type

from pydantic import ConfigDict
from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource


class JsonConfigSettingsSource(PydanticBaseSettingsSource):
"""
A simple settings source class that loads variables from a JSON file
at the project's root.
Here we happen to choose to use the `env_file_encoding` from Config
when reading `config.json`
"""

def get_field_value(self, field: FieldInfo, field_name: str) -> Tuple[Any, str, bool]:
encoding = self.config.get('env_file_encoding')
file_content_json = json.loads(Path('config.json').read_text(encoding))
fiel_value = file_content_json.get(field_name)
return fiel_value, field_name, False


def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
return value

def __call__(self) -> Dict[str, Any]:
d: Dict[str, Any] = {}

for field_name, field in self.settings_cls.model_fields.items():
field_value, field_key, value_is_complex = self.get_field_value(field, field_name)
field_value = self.prepare_field_value(field_name, field, field_value, value_is_complex)
if field_value is not None:
d[field_key] = field_value

return d


class Settings(BaseSettings):
foobar: str

model_config = ConfigDict(env_file_encoding='utf-8')

@classmethod
def customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
JsonConfigSettingsSource(settings_cls),
env_settings,
file_secret_settings,
)


print(Settings())
8 changes: 8 additions & 0 deletions docs/examples/settings_case_sensitive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pydantic import ConfigDict
from pydantic_settings import BaseSettings


class Settings(BaseSettings):
redis_host: str = 'localhost'

model_config = ConfigDict(case_sensitive=True)
23 changes: 23 additions & 0 deletions docs/examples/settings_disable_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Tuple, Type

from pydantic_settings import BaseSettings, PydanticBaseSettingsSource


class Settings(BaseSettings):
my_api_key: str

@classmethod
def customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
# here we choose to ignore arguments from init_settings
return env_settings, file_secret_settings


print(Settings(my_api_key='this is ignored'))
# requires: `MY_API_KEY` env variable to be set, e.g. `export MY_API_KEY=xxx`
21 changes: 21 additions & 0 deletions docs/examples/settings_env_priority.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Tuple, Type
from pydantic import PostgresDsn
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource


class Settings(BaseSettings):
database_dsn: PostgresDsn

@classmethod
def customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return env_settings, init_settings, file_secret_settings


print(Settings(database_dsn='postgres://postgres@localhost:5432/kwargs_db'))
56 changes: 56 additions & 0 deletions docs/examples/settings_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import Any, Callable, Set

from pydantic import (
AliasChoices,
BaseModel,
ConfigDict,
ImportString,
RedisDsn,
PostgresDsn,
AmqpDsn,
Field,
)
from pydantic_settings import BaseSettings


class SubModel(BaseModel):
foo: str = 'bar'
apple: int = 1


class Settings(BaseSettings):
auth_key: str = Field(validation_alias='my_auth_key')
api_key: str = Field(validation_alias='my_api_key')

redis_dsn: RedisDsn = Field(
'redis://user:pass@localhost:6379/1',
validation_alias=AliasChoices('service_redis_dsn', 'redis_url')
)
pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar'
amqp_dsn: AmqpDsn = 'amqp://user:pass@localhost:5672/'

special_function: ImportString[Callable[[Any], Any]] = 'math.cos'

# to override domains:
# export my_prefix_domains='["foo.com", "bar.com"]'
domains: Set[str] = set()

# to override more_settings:
# export my_prefix_more_settings='{"foo": "x", "apple": 1}'
more_settings: SubModel = SubModel()

model_config = ConfigDict(env_prefix = 'my_prefix_') # defaults to no prefix, i.e. ""

print(Settings().model_dump())
"""
{
'auth_key': 'xxx',
'api_key': 'xxx',
'redis_dsn': Url('redis://user:pass@localhost:6379/1'),
'pg_dsn': Url('postgres://user:pass@localhost:5432/foobar'),
'amqp_dsn': Url('amqp://user:pass@localhost:5672/'),
'special_function': <built-in function cos>,
'domains': set(),
'more_settings': {'foo': 'bar', 'apple': 1},
}
"""
23 changes: 23 additions & 0 deletions docs/examples/settings_nested_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from pydantic import BaseModel, ConfigDict
from pydantic_settings import BaseSettings


class DeepSubModel(BaseModel):
v4: str


class SubModel(BaseModel):
v1: str
v2: bytes
v3: int
deep: DeepSubModel


class Settings(BaseSettings):
v0: str
sub_model: SubModel

model_config = ConfigDict(env_nested_delimiter='__')


print(Settings().model_dump())
32 changes: 32 additions & 0 deletions docs/examples/settings_with_custom_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import json
import os
from typing import Any, List, Tuple, Type

from pydantic.fields import FieldInfo
from pydantic_settings import BaseSettings, EnvSettingsSource, PydanticBaseSettingsSource


class MyCustomSource(EnvSettingsSource):
def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
if field_name == 'numbers':
return [int(x) for x in value.split(',')]
return json.loads(value)


class Settings(BaseSettings):
numbers: List[int]

@classmethod
def customise_sources(
cls,
settings_cls: Type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> Tuple[PydanticBaseSettingsSource, ...]:
return (MyCustomSource(settings_cls),)


os.environ['numbers'] = '1,2,3'
print(Settings().model_dump())
Loading

0 comments on commit e6e28da

Please sign in to comment.