Skip to content

dynaconf API

dynaconf.DynaconfFormatError

Bases: Exception

Error to raise when formatting a lazy variable fails

dynaconf.DynaconfParseError

Bases: Exception

Error to raise when parsing @casts

dynaconf.FlaskDynaconf(app=None, instance_relative_config=False, dynaconf_instance=None, extensions_list=False, **kwargs)

The arguments are. app = The created app dynaconf_args = Extra args to be passed to Dynaconf (validator for example)

All other values are stored as config vars specially::

ENVVAR_PREFIX_FOR_DYNACONF = env prefix for your envvars to be loaded
                    example:
                        if you set to `MYSITE` then
                        export MYSITE_SQL_PORT='@int 5445'

                    with that exported to env you access using:
                        app.config.SQL_PORT
                        app.config.get('SQL_PORT')
                        app.config.get('sql_port')
                        # get is case insensitive
                        app.config['SQL_PORT']

                    Dynaconf uses `@int, @bool, @float, @json` to cast
                    env vars

SETTINGS_FILE_FOR_DYNACONF = The name of the module or file to use as
                            default to load settings. If nothing is
                            passed it will be `settings.*` or value
                            found in `ENVVAR_FOR_DYNACONF`
                            Dynaconf supports
                            .py, .yml, .toml, ini, json
Take a look at settings.yml and .secrets.yml to know the

required settings format.

Settings load order in Dynaconf:

  • Load all defaults and Flask defaults
  • Load all passed variables when applying FlaskDynaconf
  • Update with data in settings files
  • Update with data in environment vars ENVVAR_FOR_DYNACONF_

TOML files are very useful to have envd settings, lets say, production and development.

You can also achieve the same using multiple .py files naming as settings.py, production_settings.py and development_settings.py (see examples/validator)

Example::

app = Flask(__name__)
FlaskDynaconf(
    app,
    ENV='MYSITE',
    SETTINGS_FILE='settings.yml',
    EXTRA_VALUE='You can add additional config vars here'
)

Take a look at examples/flask in Dynaconf repository

Source code in dynaconf/contrib/flask_dynaconf.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def __init__(
    self,
    app=None,
    instance_relative_config=False,
    dynaconf_instance=None,
    extensions_list=False,
    **kwargs,
):
    """kwargs holds initial dynaconf configuration"""
    if not flask_installed:  # pragma: no cover
        raise RuntimeError(
            "To use this extension Flask must be installed "
            "install it with: pip install flask"
        )
    self.kwargs = {k.upper(): v for k, v in kwargs.items()}
    self.kwargs.setdefault("ENVVAR_PREFIX", "FLASK")
    env_prefix = f"{self.kwargs['ENVVAR_PREFIX']}_ENV"  # FLASK_ENV
    self.kwargs.setdefault("ENV_SWITCHER", env_prefix)
    self.kwargs.setdefault("ENVIRONMENTS", True)
    self.kwargs.setdefault("LOAD_DOTENV", True)
    self.kwargs.setdefault(
        "default_settings_paths", dynaconf.DEFAULT_SETTINGS_FILES
    )

    self.dynaconf_instance = dynaconf_instance
    self.instance_relative_config = instance_relative_config
    self.extensions_list = extensions_list
    if app:
        self.init_app(app, **kwargs)

dynaconf.FlaskDynaconf.init_app(app, **kwargs)

kwargs holds initial dynaconf configuration

Source code in dynaconf/contrib/flask_dynaconf.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def init_app(self, app, **kwargs):
    """kwargs holds initial dynaconf configuration"""
    self.kwargs.update(kwargs)
    self.settings = self.dynaconf_instance or dynaconf.LazySettings(
        **self.kwargs
    )
    dynaconf.settings = self.settings  # rebind customized settings
    app.config = self.make_config(app)
    app.dynaconf = self.settings

    if self.extensions_list:
        if not isinstance(self.extensions_list, str):
            self.extensions_list = "EXTENSIONS"
        app.config.load_extensions(self.extensions_list)

dynaconf.LazySettings(wrapped=None, **kwargs)

Bases: LazyObject

Loads settings lazily from multiple sources:

settings = Dynaconf(
    settings_files=["settings.toml"],  # path/glob
    environments=True,                 # activate layered environments
    envvar_prefix="MYAPP",             # `export MYAPP_FOO=bar`
    env_switcher="MYAPP_MODE",         # `export MYAPP_MODE=production`
    load_dotenv=True,                  # read a .env file
)

More options available on https://www.dynaconf.com/configuration/

