Skip to content

dyanconf API

dynaconf.DynaconfFormatError

Bases: Exception

Error to raise when formatting a lazy variable fails

Source code in dynaconf/utils/parse_conf.py
35
36
class DynaconfFormatError(Exception):
    """Error to raise when formatting a lazy variable fails"""

dynaconf.DynaconfParseError

Bases: Exception

Error to raise when parsing @casts

Source code in dynaconf/utils/parse_conf.py
39
40
class DynaconfParseError(Exception):
    """Error to raise when parsing @casts"""

dynaconf.FlaskDynaconf

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
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 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
class FlaskDynaconf:
    """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

    ATTENTION: 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

    """

    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)

    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)

    def make_config(self, app):
        root_path = app.root_path
        if self.instance_relative_config:  # pragma: no cover
            root_path = app.instance_path
        if self.dynaconf_instance:
            self.settings.update(self.kwargs)
        return DynaconfConfig(
            root_path=root_path,
            defaults=app.config,
            _settings=self.settings,
            _app=app,
        )

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

kwargs holds initial dynaconf configuration

Source code in dynaconf/contrib/flask_dynaconf.py
 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
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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

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/

Source code in dynaconf/base.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 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
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
class LazySettings(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/
    """

    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

    def __resolve_config_aliases(self, kwargs):
        """takes aliases for _FOR_DYNACONF configurations

        e.g: ROOT_PATH='/' is transformed into `ROOT_PATH_FOR_DYNACONF`
        """

        misspells = {
            "settings_files": "settings_file",
            "SETTINGS_FILES": "SETTINGS_FILE",
            "environment": "environments",
            "ENVIRONMENT": "ENVIRONMENTS",
        }
        for misspell, correct in misspells.items():
            if misspell in kwargs:
                kwargs[correct] = kwargs.pop(misspell)

        for_dynaconf_keys = {
            key
            for key in UPPER_DEFAULT_SETTINGS
            if key.endswith("_FOR_DYNACONF")
        }
        aliases = {
            key.upper()
            for key in kwargs
            if f"{key.upper()}_FOR_DYNACONF" in for_dynaconf_keys
        }
        for alias in aliases:
            value = kwargs.pop(alias, empty)
            if value is empty:
                value = kwargs.pop(alias.lower())
            kwargs[f"{alias}_FOR_DYNACONF"] = value

    def __getattr__(self, name):
        """Allow getting keys from self.store using dot notation"""
        if self._wrapped is empty:
            self._setup()
        if name in self._wrapped._deleted:  # noqa
            raise AttributeError(
                f"Attribute {name} was deleted, " "or belongs to different env"
            )

        if name not in RESERVED_ATTRS:
            lowercase_mode = self._kwargs.get(
                "LOWERCASE_READ_FOR_DYNACONF",
                default_settings.LOWERCASE_READ_FOR_DYNACONF,
            )
            if lowercase_mode is True:
                name = name.upper()

        if (
            name.isupper()
            and (
                self._wrapped._fresh
                or name in self._wrapped.FRESH_VARS_FOR_DYNACONF
            )
            and name not in UPPER_DEFAULT_SETTINGS
        ):
            return self._wrapped.get_fresh(name)
        value = getattr(self._wrapped, name)
        if name not in RESERVED_ATTRS:
            return recursively_evaluate_lazy_format(value, self)
        return value

    def __call__(self, *args, **kwargs):
        """Allow direct call of settings('val')
        in place of settings.get('val')
        """
        return self.get(*args, **kwargs)

    @property
    def _should_load_dotenv(self):
        """Chicken and egg problem, we must manually check envvar
        before deciding if we are loading envvars :)"""
        _environ_load_dotenv = parse_conf_data(
            boolean_fix(os.environ.get("LOAD_DOTENV_FOR_DYNACONF")),
            tomlfy=True,
        )
        return self._kwargs.get("load_dotenv", _environ_load_dotenv)

    def _setup(self):
        """Initial setup, run once."""

        if self._warn_dynaconf_global_settings:
            warnings.warn(
                "Usage of `from dynaconf import settings` is now "
                "DEPRECATED in 3.0.0+. You are encouraged to change it to "
                "your own instance e.g: `settings = Dynaconf(*options)`",
                DeprecationWarning,
            )
            self._wrapper_class = Settings  # Force unhooked for this

        default_settings.reload(self._should_load_dotenv)
        environment_variable = self._kwargs.get(
            "ENVVAR_FOR_DYNACONF", default_settings.ENVVAR_FOR_DYNACONF
        )
        settings_module = os.environ.get(environment_variable)
        self._wrapped = self._wrapper_class(
            settings_module=settings_module, **self._kwargs
        )

    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
        )

    @property
    def configured(self):
        """If wrapped is configured"""
        return self._wrapped is not empty

dynaconf.LazySettings.configured property

If wrapped is configured

dynaconf.LazySettings.__call__(*args, **kwargs)

Allow direct call of settings('val') in place of settings.get('val')

Source code in dynaconf/base.py
150
151
152
153
154
def __call__(self, *args, **kwargs):
    """Allow direct call of settings('val')
    in place of settings.get('val')
    """
    return self.get(*args, **kwargs)

dynaconf.LazySettings.__getattr__(name)

Allow getting keys from self.store using dot notation

Source code in dynaconf/base.py
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 __getattr__(self, name):
    """Allow getting keys from self.store using dot notation"""
    if self._wrapped is empty:
        self._setup()
    if name in self._wrapped._deleted:  # noqa
        raise AttributeError(
            f"Attribute {name} was deleted, " "or belongs to different env"
        )

    if name not in RESERVED_ATTRS:
        lowercase_mode = self._kwargs.get(
            "LOWERCASE_READ_FOR_DYNACONF",
            default_settings.LOWERCASE_READ_FOR_DYNACONF,
        )
        if lowercase_mode is True:
            name = name.upper()

    if (
        name.isupper()
        and (
            self._wrapped._fresh
            or name in self._wrapped.FRESH_VARS_FOR_DYNACONF
        )
        and name not in UPPER_DEFAULT_SETTINGS
    ):
        return self._wrapped.get_fresh(name)
    value = getattr(self._wrapped, name)
    if name not in RESERVED_ATTRS:
        return recursively_evaluate_lazy_format(value, self)
    return value

dynaconf.LazySettings.__init__(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

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.__resolve_config_aliases(kwargs)

takes aliases for _FOR_DYNACONF configurations

e.g: ROOT_PATH='/' is transformed into ROOT_PATH_FOR_DYNACONF

Source code in dynaconf/base.py
 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
def __resolve_config_aliases(self, kwargs):
    """takes aliases for _FOR_DYNACONF configurations

    e.g: ROOT_PATH='/' is transformed into `ROOT_PATH_FOR_DYNACONF`
    """

    misspells = {
        "settings_files": "settings_file",
        "SETTINGS_FILES": "SETTINGS_FILE",
        "environment": "environments",
        "ENVIRONMENT": "ENVIRONMENTS",
    }
    for misspell, correct in misspells.items():
        if misspell in kwargs:
            kwargs[correct] = kwargs.pop(misspell)

    for_dynaconf_keys = {
        key
        for key in UPPER_DEFAULT_SETTINGS
        if key.endswith("_FOR_DYNACONF")
    }
    aliases = {
        key.upper()
        for key in kwargs
        if f"{key.upper()}_FOR_DYNACONF" in for_dynaconf_keys
    }
    for alias in aliases:
        value = kwargs.pop(alias, empty)
        if value is empty:
            value = kwargs.pop(alias.lower())
        kwargs[f"{alias}_FOR_DYNACONF"] = value

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

Bases: Exception

Raised when a validation fails

Source code in dynaconf/validator.py
28
29
30
31
32
33
34
class ValidationError(Exception):
    """Raised when a validation fails"""

    def __init__(self, message: str, *args, **kwargs):
        self.details = kwargs.pop("details", [])
        super().__init__(message, *args, **kwargs)
        self.message = message

dynaconf.Validator

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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 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
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
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
class Validator:
    """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.

    """

    default_messages = MappingProxyType(
        {
            "must_exist_true": "{name} is required in env {env}",
            "must_exist_false": "{name} cannot exists in env {env}",
            "condition": "{name} invalid for {function}({value}) in env {env}",
            "operations": (
                "{name} must {operation} {op_value} "
                "but it is {value} in env {env}"
            ),
            "combined": "combined validators failed {errors}",
        }
    )

    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

    def __or__(self, other: Validator) -> CombinedValidator:
        return OrValidator(self, other, description=self.description)

    def __and__(self, other: Validator) -> CombinedValidator:
        return AndValidator(self, other, description=self.description)

    def __eq__(self, other: object) -> bool:
        if self is other:
            return True

        if type(self).__name__ != type(other).__name__:
            return False

        identical_attrs = (
            getattr(self, attr) == getattr(other, attr)
            for attr in EQUALITY_ATTRS
        )
        if all(identical_attrs):
            return True

        return False

    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)

    def _validate_items(
        self,
        settings: Settings,
        env: str | None = None,
        only: str | Sequence | None = None,
        exclude: str | Sequence | None = None,
    ) -> None:
        env = env or settings.current_env
        for name in self.names:
            # Skip if only is set and name isn't in the only list
            if only and not any(name.startswith(sub) for sub in only):
                continue

            # Skip if exclude is set and name is in the exclude list
            if exclude and any(name.startswith(sub) for sub in exclude):
                continue

            if self.default is not empty:
                default_value = (
                    self.default(settings, self)
                    if callable(self.default)
                    else self.default
                )
            else:
                default_value = empty

            # THIS IS A FIX FOR #585 in contrast with #799
            # toml considers signed strings "+-1" as integers
            # however existing users are passing strings
            # to default on validator (see #585)
            # The solution we added on #667 introduced a new problem
            # This fix here makes it to work for both cases.
            if (
                isinstance(default_value, str)
                and default_value.startswith(("+", "-"))
                and self.is_type_of is str
            ):
                # avoid TOML from parsing "+-1" as integer
                default_value = f"'{default_value}'"

            value = settings.setdefault(
                name,
                default_value,
                apply_default_on_none=self.apply_default_on_none,
                env=env,
            )

            # is name required but not exists?
            if self.must_exist is True and value is empty:
                _message = self.messages["must_exist_true"].format(
                    name=name, env=env
                )
                raise ValidationError(_message, details=[(self, _message)])

            if self.must_exist is False and value is not empty:
                _message = self.messages["must_exist_false"].format(
                    name=name, env=env
                )
                raise ValidationError(_message, details=[(self, _message)])

            if self.must_exist in (False, None) and value is empty:
                continue

            # value or default value already set
            # by settings.setdefault above
            # however we need to cast it
            # so we call .set again
            value = self.cast(settings.get(name))
            settings.set(name, value, validate=False)

            # is there a callable condition?
            if self.condition is not None:
                if not self.condition(value):
                    _message = self.messages["condition"].format(
                        name=name,
                        function=self.condition.__name__,
                        value=value,
                        env=env,
                    )
                    raise ValidationError(_message, details=[(self, _message)])

            # operations
            for op_name, op_value in self.operations.items():
                op_function = getattr(validator_conditions, op_name)
                op_succeeded = False

                # 'is_type_of' special error handling - related to #879
                if op_name == "is_type_of":
                    # auto transform quoted types
                    if isinstance(op_value, str):
                        op_value = __builtins__.get(  # type: ignore
                            op_value, op_value
                        )

                    # invalid type (not in __builtins__) may raise TypeError
                    try:
                        op_succeeded = op_function(value, op_value)
                    except TypeError:
                        raise ValidationError(
                            f"Invalid type '{op_value}' for condition "
                            "'is_type_of'. Should provide a valid type"
                        )
                else:
                    op_succeeded = op_function(value, op_value)

                if not op_succeeded:
                    _message = self.messages["operations"].format(
                        name=name,
                        operation=op_function.__name__,
                        op_value=op_value,
                        value=value,
                        env=env,
                    )
                    raise ValidationError(_message, details=[(self, _message)])

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

Raise ValidationError if invalid

Source code in dynaconf/validator.py
171
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
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
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
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
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
 75
 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
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