Skip to content

Pydantic Settings

PydanticBaseSettingsSource

PydanticBaseSettingsSource(settings_cls)

Bases: ABC

Abstract base class for settings sources, every settings source classes should inherit from it.

Source code in pydantic_settings/sources.py
41
42
43
def __init__(self, settings_cls: type[BaseSettings]):
    self.settings_cls = settings_cls
    self.config = settings_cls.model_config

get_field_value abstractmethod

get_field_value(field, field_name)

Gets the value, the key for model creation, and a flag to determine whether value is complex.

This is an abstract method that should be overridden in every settings source classes.

Parameters:

Name Type Description Default
field FieldInfo

The field.

required
field_name str

The field name.

required

Returns:

Type Description
tuple[Any, str, bool]

A tuple contains the key, value and a flag to determine whether value is complex.

Source code in pydantic_settings/sources.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@abstractmethod
def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
    """
    Gets the value, the key for model creation, and a flag to determine whether value is complex.

    This is an abstract method that should be overridden in every settings source classes.

    Args:
        field: The field.
        field_name: The field name.

    Returns:
        A tuple contains the key, value and a flag to determine whether value is complex.
    """
    pass

field_is_complex

field_is_complex(field)

Checks whether a field is complex, in which case it will attempt to be parsed as JSON.

Parameters:

Name Type Description Default
field FieldInfo

The field.

required

Returns:

Type Description
bool

Whether the field is complex.

Source code in pydantic_settings/sources.py
61
62
63
64
65
66
67
68
69
70
71
def field_is_complex(self, field: FieldInfo) -> bool:
    """
    Checks whether a field is complex, in which case it will attempt to be parsed as JSON.

    Args:
        field: The field.

    Returns:
        Whether the field is complex.
    """
    return _annotation_is_complex(field.annotation, field.metadata)

prepare_field_value

prepare_field_value(
    field_name, field, value, value_is_complex
)

Prepares the value of a field.

Parameters:

Name Type Description Default
field_name str

The field name.

required
field FieldInfo

The field.

required
value Any

The value of the field that has to be prepared.

required
value_is_complex bool

A flag to determine whether value is complex.

required

Returns:

Type Description
Any

The prepared value.

Source code in pydantic_settings/sources.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
    """
    Prepares the value of a field.

    Args:
        field_name: The field name.
        field: The field.
        value: The value of the field that has to be prepared.
        value_is_complex: A flag to determine whether value is complex.

    Returns:
        The prepared value.
    """
    if value is not None and (self.field_is_complex(field) or value_is_complex):
        return self.decode_complex_value(field_name, field, value)
    return value

decode_complex_value

decode_complex_value(field_name, field, value)

Decode the value for a complex field

Parameters:

Name Type Description Default
field_name str

The field name.

required
field FieldInfo

The field.

required
value Any

The value of the field that has to be prepared.

required

Returns:

Type Description
Any

The decoded value for further preparation

Source code in pydantic_settings/sources.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def decode_complex_value(self, field_name: str, field: FieldInfo, value: Any) -> Any:
    """
    Decode the value for a complex field

    Args:
        field_name: The field name.
        field: The field.
        value: The value of the field that has to be prepared.

    Returns:
        The decoded value for further preparation
    """
    return json.loads(value)

BaseSettings

BaseSettings(
    __pydantic_self__,
    _case_sensitive=None,
    _env_prefix=None,
    _env_file=ENV_FILE_SENTINEL,
    _env_file_encoding=None,
    _env_nested_delimiter=None,
    _secrets_dir=None,
    **values
)

Bases: BaseModel

Base class for settings, allowing values to be overridden by environment variables.

This is useful in production for secrets you do not wish to save in code, it plays nicely with docker(-compose), Heroku and any 12 factor app design.

All the below attributes can be set via model_config.

Parameters:

Name Type Description Default
_case_sensitive bool | None

Whether environment variables names should be read with case-sensitivity. Defaults to None.

None
_env_prefix str | None

Prefix for all environment variables. Defaults to None.

None
_env_file DotenvType | None

The env file(s) to load settings values from. Defaults to Path(''), which means that the value from model_config['env_file'] should be used. You can also pass None to indicate that environment variables should not be loaded from an env file.

ENV_FILE_SENTINEL
_env_file_encoding str | None

The env file encoding, e.g. 'latin-1'. Defaults to None.

None
_env_nested_delimiter str | None

The nested env values delimiter. Defaults to None.

None
_secrets_dir str | Path | None

The secret files directory. Defaults to None.

