Settings management

One of pydantic's most useful applications is to define default settings, and allow them to be overridden by environment variables or keyword arguments (e.g. in unit tests).

from typing import Set

from devtools import debug
from pydantic import BaseModel, BaseSettings, PyObject, RedisDsn, PostgresDsn, Field

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

class Settings(BaseSettings):
    auth_key: str
    api_key: str = Field(..., env='my_api_key')

    redis_dsn: RedisDsn = 'redis://user:pass@localhost:6379/1'
    pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar'

    special_function: PyObject = '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()

    class Config:
        env_prefix = 'my_prefix_'  # defaults to no prefix, e.g. ""
        fields = {
            'auth_key': {
                'env': 'my_auth_key',
            },
            'redis_dsn': {
                'env': ['service_redis_dsn', 'redis_url']
            }
        }

"""
When calling with
my_auth_key=a \
MY_API_KEY=b \
my_prefix_domains='["foo.com", "bar.com"]' \
python docs/examples/settings.py 
"""
debug(Settings().dict())
"""
docs/examples/settings.py:45 <module>
  Settings().dict(): {
    'auth_key': 'a',
    'api_key': 'b',
    'redis_dsn': <RedisDsn('redis://user:pass@localhost:6379/1' scheme='redis' ...)>,
    'pg_dsn': <PostgresDsn('postgres://user:pass@localhost:5432/foobar' scheme='postgres' ...)>,
    'special_function': <built-in function cos>,
    'domains': {'bar.com', 'foo.com'},
    'more_settings': {'foo': 'bar', 'apple': 1},
  } (dict) len=7
"""

(This script is complete, it should run "as is")

The following rules apply when finding and interpreting environment variables:

  • When no custom environment variable name(s) are given, the environment variable name is built using the field name and prefix, eg to override special_function use export my_prefix_special_function='foo.bar', the default prefix is an empty string. aliases are ignored for building the environment variable name.
  • Custom environment variable names can be set using with Config.fields.[field name].env or Field(..., env=...), in the above example auth_key and api_key's environment variable setups are the equivalent.
  • In these cases env can either be a string or a list of strings. When a list of strings order is important: in the case of redis_dsn service_redis_dsn would take precedence over redis_url.

Warning

Since v1.0 pydantic does not consider field aliases when finding environment variables to populate settings models, use env instead as described above.

To aid the transition from aliases to env, a warning will be raised when aliases are used on settings models without a custom env var name. If you really mean to use aliases, either ignore the warning or set env to suppress it.

By default BaseSettings considers field values in the following priority (where 3. has the highest priority and overrides the other two):

  1. The default values set in your Settings class.
  2. Environment variables, e.g. my_prefix_special_function as described above.
  3. Arguments passed to the Settings class on initialisation.

Complex types like list, set, dict and sub-models can be set by using JSON environment variables.

Case-sensitivity can be turned on through Config:

from pydantic import BaseSettings


class Settings(BaseSettings):
    redis_host = 'localhost'

    class Config:
        case_sensitive = True

When case_sensitive is True, the environment variable must be in all-caps, so in this example redis_host could only be modified via export REDIS_HOST.

Note

On Windows, python's os module always treats environment variables as case-insensitive, so the case_sensitive config setting will have no effect -- settings will always be updated ignoring case.