:param wrapped: a deepcopy of this object will be wrapped (issue #596) :param kwargs: values that overrides default_settings

Source code in dynaconf/base.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def __init__(self, wrapped=None, **kwargs):
    """
    handle initialization for the customization cases

    :param wrapped: a deepcopy of this object will be wrapped (issue #596)
    :param kwargs: values that overrides default_settings
    """
    self._wrapper_class = kwargs.pop("_wrapper_class", Settings)
    self._warn_dynaconf_global_settings = kwargs.pop(
        "warn_dynaconf_global_settings", None
    )  # in 3.0.0 global settings is deprecated

    self.__resolve_config_aliases(kwargs)
    compat_kwargs(kwargs)
    self._kwargs = kwargs
    super().__init__()

    if wrapped:
        if self._django_override:
            # This fixes django issue #596
            self._wrapped = copy.deepcopy(wrapped)
        else:
            self._wrapped = wrapped

dynaconf.LazySettings.configured property

If wrapped is configured

dynaconf.LazySettings.configure(settings_module=None, **kwargs)

Allows user to reconfigure settings object passing a new settings module or separated kwargs

:param settings_module: defines the settings file :param kwargs: override default settings

Source code in dynaconf/base.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def configure(self, settings_module=None, **kwargs):
    """
    Allows user to reconfigure settings object passing a new settings
    module or separated kwargs

    :param settings_module: defines the settings file
    :param kwargs:  override default settings
    """
    default_settings.reload(self._should_load_dotenv)
    environment_var = self._kwargs.get(
        "ENVVAR_FOR_DYNACONF", default_settings.ENVVAR_FOR_DYNACONF
    )
    settings_module = settings_module or os.environ.get(environment_var)
    compat_kwargs(kwargs)
    kwargs.update(self._kwargs)
    self._wrapped = self._wrapper_class(
        settings_module=settings_module, **kwargs
    )

dynaconf.ValidationError(message, *args, **kwargs)

Bases: Exception

Raised when a validation fails

Source code in dynaconf/validator.py
32
33
34
35
def __init__(self, message: str, *args, **kwargs):
    self.details = kwargs.pop("details", [])
    super().__init__(message, *args, **kwargs)
    self.message = message

dynaconf.Validator(*names, must_exist=None, required=None, condition=None, when=None, env=None, messages=None, cast=None, default=empty, description=None, apply_default_on_none=False, **operations)

Validators are conditions attached to settings variables names or patterns::

Validator('MESSAGE', must_exist=True, eq='Hello World')

The above ensure MESSAGE is available in default env and is equal to 'Hello World'

names are a one (or more) names or patterns::

Validator('NAME')
Validator('NAME', 'OTHER_NAME', 'EVEN_OTHER')
Validator(r'^NAME', r'OTHER./*')

The operations are::

eq: value == other
ne: value != other
gt: value > other
lt: value < other
gte: value >= other
lte: value <= other
is_type_of: isinstance(value, type)
is_in:  value in sequence
is_not_in: value not in sequence
identity: value is other
cont: contain value in
len_eq: len(value) == other
len_ne: len(value) != other
len_min: len(value) > other
len_max: len(value) < other

env is which env to be checked, can be a list or default is used.

when holds a validator and its return decides if validator runs or not::

Validator('NAME', must_exist=True, when=Validator('OTHER', eq=2))
# NAME is required only if OTHER eq to 2
# When the very first thing to be performed when passed.
# if no env is passed to `when` it is inherited

must_exist is alias to required requirement. (executed after when)::

settings.get(value, empty) returns non empty

condition is a callable to be executed and return boolean::

Validator('NAME', condition=lambda x: x == 1) # it is executed before operations.

Source code in dynaconf/validator.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def __init__(
    self,
    *names: str,
    must_exist: bool | None = None,
    required: bool | None = None,  # alias for `must_exist`
    condition: Callable[[Any], bool] | None = None,
    when: Validator | None = None,
    env: str | Sequence[str] | None = None,
    messages: dict[str, str] | None = None,
    cast: Callable[[Any], Any] | None = None,
    default: Any | Callable[[Any, Validator], Any] | None = empty,
    description: str | None = None,
    apply_default_on_none: bool | None = False,
    **operations: Any,
) -> None:
    # Copy immutable MappingProxyType as a mutable dict
    self.messages = dict(self.default_messages)
    if messages:
        self.messages.update(messages)

    if when is not None and not isinstance(when, Validator):
        raise TypeError("when must be Validator instance")

    if condition is not None and not callable(condition):
        raise TypeError("condition must be callable")

    self.names = names
    self.must_exist = must_exist if must_exist is not None else required
    self.condition = condition
    self.when = when
    self.cast = cast or (lambda value: value)
    self.operations = operations
    self.default = default
    self.description = description
    self.envs: Sequence[str] | None = None
    self.apply_default_on_none = apply_default_on_none

    # See #585
    self.is_type_of = operations.get("is_type_of")

    if isinstance(env, str):
        self.envs = [env]
    elif isinstance(env, (list, tuple)):
        self.envs = env

dynaconf.Validator.validate(settings, only=None, exclude=None, only_current_env=False)

Raise ValidationError if invalid

Source code in dynaconf/validator.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
def validate(
    self,
    settings: Settings,
    only: str | Sequence | None = None,
    exclude: str | Sequence | None = None,
    only_current_env: bool = False,
) -> None:
    """Raise ValidationError if invalid"""
    # If only or exclude are not set, this value always passes startswith
    only = ensure_a_list(only or [""])
    if only and not isinstance(only[0], str):
        raise ValueError("'only' must be a string or list of strings.")

    exclude = ensure_a_list(exclude)
    if exclude and not isinstance(exclude[0], str):
        raise ValueError("'exclude' must be a string or list of strings.")

    if self.envs is None:
        self.envs = [settings.current_env]

    if self.when is not None:
        try:
            # inherit env if not defined
            if self.when.envs is None:
                self.when.envs = self.envs

            self.when.validate(settings, only=only, exclude=exclude)
        except ValidationError:
            # if when is invalid, return canceling validation flow
            return

    if only_current_env:
        if settings.current_env.upper() in map(
            lambda s: s.upper(), self.envs
        ):
            self._validate_items(
                settings, settings.current_env, only=only, exclude=exclude
            )
        return

    # If only using current_env, skip using_env decoration (reload)
    if (
        len(self.envs) == 1
        and self.envs[0].upper() == settings.current_env.upper()
    ):
        self._validate_items(
            settings, settings.current_env, only=only, exclude=exclude
        )
        return

    for env in self.envs:
        env_settings: Settings = settings.from_env(env)
        self._validate_items(env_settings, only=only, exclude=exclude)
        # merge source metadata into original settings for history inspect
        settings._loaded_by_loaders.update(env_settings._loaded_by_loaders)

dynaconf.add_converter(converter_key, func)

Adds a new converter to the converters dict

Source code in dynaconf/utils/parse_conf.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
def add_converter(converter_key, func):
    """Adds a new converter to the converters dict"""
    if not converter_key.startswith("@"):
        converter_key = f"@{converter_key}"

    converters[converter_key] = wraps(func)(
        lambda value: value.set_casting(func)
        if isinstance(value, Lazy)
        else Lazy(
            value,
            casting=func,
            formatter=BaseFormatter(lambda x, **_: x, converter_key),
        )
    )

dynaconf.get_history(obj, key=None, *, filter_callable=None, include_internal=False, history_limit=None)

Gets data from settings.loaded_by_loaders in order of loading with optional filtering options.

Returns a list of dict in new-first order, where the dict contains the data and it's source metadata.

:param obj: Setting object which contain the data :param key: Key path to desired key. Use all if not provided :param filter_callable: Takes SourceMetadata and returns a boolean :param include_internal: If True, include internal loaders (e.g. defaults). This has effect only if key is not provided. history_limit: limits how many entries are shown

Example

settings = Dynaconf(...) _get_history(settings) [ { "loader": "yaml" "identifier": "path/to/file.yml" "env": "default" "data": {"foo": 123, "spam": "eggs"} }, ... ]

Source code in dynaconf/utils/inspect.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
def get_history(
    obj: Settings | LazySettings,
    key: str | None = None,
    *,
    filter_callable: Callable[[SourceMetadata], bool] | None = None,
    include_internal: bool = False,
    history_limit: int | None = None,
) -> list[dict]:
    """
    Gets data from `settings.loaded_by_loaders` in order of loading with
    optional filtering options.

    Returns a list of dict in new-first order, where the dict contains the
    data and it's source metadata.

    :param obj: Setting object which contain the data
    :param key: Key path to desired key. Use all if not provided
    :param filter_callable: Takes SourceMetadata and returns a boolean
    :param include_internal: If True, include internal loaders (e.g. defaults).
        This has effect only if key is not provided.
    history_limit: limits how many entries are shown

    Example:
        >>> settings = Dynaconf(...)
        >>> _get_history(settings)
        [
            {
                "loader": "yaml"
                "identifier": "path/to/file.yml"
                "env": "default"
                "data": {"foo": 123, "spam": "eggs"}
            },
            ...
        ]
    """
    if filter_callable is None:
        filter_callable = lambda x: True  # noqa

    sep = obj.get("NESTED_SEPARATOR_FOR_DYNACONF", "__")

    # trigger key based hooks
    if key:
        obj.get(key)  # noqa

    internal_identifiers = ["default_settings", "_root_path"]
    result = []
    for source_metadata, data in obj._loaded_by_loaders.items():
        # filter by source_metadata
        if filter_callable(source_metadata) is False:
            continue

        # filter by internal identifiers
        if (
            not key
            and include_internal is False
            and source_metadata.identifier in internal_identifiers
        ):
            continue  # skip: internal loaders

        # filter by key path
        try:
            data = _get_data_by_key(data, key, sep=sep) if key else data
        except KeyError:
            continue  # skip: source doesn't contain the requested key

        # Normalize output
        data = _ensure_serializable(data)
        result.append({**source_metadata._asdict(), "value": data})

    if key and not result:
        # Key may be set in obj but history not tracked
        if (data := obj.get(key, empty)) is not empty:
            generic_source_metadata = SourceMetadata(
                loader="undefined",
                identifier="undefined",
            )
            data = _ensure_serializable(data)
            result.append({**generic_source_metadata._asdict(), "value": data})

        # Raise if still not found
        if key and not result:
            raise KeyNotFoundError(f"The requested key was not found: {key!r}")

    return result

dynaconf.inspect_settings(settings, key=None, env=None, *, new_first=True, history_limit=None, include_internal=False, to_file=None, print_report=False, dumper=None, report_builder=None)

Print and return the loading history of a settings object.

Optional arguments must be provided as kwargs.

:param settings: A Dynaconf instance :param key: String dotted path. E.g "path.to.key" :param env: Filter by this env

:param new_first: If True, uses newest to oldest loading order :param history_limit: Limits how many entries are shown :param include_internal: If True, include internal loaders (e.g. defaults). This has effect only if key is not provided. :param to_file: If specified, write to this filename :param print_report: If true, prints the dumped report to stdout :param dumper: Accepts preset strings (e.g. "yaml", "json") or custom dumper callable (dict, TextIO) -> None. Defaults to "yaml" :param report_builder: if provided, it is used to generate the report

:return: Dict with a dict containing report data :rtype: dict

Source code in dynaconf/utils/inspect.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
def inspect_settings(
    settings: Settings | LazySettings,
    key: str | None = None,
    env: str | None = None,
    *,
    new_first: bool = True,
    history_limit: int | None = None,
    include_internal: bool = False,
    to_file: str | PosixPath | None = None,
    print_report: bool = False,
    dumper: DumperPreset | DumperType | None = None,
    report_builder: ReportBuilderType | None = None,
):
    """
    Print and return the loading history of a settings object.

    Optional arguments must be provided as kwargs.

    :param settings: A Dynaconf instance
    :param key: String dotted path. E.g "path.to.key"
    :param env: Filter by this env

    :param new_first: If True, uses newest to oldest loading order
    :param history_limit: Limits how many entries are shown
    :param include_internal: If True, include internal loaders (e.g. defaults).
        This has effect only if key is not provided.
    :param to_file: If specified, write to this filename
    :param print_report: If true, prints the dumped report to stdout
    :param dumper: Accepts preset strings (e.g. "yaml", "json") or custom
        dumper callable ``(dict, TextIO) -> None``. Defaults to "yaml"
    :param report_builder: if provided, it is used to generate the report

    :return: Dict with a dict containing report data
    :rtype: dict
    """
    # choose dumper and report builder
    if dumper is None:
        _dumper = builtin_dumpers["yaml"]
    elif isinstance(dumper, str):
        _dumper = builtin_dumpers.get(dumper)
        if _dumper is None:
            raise OutputFormatError(
                f"The desired format is not available: {dumper!r}"
            )
    else:
        _dumper = dumper

    _report_builder = report_builder or _default_report_builder

    # get history and apply optional arguments
    original_settings = settings

    env_filter = None  # type: ignore
    if env:
        settings = settings.from_env(env)
        registered_envs = {
            src_meta.env for src_meta in settings._loaded_by_loaders.keys()
        }
        if env.lower() not in registered_envs:
            raise EnvNotFoundError(f"The requested env is not valid: {env!r}")

        def env_filter(src: SourceMetadata) -> bool:  # noqa: F811
            return src.env.lower() == env.lower()

    history = get_history(
        original_settings,
        key=key,
        filter_callable=env_filter,
        include_internal=include_internal,
    )

    if new_first:
        history.reverse()

    if history_limit:
        history = history[:history_limit]

    if key:
        current_value = settings.get(key)
    else:
        current_value = settings.as_dict()

    # format output
    dict_report = _report_builder(
        history=history,
        current=current_value,
        key=key,
        env=env,
        new_first=new_first,
        history_limit=history_limit,
        include_internal=include_internal,
    )

    dict_report["current"] = _ensure_serializable(dict_report["current"])

    # write to stdout AND/OR to file AND return
    if to_file is not None:
        _encoding = settings.get("ENCODER_FOR_DYNACONF")
        with open(to_file, "w", encoding=_encoding) as file:
            _dumper(dict_report, file)

    if print_report is True:
        _dumper(dict_report, sys.stdout)

    return dict_report