None
Source code in pydantic_settings/main.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __init__(
    __pydantic_self__,
    _case_sensitive: bool | None = None,
    _env_prefix: str | None = None,
    _env_file: DotenvType | None = ENV_FILE_SENTINEL,
    _env_file_encoding: str | None = None,
    _env_nested_delimiter: str | None = None,
    _secrets_dir: str | Path | None = None,
    **values: Any,
) -> None:
    # Uses something other than `self` the first arg to allow "self" as a settable attribute
    super().__init__(
        **__pydantic_self__._settings_build_values(
            values,
            _case_sensitive=_case_sensitive,
            _env_prefix=_env_prefix,
            _env_file=_env_file,
            _env_file_encoding=_env_file_encoding,
            _env_nested_delimiter=_env_nested_delimiter,
            _secrets_dir=_secrets_dir,
        )
    )

settings_customise_sources classmethod

settings_customise_sources(
    settings_cls,
    init_settings,
    env_settings,
    dotenv_settings,
    file_secret_settings,
)

Define the sources and their order for loading the settings values.

Parameters:

Name Type Description Default
settings_cls type[BaseSettings]

The Settings class.

required
init_settings PydanticBaseSettingsSource

The InitSettingsSource instance.

required
env_settings PydanticBaseSettingsSource

The EnvSettingsSource instance.

required
dotenv_settings PydanticBaseSettingsSource

The DotEnvSettingsSource instance.

required
file_secret_settings PydanticBaseSettingsSource

The SecretsSettingsSource instance.

required

Returns:

Type Description
tuple[PydanticBaseSettingsSource, ...]

A tuple containing the sources and their order for loading the settings values.

Source code in pydantic_settings/main.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@classmethod
def settings_customise_sources(
    cls,
    settings_cls: type[BaseSettings],
    init_settings: PydanticBaseSettingsSource,
    env_settings: PydanticBaseSettingsSource,
    dotenv_settings: PydanticBaseSettingsSource,
    file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
    """
    Define the sources and their order for loading the settings values.

    Args:
        settings_cls: The Settings class.
        init_settings: The `InitSettingsSource` instance.
        env_settings: The `EnvSettingsSource` instance.
        dotenv_settings: The `DotEnvSettingsSource` instance.
        file_secret_settings: The `SecretsSettingsSource` instance.

    Returns:
        A tuple containing the sources and their order for loading the settings values.
    """
    return init_settings, env_settings, dotenv_settings, file_secret_settings

InitSettingsSource

InitSettingsSource(settings_cls, init_kwargs)

Bases: PydanticBaseSettingsSource

Source class for loading values provided during settings class initialization.

Source code in pydantic_settings/sources.py
114
115
116
def __init__(self, settings_cls: type[BaseSettings], init_kwargs: dict[str, Any]):
    self.init_kwargs = init_kwargs
    super().__init__(settings_cls)

SecretsSettingsSource

SecretsSettingsSource(
    settings_cls,
    secrets_dir=None,
    case_sensitive=None,
    env_prefix=None,
)

Bases: PydanticBaseEnvSettingsSource

Source class for loading settings values from secret files.

Source code in pydantic_settings/sources.py
275
276
277
278
279
280
281
282
283
def __init__(
    self,
    settings_cls: type[BaseSettings],
    secrets_dir: str | Path | None = None,
    case_sensitive: bool | None = None,
    env_prefix: str | None = None,
) -> None:
    super().__init__(settings_cls, case_sensitive, env_prefix)
    self.secrets_dir = secrets_dir if secrets_dir is not None else self.config.get('secrets_dir')

find_case_path classmethod

find_case_path(dir_path, file_name, case_sensitive)

Find a file within path's directory matching filename, optionally ignoring case.

Parameters:

Name Type Description Default
dir_path Path

Directory path.

required
file_name str

File name.

required
case_sensitive bool

Whether to search for file name case sensitively.

required

Returns:

Type Description
Path | None

Whether file path or None if file does not exist in directory.

Source code in pydantic_settings/sources.py
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
@classmethod
def find_case_path(cls, dir_path: Path, file_name: str, case_sensitive: bool) -> Path | None:
    """
    Find a file within path's directory matching filename, optionally ignoring case.

    Args:
        dir_path: Directory path.
        file_name: File name.
        case_sensitive: Whether to search for file name case sensitively.

    Returns:
        Whether file path or `None` if file does not exist in directory.
    """
    for f in dir_path.iterdir():
        if f.name == file_name:
            return f
        elif not case_sensitive and f.name.lower() == file_name.lower():
            return f
    return None

get_field_value

get_field_value(field, field_name)

Gets the value for field from secret file and a flag to determine whether value is complex.

Parameters:

Name Type Description Default
field FieldInfo

The field.

required
field_name str

The field name.

required

Returns:

Type Description
tuple[Any, str, bool]

A tuple contains the key, value if the file exists otherwise None, and a flag to determine whether value is complex.

Source code in pydantic_settings/sources.py
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
    """
    Gets the value for field from secret file and a flag to determine whether value is complex.

    Args:
        field: The field.
        field_name: The field name.

    Returns:
        A tuple contains the key, value if the file exists otherwise `None`, and
            a flag to determine whether value is complex.
    """

    for field_key, env_name, value_is_complex in self._extract_field_info(field, field_name):
        path = self.find_case_path(self.secrets_path, env_name, self.case_sensitive)
        if not path:
            # path does not exist, we currently don't return a warning for this
            continue

        if path.is_file():
            return path.read_text().strip(), field_key, value_is_complex
        else:
            warnings.warn(
                f'attempted to load secret file "{path}" but found a {path_type_label(path)} instead.',
                stacklevel=4,
            )

    return None, field_key, value_is_complex

EnvSettingsSource

EnvSettingsSource(
    settings_cls,
    case_sensitive=None,
    env_prefix=None,
    env_nested_delimiter=None,
)

Bases: PydanticBaseEnvSettingsSource

Source class for loading settings values from environment variables.

Source code in pydantic_settings/sources.py
363
364
365
366
367
368
369
370
371
372
373
374
375
376
def __init__(
    self,
    settings_cls: type[BaseSettings],
    case_sensitive: bool | None = None,
    env_prefix: str | None = None,
    env_nested_delimiter: str | None = None,
) -> None:
    super().__init__(settings_cls, case_sensitive, env_prefix)
    self.env_nested_delimiter = (
        env_nested_delimiter if env_nested_delimiter is not None else self.config.get('env_nested_delimiter')
    )
    self.env_prefix_len = len(self.env_prefix)

    self.env_vars = self._load_env_vars()

get_field_value

get_field_value(field, field_name)

Gets the value for field from environment variables and a flag to determine whether value is complex.

Parameters:

Name Type Description Default
field FieldInfo

The field.

required
field_name str

The field name.

required

Returns:

Type Description
tuple[Any, str, bool]

A tuple contains the key, value if the file exists otherwise None, and a flag to determine whether value is complex.

Source code in pydantic_settings/sources.py
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
    """
    Gets the value for field from environment variables and a flag to determine whether value is complex.

    Args:
        field: The field.
        field_name: The field name.

    Returns:
        A tuple contains the key, value if the file exists otherwise `None`, and
            a flag to determine whether value is complex.
    """

    env_val: str | None = None
    for field_key, env_name, value_is_complex in self._extract_field_info(field, field_name):
        env_val = self.env_vars.get(env_name)
        if env_val is not None:
            break

    return env_val, field_key, value_is_complex

prepare_field_value

prepare_field_value(
    field_name, field, value, value_is_complex
)

Prepare value for the field.

  • Extract value for nested field.
  • Deserialize value to python object for complex field.

Parameters:

Name Type Description Default
field FieldInfo

The field.

required
field_name str

The field name.

required

Returns:

Type Description
Any

A tuple contains prepared value for the field.

Raises:

Type Description
ValuesError

When There is an error in deserializing value for complex field.

Source code in pydantic_settings/sources.py
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
    """
    Prepare value for the field.

    * Extract value for nested field.
    * Deserialize value to python object for complex field.

    Args:
        field: The field.
        field_name: The field name.

    Returns:
        A tuple contains prepared value for the field.

    Raises:
        ValuesError: When There is an error in deserializing value for complex field.
    """
    is_complex, allow_parse_failure = self._field_is_complex(field)
    if is_complex or value_is_complex:
        if value is None:
            # field is complex but no value found so far, try explode_env_vars
            env_val_built = self.explode_env_vars(field_name, field, self.env_vars)
            if env_val_built:
                return env_val_built
        else:
            # field is complex and there's a value, decode that as JSON, then add explode_env_vars
            try:
                value = self.decode_complex_value(field_name, field, value)
            except ValueError as e:
                if not allow_parse_failure:
                    raise e

            if isinstance(value, dict):
                return deep_update(value, self.explode_env_vars(field_name, field, self.env_vars))
            else:
                return value
    elif value is not None:
        # simplest case, field is not complex, we only need to add the value if it was found
        return value

next_field staticmethod

next_field(field, key)

Find the field in a sub model by key(env name)

By having the following models
class SubSubModel(BaseSettings):
    dvals: Dict

class SubModel(BaseSettings):
    vals: list[str]
    sub_sub_model: SubSubModel

class Cfg(BaseSettings):
    sub_model: SubModel
Then

next_field(sub_model, 'vals') Returns the vals field of SubModel class next_field(sub_model, 'sub_sub_model') Returns sub_sub_model field of SubModel class

Parameters:

Name Type Description Default
field FieldInfo | None

The field.

required
key str

The key (env name).

required

Returns:

Type Description
FieldInfo | None

Field if it finds the next field otherwise None.

Source code in pydantic_settings/sources.py
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@staticmethod
def next_field(field: FieldInfo | None, key: str) -> FieldInfo | None:
    """
    Find the field in a sub model by key(env name)

    By having the following models:

        ```py
        class SubSubModel(BaseSettings):
            dvals: Dict

        class SubModel(BaseSettings):
            vals: list[str]
            sub_sub_model: SubSubModel

        class Cfg(BaseSettings):
            sub_model: SubModel
        ```

    Then:
        next_field(sub_model, 'vals') Returns the `vals` field of `SubModel` class
        next_field(sub_model, 'sub_sub_model') Returns `sub_sub_model` field of `SubModel` class

    Args:
        field: The field.
        key: The key (env name).

    Returns:
        Field if it finds the next field otherwise `None`.
    """
    if not field or origin_is_union(get_origin(field.annotation)):
        # no support for Unions of complex BaseSettings fields
        return None
    elif field.annotation and hasattr(field.annotation, 'model_fields') and field.annotation.model_fields.get(key):
        return field.annotation.model_fields[key]

    return None

explode_env_vars

explode_env_vars(field_name, field, env_vars)

Process env_vars and extract the values of keys containing env_nested_delimiter into nested dictionaries.

This is applied to a single field, hence filtering by env_var prefix.

Parameters:

Name Type Description Default
field_name str

The field name.

required
field FieldInfo

The field.

required
env_vars Mapping[str, str | None]

Environment variables.

required

Returns:

Type Description
dict[str, Any]

A dictionaty contains extracted values from nested env values.

Source code in pydantic_settings/sources.py
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
def explode_env_vars(self, field_name: str, field: FieldInfo, env_vars: Mapping[str, str | None]) -> dict[str, Any]:
    """
    Process env_vars and extract the values of keys containing env_nested_delimiter into nested dictionaries.

    This is applied to a single field, hence filtering by env_var prefix.

    Args:
        field_name: The field name.
        field: The field.
        env_vars: Environment variables.

    Returns:
        A dictionaty contains extracted values from nested env values.
    """
    prefixes = [
        f'{env_name}{self.env_nested_delimiter}' for _, env_name, _ in self._extract_field_info(field, field_name)
    ]
    result: dict[str, Any] = {}
    for env_name, env_val in env_vars.items():
        if not any(env_name.startswith(prefix) for prefix in prefixes):
            continue
        # we remove the prefix before splitting in case the prefix has characters in common with the delimiter
        env_name_without_prefix = env_name[self.env_prefix_len :]
        _, *keys, last_key = env_name_without_prefix.split(self.env_nested_delimiter)
        env_var = result
        target_field: FieldInfo | None = field
        for key in keys:
            target_field = self.next_field(target_field, key)
            env_var = env_var.setdefault(key, {})

        # get proper field with last_key
        target_field = self.next_field(target_field, last_key)

        # check if env_val maps to a complex field and if so, parse the env_val
        if target_field and env_val:
            is_complex, allow_json_failure = self._field_is_complex(target_field)
            if is_complex:
                try:
                    env_val = self.decode_complex_value(last_key, target_field, env_val)
                except ValueError as e:
                    if not allow_json_failure:
                        raise e
        env_var[last_key] = env_val

    return result

DotEnvSettingsSource

DotEnvSettingsSource(
    settings_cls,
    env_file=ENV_FILE_SENTINEL,
    env_file_encoding=None,
    case_sensitive=None,
    env_prefix=None,
    env_nested_delimiter=None,
)

Bases: EnvSettingsSource

Source class for loading settings values from env files.

Source code in pydantic_settings/sources.py
556
557
558
559
560
561
562
563
564
565
566
567
568
569
def __init__(
    self,
    settings_cls: type[BaseSettings],
    env_file: DotenvType | None = ENV_FILE_SENTINEL,
    env_file_encoding: str | None = None,
    case_sensitive: bool | None = None,
    env_prefix: str | None = None,
    env_nested_delimiter: str | None = None,
) -> None:
    self.env_file = env_file if env_file != ENV_FILE_SENTINEL else settings_cls.model_config.get('env_file')
    self.env_file_encoding = (
        env_file_encoding if env_file_encoding is not None else settings_cls.model_config.get('env_file_encoding')
    )
    super().__init__(settings_cls, case_sensitive, env_prefix, env_nested_delimiter)