dynaconf 3.2.7__tar.gz → 3.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dynaconf-3.2.7 → dynaconf-3.2.8}/CHANGELOG.md +14 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/PKG-INFO +43 -10
- dynaconf-3.2.8/dynaconf/VERSION +1 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/__init__.py +2 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/base.py +96 -62
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/cli.py +50 -15
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/default_settings.py +1 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/hooking.py +32 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/__init__.py +36 -9
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/py_loader.py +9 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/vault_loader.py +4 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/utils/inspect.py +141 -4
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/utils/parse_conf.py +6 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/_termui_impl.py +1 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/_unicodefun.py +1 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/core.py +2 -2
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/exceptions.py +2 -2
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/formatting.py +3 -3
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/parser.py +1 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/termui.py +1 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/testing.py +1 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/types.py +5 -5
- dynaconf-3.2.8/dynaconf/vendor/dotenv/py.typed +1 -0
- dynaconf-3.2.8/dynaconf/vendor/ruamel/yaml/py.typed +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/PKG-INFO +43 -10
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/SOURCES.txt +27 -0
- dynaconf-3.2.8/tests/test_base.py +1633 -0
- dynaconf-3.2.8/tests/test_basic.py +7 -0
- dynaconf-3.2.8/tests/test_cli.py +892 -0
- dynaconf-3.2.8/tests/test_compat.py +56 -0
- dynaconf-3.2.8/tests/test_django.py +44 -0
- dynaconf-3.2.8/tests/test_dynabox.py +103 -0
- dynaconf-3.2.8/tests/test_endtoend.py +67 -0
- dynaconf-3.2.8/tests/test_env_loader.py +523 -0
- dynaconf-3.2.8/tests/test_envvar_prefix.py +46 -0
- dynaconf-3.2.8/tests/test_feature_flag.py +34 -0
- dynaconf-3.2.8/tests/test_flask.py +165 -0
- dynaconf-3.2.8/tests/test_hooking.py +268 -0
- dynaconf-3.2.8/tests/test_ini_loader.py +208 -0
- dynaconf-3.2.8/tests/test_inspect.py +1009 -0
- dynaconf-3.2.8/tests/test_json_loader.py +230 -0
- dynaconf-3.2.8/tests/test_nested_loading.py +385 -0
- dynaconf-3.2.8/tests/test_py_loader.py +275 -0
- dynaconf-3.2.8/tests/test_redis.py +124 -0
- dynaconf-3.2.8/tests/test_settings_loader.py +167 -0
- dynaconf-3.2.8/tests/test_toml_loader.py +268 -0
- dynaconf-3.2.8/tests/test_utils.py +556 -0
- dynaconf-3.2.8/tests/test_validators.py +887 -0
- dynaconf-3.2.8/tests/test_validators_conditions.py +64 -0
- dynaconf-3.2.8/tests/test_vault.py +131 -0
- dynaconf-3.2.8/tests/test_yaml_loader.py +580 -0
- dynaconf-3.2.7/dynaconf/VERSION +0 -1
- {dynaconf-3.2.7 → dynaconf-3.2.8}/3.x-release-notes.md +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/CONTRIBUTING.md +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/CONTRIBUTORS.md +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/LICENSE +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/MANIFEST.in +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/README.md +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/constants.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/contrib/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/contrib/django_dynaconf_v2.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/contrib/flask_dynaconf.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/base.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/env_loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/ini_loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/json_loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/redis_loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/toml_loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/loaders/yaml_loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/strategies/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/strategies/filtering.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/test_settings.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/utils/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/utils/boxing.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/utils/files.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/utils/functional.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/validator.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/validator_conditions.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/box.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/box_list.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/config_box.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/converters.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/exceptions.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/from_file.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/box/shorthand_box.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/_bashcomplete.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/_compat.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/_textwrap.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/_winconsole.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/decorators.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/globals.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/click/utils.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/cli.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/compat.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/ipython.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/main.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/parser.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/version.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/anchor.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/comments.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/compat.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/composer.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/configobjwalker.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/constructor.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/cyaml.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/dumper.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/emitter.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/error.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/events.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/loader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/main.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/nodes.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/parser.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/reader.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/representer.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/resolver.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarbool.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarfloat.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarint.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarstring.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scanner.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/serializer.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/setup.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/timestamp.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/tokens.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/util.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/toml/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/toml/decoder.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/toml/encoder.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/toml/ordered.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/toml/tz.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/__init__.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_parser.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_re.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_types.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_writer.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/dependency_links.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/entry_points.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/not-zip-safe +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/requires.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/dynaconf.egg-info/top_level.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/setup.cfg +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/setup.py +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/box-LICENSE.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/click-LICENSE.rst +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/licenses.sh +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/python-dotenv-LICENSE.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/ruamel.yaml-LICENSE.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/toml-LICENSE.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/tomli-LICENSE.txt +0 -0
- {dynaconf-3.2.7 → dynaconf-3.2.8}/vendor_licenses/vendor_versions.txt +0 -0
|
@@ -2,6 +2,20 @@ Changelog
|
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
4
|
<!-- insertion marker -->
|
|
5
|
+
## [3.2.8](https://github.com/dynaconf/dynaconf/releases/tag/3.2.8) - 2025-02-16
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Parse data type on merge with comma separated value. *By Bruno Rocha*.
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- Add CLI command `debug-info` (#1251). *By Bruno Rocha*.
|
|
14
|
+
- Add support for decorated hooks on settings files (#1246). *By Bruno Rocha*.
|
|
15
|
+
- Add VAULT_TOKEN_RENEW_FOR_DYNACONF config/code (#1094) (#1242). *By Pedro Brochado*.
|
|
16
|
+
- populate_obj takes convert_to_dict (#1237). *By Bruno Rocha*.
|
|
17
|
+
- add VAULT_TOKEN_RENEW. *By Bruno Rocha*.
|
|
18
|
+
|
|
5
19
|
## [3.2.7](https://github.com/dynaconf/dynaconf/releases/tag/3.2.7) - 2025-01-21
|
|
6
20
|
|
|
7
21
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: dynaconf
|
|
3
|
-
Version: 3.2.
|
|
3
|
+
Version: 3.2.8
|
|
4
4
|
Summary: The dynamic configurator for your Python Project
|
|
5
5
|
Home-page: https://github.com/dynaconf/dynaconf
|
|
6
6
|
Author: Bruno Rocha
|
|
@@ -26,14 +26,6 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Requires-Python: >=3.8
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
|
-
Provides-Extra: redis
|
|
30
|
-
Provides-Extra: vault
|
|
31
|
-
Provides-Extra: yaml
|
|
32
|
-
Provides-Extra: toml
|
|
33
|
-
Provides-Extra: ini
|
|
34
|
-
Provides-Extra: configobj
|
|
35
|
-
Provides-Extra: all
|
|
36
|
-
Provides-Extra: test
|
|
37
29
|
License-File: LICENSE
|
|
38
30
|
License-File: vendor_licenses/box-LICENSE.txt
|
|
39
31
|
License-File: vendor_licenses/click-LICENSE.rst
|
|
@@ -43,6 +35,47 @@ License-File: vendor_licenses/ruamel.yaml-LICENSE.txt
|
|
|
43
35
|
License-File: vendor_licenses/toml-LICENSE.txt
|
|
44
36
|
License-File: vendor_licenses/tomli-LICENSE.txt
|
|
45
37
|
License-File: vendor_licenses/vendor_versions.txt
|
|
38
|
+
Provides-Extra: redis
|
|
39
|
+
Requires-Dist: redis; extra == "redis"
|
|
40
|
+
Provides-Extra: vault
|
|
41
|
+
Requires-Dist: hvac; extra == "vault"
|
|
42
|
+
Provides-Extra: yaml
|
|
43
|
+
Requires-Dist: ruamel.yaml; extra == "yaml"
|
|
44
|
+
Provides-Extra: toml
|
|
45
|
+
Requires-Dist: toml; extra == "toml"
|
|
46
|
+
Provides-Extra: ini
|
|
47
|
+
Requires-Dist: configobj; extra == "ini"
|
|
48
|
+
Provides-Extra: configobj
|
|
49
|
+
Requires-Dist: configobj; extra == "configobj"
|
|
50
|
+
Provides-Extra: all
|
|
51
|
+
Requires-Dist: redis; extra == "all"
|
|
52
|
+
Requires-Dist: ruamel.yaml; extra == "all"
|
|
53
|
+
Requires-Dist: configobj; extra == "all"
|
|
54
|
+
Requires-Dist: hvac; extra == "all"
|
|
55
|
+
Provides-Extra: test
|
|
56
|
+
Requires-Dist: pytest; extra == "test"
|
|
57
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
58
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
|
59
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
60
|
+
Requires-Dist: radon; extra == "test"
|
|
61
|
+
Requires-Dist: flask>=0.12; extra == "test"
|
|
62
|
+
Requires-Dist: django; extra == "test"
|
|
63
|
+
Requires-Dist: python-dotenv; extra == "test"
|
|
64
|
+
Requires-Dist: toml; extra == "test"
|
|
65
|
+
Requires-Dist: redis; extra == "test"
|
|
66
|
+
Requires-Dist: hvac>=1.1.0; extra == "test"
|
|
67
|
+
Requires-Dist: configobj; extra == "test"
|
|
68
|
+
Dynamic: author
|
|
69
|
+
Dynamic: author-email
|
|
70
|
+
Dynamic: classifier
|
|
71
|
+
Dynamic: description
|
|
72
|
+
Dynamic: description-content-type
|
|
73
|
+
Dynamic: home-page
|
|
74
|
+
Dynamic: license
|
|
75
|
+
Dynamic: platform
|
|
76
|
+
Dynamic: provides-extra
|
|
77
|
+
Dynamic: requires-python
|
|
78
|
+
Dynamic: summary
|
|
46
79
|
|
|
47
80
|
<!-- [](http://dynaconf.com) -->
|
|
48
81
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.8
|
|
@@ -4,6 +4,7 @@ from dynaconf.base import LazySettings
|
|
|
4
4
|
from dynaconf.constants import DEFAULT_SETTINGS_FILES
|
|
5
5
|
from dynaconf.contrib import DjangoDynaconf
|
|
6
6
|
from dynaconf.contrib import FlaskDynaconf
|
|
7
|
+
from dynaconf.hooking import post_hook
|
|
7
8
|
from dynaconf.utils.inspect import get_history
|
|
8
9
|
from dynaconf.utils.inspect import inspect_settings
|
|
9
10
|
from dynaconf.utils.parse_conf import add_converter
|
|
@@ -39,4 +40,5 @@ __all__ = [
|
|
|
39
40
|
"get_history",
|
|
40
41
|
"DynaconfFormatError",
|
|
41
42
|
"DynaconfParseError",
|
|
43
|
+
"post_hook",
|
|
42
44
|
]
|
|
@@ -1161,6 +1161,10 @@ class Settings:
|
|
|
1161
1161
|
"""Clean end Execute all loaders"""
|
|
1162
1162
|
self.clean()
|
|
1163
1163
|
self._loaded_hooks.clear()
|
|
1164
|
+
for hook in self._post_hooks:
|
|
1165
|
+
with suppress(AttributeError, TypeError):
|
|
1166
|
+
hook._called = False
|
|
1167
|
+
|
|
1164
1168
|
self.execute_loaders(env, silent)
|
|
1165
1169
|
|
|
1166
1170
|
def execute_loaders(
|
|
@@ -1217,7 +1221,13 @@ class Settings:
|
|
|
1217
1221
|
last_loader.load(self, env, silent, key)
|
|
1218
1222
|
|
|
1219
1223
|
def load_file(
|
|
1220
|
-
self,
|
|
1224
|
+
self,
|
|
1225
|
+
path=None,
|
|
1226
|
+
env=None,
|
|
1227
|
+
silent=True,
|
|
1228
|
+
key=None,
|
|
1229
|
+
validate=empty,
|
|
1230
|
+
run_hooks=True,
|
|
1221
1231
|
):
|
|
1222
1232
|
"""Programmatically load files from ``path``.
|
|
1223
1233
|
|
|
@@ -1226,82 +1236,95 @@ class Settings:
|
|
|
1226
1236
|
- Directory of the last loaded file
|
|
1227
1237
|
- CWD
|
|
1228
1238
|
|
|
1229
|
-
:param path: A single filename or a file list
|
|
1239
|
+
:param path: A single filename, a glob or a file list
|
|
1230
1240
|
:param env: Which env to load from file (default current_env)
|
|
1231
1241
|
:param silent: Should raise errors?
|
|
1232
1242
|
:param key: Load a single key?
|
|
1233
1243
|
:param validate: Should trigger validation?
|
|
1244
|
+
:param run_hooks: Should run collected hooks?
|
|
1234
1245
|
"""
|
|
1246
|
+
files = ensure_a_list(path)
|
|
1247
|
+
if not files: # a glob pattern may return empty
|
|
1248
|
+
return
|
|
1249
|
+
|
|
1235
1250
|
if validate is empty:
|
|
1236
1251
|
validate = self.get("VALIDATE_ON_UPDATE_FOR_DYNACONF")
|
|
1237
1252
|
|
|
1238
1253
|
env = (env or self.current_env).upper()
|
|
1239
1254
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1255
|
+
# Using inspect take the filename and line number of the caller
|
|
1256
|
+
# to be used in the source_metadata
|
|
1257
|
+
frame = inspect.currentframe()
|
|
1258
|
+
caller = inspect.getouterframes(frame)[1]
|
|
1259
|
+
|
|
1260
|
+
already_loaded = set()
|
|
1261
|
+
for _filename in files:
|
|
1262
|
+
# load_file() will handle validation later
|
|
1263
|
+
with suppress(ValidationError):
|
|
1264
|
+
source_metadata = SourceMetadata(
|
|
1265
|
+
loader=f"load_file@{caller.filename}:{caller.lineno}",
|
|
1266
|
+
identifier=_filename,
|
|
1267
|
+
env=env,
|
|
1268
|
+
)
|
|
1269
|
+
if py_loader.try_to_load_from_py_module_name(
|
|
1270
|
+
obj=self,
|
|
1271
|
+
name=_filename,
|
|
1272
|
+
silent=True,
|
|
1273
|
+
identifier=source_metadata,
|
|
1274
|
+
):
|
|
1275
|
+
# if it was possible to load from module name
|
|
1276
|
+
# continue the loop.
|
|
1277
|
+
continue
|
|
1278
|
+
|
|
1279
|
+
root_dir = str(self._root_path or os.getcwd())
|
|
1280
|
+
|
|
1281
|
+
# Issue #494
|
|
1282
|
+
if (
|
|
1283
|
+
isinstance(_filename, Path)
|
|
1284
|
+
and str(_filename.parent) in root_dir
|
|
1285
|
+
): # pragma: no cover
|
|
1286
|
+
filepath = str(_filename)
|
|
1287
|
+
else:
|
|
1288
|
+
filepath = os.path.join(root_dir, str(_filename))
|
|
1289
|
+
|
|
1290
|
+
paths = [p for p in sorted(glob(filepath)) if ".local." not in p]
|
|
1291
|
+
local_paths = [p for p in sorted(glob(filepath)) if ".local." in p]
|
|
1292
|
+
|
|
1293
|
+
# Handle possible *.globs sorted alphanumeric
|
|
1294
|
+
for path in paths + local_paths:
|
|
1295
|
+
if path in already_loaded: # pragma: no cover
|
|
1296
|
+
continue
|
|
1297
|
+
|
|
1248
1298
|
# load_file() will handle validation later
|
|
1249
1299
|
with suppress(ValidationError):
|
|
1250
1300
|
source_metadata = SourceMetadata(
|
|
1251
1301
|
loader=f"load_file@{caller.filename}:{caller.lineno}",
|
|
1252
|
-
identifier=
|
|
1302
|
+
identifier=path,
|
|
1253
1303
|
env=env,
|
|
1254
1304
|
)
|
|
1255
|
-
|
|
1305
|
+
settings_loader(
|
|
1256
1306
|
obj=self,
|
|
1257
|
-
|
|
1258
|
-
silent=
|
|
1307
|
+
env=env,
|
|
1308
|
+
silent=silent,
|
|
1309
|
+
key=key,
|
|
1310
|
+
filename=path,
|
|
1311
|
+
validate=validate,
|
|
1259
1312
|
identifier=source_metadata,
|
|
1260
|
-
)
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
paths = [
|
|
1277
|
-
p for p in sorted(glob(filepath)) if ".local." not in p
|
|
1278
|
-
]
|
|
1279
|
-
local_paths = [
|
|
1280
|
-
p for p in sorted(glob(filepath)) if ".local." in p
|
|
1281
|
-
]
|
|
1282
|
-
|
|
1283
|
-
# Handle possible *.globs sorted alphanumeric
|
|
1284
|
-
for path in paths + local_paths:
|
|
1285
|
-
if path in already_loaded: # pragma: no cover
|
|
1286
|
-
continue
|
|
1287
|
-
|
|
1288
|
-
# load_file() will handle validation later
|
|
1289
|
-
with suppress(ValidationError):
|
|
1290
|
-
source_metadata = SourceMetadata(
|
|
1291
|
-
loader=f"load_file@{caller.filename}:{caller.lineno}",
|
|
1292
|
-
identifier=path,
|
|
1293
|
-
env=env,
|
|
1294
|
-
)
|
|
1295
|
-
settings_loader(
|
|
1296
|
-
obj=self,
|
|
1297
|
-
env=env,
|
|
1298
|
-
silent=silent,
|
|
1299
|
-
key=key,
|
|
1300
|
-
filename=path,
|
|
1301
|
-
validate=validate,
|
|
1302
|
-
identifier=source_metadata,
|
|
1303
|
-
)
|
|
1304
|
-
already_loaded.add(path)
|
|
1313
|
+
)
|
|
1314
|
+
already_loaded.add(path)
|
|
1315
|
+
|
|
1316
|
+
if run_hooks:
|
|
1317
|
+
# this will call any collected hook that was not called yet
|
|
1318
|
+
execute_instance_hooks(
|
|
1319
|
+
self,
|
|
1320
|
+
"post",
|
|
1321
|
+
[
|
|
1322
|
+
_hook
|
|
1323
|
+
for _hook in self._post_hooks
|
|
1324
|
+
if getattr(_hook, "_dynaconf_hook", False) is True
|
|
1325
|
+
and not getattr(_hook, "_called", False)
|
|
1326
|
+
],
|
|
1327
|
+
)
|
|
1305
1328
|
|
|
1306
1329
|
# handle param `validate`
|
|
1307
1330
|
if validate is True:
|
|
@@ -1385,22 +1408,33 @@ class Settings:
|
|
|
1385
1408
|
value = self.get_fresh(key)
|
|
1386
1409
|
return value is True or value in true_values
|
|
1387
1410
|
|
|
1388
|
-
def populate_obj(
|
|
1411
|
+
def populate_obj(
|
|
1412
|
+
self,
|
|
1413
|
+
obj,
|
|
1414
|
+
keys=None,
|
|
1415
|
+
ignore=None,
|
|
1416
|
+
internal=False,
|
|
1417
|
+
convert_to_dict=False,
|
|
1418
|
+
):
|
|
1389
1419
|
"""Given the `obj` populate it using self.store items.
|
|
1390
1420
|
|
|
1391
1421
|
:param obj: An object to be populated, a class instance.
|
|
1392
1422
|
:param keys: A list of keys to be included.
|
|
1393
1423
|
:param ignore: A list of keys to be excluded.
|
|
1424
|
+
:param internal: Include internal keys.
|
|
1425
|
+
:param convert_to_dict: Convert the settings to a pure dict (no Box)
|
|
1426
|
+
before populating.
|
|
1394
1427
|
"""
|
|
1428
|
+
data = self.to_dict(internal=internal) if convert_to_dict else self
|
|
1395
1429
|
keys = keys or self.keys()
|
|
1396
1430
|
for key in keys:
|
|
1431
|
+
key = upperfy(key)
|
|
1397
1432
|
if not internal:
|
|
1398
1433
|
if key in UPPER_DEFAULT_SETTINGS:
|
|
1399
1434
|
continue
|
|
1400
|
-
key = upperfy(key)
|
|
1401
1435
|
if ignore and key in ignore:
|
|
1402
1436
|
continue
|
|
1403
|
-
value =
|
|
1437
|
+
value = data.get(key, empty)
|
|
1404
1438
|
if value is not empty:
|
|
1405
1439
|
setattr(obj, key, value)
|
|
1406
1440
|
|
|
@@ -28,6 +28,7 @@ from dynaconf.utils.inspect import EnvNotFoundError
|
|
|
28
28
|
from dynaconf.utils.inspect import inspect_settings
|
|
29
29
|
from dynaconf.utils.inspect import KeyNotFoundError
|
|
30
30
|
from dynaconf.utils.inspect import OutputFormatError
|
|
31
|
+
from dynaconf.utils.inspect import print_debug_info
|
|
31
32
|
from dynaconf.utils.parse_conf import parse_conf_data
|
|
32
33
|
from dynaconf.utils.parse_conf import unparse_conf_data
|
|
33
34
|
from dynaconf.validator import ValidationError
|
|
@@ -54,7 +55,12 @@ def set_settings(ctx, instance=None):
|
|
|
54
55
|
|
|
55
56
|
settings = None
|
|
56
57
|
|
|
57
|
-
_echo_enabled = ctx.invoked_subcommand not in [
|
|
58
|
+
_echo_enabled = ctx.invoked_subcommand not in [
|
|
59
|
+
"get",
|
|
60
|
+
"inspect",
|
|
61
|
+
"debug-info",
|
|
62
|
+
None,
|
|
63
|
+
]
|
|
58
64
|
if "--json" in click.get_os_args():
|
|
59
65
|
_echo_enabled = False
|
|
60
66
|
|
|
@@ -857,7 +863,10 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
|
|
|
857
863
|
@main.command()
|
|
858
864
|
@click.option("--key", "-k", help="Filters result by key.")
|
|
859
865
|
@click.option(
|
|
860
|
-
"--env",
|
|
866
|
+
"--env",
|
|
867
|
+
"-e",
|
|
868
|
+
help="Filters result by environment on --report-mode=inspect.",
|
|
869
|
+
default=None,
|
|
861
870
|
)
|
|
862
871
|
@click.option(
|
|
863
872
|
"--format",
|
|
@@ -870,7 +879,7 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
|
|
|
870
879
|
"--old-first",
|
|
871
880
|
"new_first",
|
|
872
881
|
"-s",
|
|
873
|
-
help="Invert history sorting to 'old-first'",
|
|
882
|
+
help="Invert history sorting to 'old-first' on --report-mode=inspect.",
|
|
874
883
|
default=True,
|
|
875
884
|
is_flag=True,
|
|
876
885
|
)
|
|
@@ -880,7 +889,7 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
|
|
|
880
889
|
"-n",
|
|
881
890
|
default=None,
|
|
882
891
|
type=int,
|
|
883
|
-
help="Limits how many history entries are shown.",
|
|
892
|
+
help="Limits how many history entries are shown on --report-mode=inspect.",
|
|
884
893
|
)
|
|
885
894
|
@click.option(
|
|
886
895
|
"--all",
|
|
@@ -890,28 +899,54 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
|
|
|
890
899
|
is_flag=True,
|
|
891
900
|
help="Show dynaconf internal settings?",
|
|
892
901
|
)
|
|
902
|
+
@click.option(
|
|
903
|
+
"--report-mode",
|
|
904
|
+
"report_mode",
|
|
905
|
+
"-m",
|
|
906
|
+
default="inspect",
|
|
907
|
+
type=click.Choice(["inspect", "debug"]),
|
|
908
|
+
)
|
|
909
|
+
@click.option(
|
|
910
|
+
"-v",
|
|
911
|
+
"--verbose",
|
|
912
|
+
count=True,
|
|
913
|
+
help="Increase verbosity of the output on --report-mode=debug.",
|
|
914
|
+
)
|
|
893
915
|
def inspect(
|
|
894
|
-
key, env, format, new_first, history_limit, _all
|
|
916
|
+
key, env, format, new_first, history_limit, _all, report_mode, verbose
|
|
895
917
|
): # pragma: no cover
|
|
896
918
|
"""
|
|
897
919
|
Inspect the loading history of the given settings instance.
|
|
898
920
|
|
|
899
921
|
Filters by key and environment, otherwise shows all.
|
|
900
922
|
"""
|
|
901
|
-
|
|
902
|
-
|
|
923
|
+
|
|
924
|
+
if report_mode == "debug":
|
|
925
|
+
print_debug_info(
|
|
903
926
|
settings,
|
|
904
|
-
key=key,
|
|
905
|
-
env=env or None,
|
|
906
927
|
dumper=format,
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
history_limit=history_limit,
|
|
910
|
-
print_report=True,
|
|
928
|
+
verbosity=verbose,
|
|
929
|
+
key=key,
|
|
911
930
|
)
|
|
912
931
|
click.echo()
|
|
913
|
-
|
|
914
|
-
|
|
932
|
+
elif report_mode == "inspect":
|
|
933
|
+
try:
|
|
934
|
+
inspect_settings(
|
|
935
|
+
settings,
|
|
936
|
+
key=key,
|
|
937
|
+
env=env or None,
|
|
938
|
+
dumper=format,
|
|
939
|
+
new_first=new_first,
|
|
940
|
+
include_internal=_all,
|
|
941
|
+
history_limit=history_limit,
|
|
942
|
+
print_report=True,
|
|
943
|
+
)
|
|
944
|
+
click.echo()
|
|
945
|
+
except (KeyNotFoundError, EnvNotFoundError, OutputFormatError) as err:
|
|
946
|
+
click.echo(err)
|
|
947
|
+
sys.exit(1)
|
|
948
|
+
else:
|
|
949
|
+
click.echo("Invalid report mode")
|
|
915
950
|
sys.exit(1)
|
|
916
951
|
|
|
917
952
|
|
|
@@ -191,6 +191,7 @@ VAULT_ROLE_ID_FOR_DYNACONF = get("VAULT_ROLE_ID_FOR_DYNACONF", None)
|
|
|
191
191
|
VAULT_SECRET_ID_FOR_DYNACONF = get("VAULT_SECRET_ID_FOR_DYNACONF", None)
|
|
192
192
|
VAULT_USERNAME_FOR_DYNACONF = get("VAULT_USERNAME_FOR_DYNACONF", None)
|
|
193
193
|
VAULT_PASSWORD_FOR_DYNACONF = get("VAULT_PASSWORD_FOR_DYNACONF", None)
|
|
194
|
+
VAULT_TOKEN_RENEW_FOR_DYNACONF = get("VAULT_TOKEN_RENEW_FOR_DYNACONF", False)
|
|
194
195
|
|
|
195
196
|
# Only core loaders defined on this list will be invoked
|
|
196
197
|
core_loaders = ["YAML", "TOML", "INI", "JSON", "PY"]
|
|
@@ -18,6 +18,7 @@ __all__ = [
|
|
|
18
18
|
"MethodValue",
|
|
19
19
|
"Action",
|
|
20
20
|
"HookableSettings",
|
|
21
|
+
"post_hook",
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
|
|
@@ -341,3 +342,34 @@ class TempSettingsHolder:
|
|
|
341
342
|
else:
|
|
342
343
|
self._initialize()
|
|
343
344
|
setattr(self._settings, attr, value)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def post_hook(function: Callable) -> Callable:
|
|
348
|
+
"""This decorator marks a function as a post hook.
|
|
349
|
+
This works by adding the _dynaconf_hook attribute to the function,
|
|
350
|
+
then the python loader, when reading the module, will look for
|
|
351
|
+
this attribute and register the function as a post_hook for the settings.
|
|
352
|
+
|
|
353
|
+
e.g: On a settings file with .py extension:
|
|
354
|
+
|
|
355
|
+
from dynaconf import post_hook
|
|
356
|
+
@post_hook
|
|
357
|
+
def set_log_handlers(settings) -> dict:
|
|
358
|
+
data = {} # data to be merged into settings
|
|
359
|
+
|
|
360
|
+
# conditionals
|
|
361
|
+
if (logging := settings.get('LOGGING')) is not None:
|
|
362
|
+
# do something with logging
|
|
363
|
+
# add it back to data
|
|
364
|
+
data['LOGGING'] = logging
|
|
365
|
+
return data
|
|
366
|
+
"""
|
|
367
|
+
try:
|
|
368
|
+
function._dynaconf_hook = True # type: ignore
|
|
369
|
+
function._called = False # type: ignore
|
|
370
|
+
function._dynaconf_hook_source = function.__module__ # type: ignore
|
|
371
|
+
except (AttributeError, TypeError):
|
|
372
|
+
raise TypeError(
|
|
373
|
+
"post_hook decorator must be applied to a function or method."
|
|
374
|
+
)
|
|
375
|
+
return function
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import importlib
|
|
4
4
|
import os
|
|
5
|
+
from contextlib import suppress
|
|
5
6
|
from typing import Callable
|
|
6
7
|
from typing import TYPE_CHECKING
|
|
7
8
|
|
|
@@ -146,6 +147,22 @@ def execute_module_hooks(
|
|
|
146
147
|
execute_hooks = execute_module_hooks
|
|
147
148
|
|
|
148
149
|
|
|
150
|
+
def _get_unique_hook_id(hook_func, hook_source):
|
|
151
|
+
"""get unique identifier for a hook function.
|
|
152
|
+
in most of cases this will be the function name@source_file
|
|
153
|
+
however, if the function is a lambda, it will be a hash of the code object.
|
|
154
|
+
because lambda functions are not hashable itself and we can't rely on its id.
|
|
155
|
+
"""
|
|
156
|
+
hook_unique_id = hook_func.__name__
|
|
157
|
+
if hook_unique_id == "<lambda>":
|
|
158
|
+
frame_info = getattr(hook_func, "__code__", None)
|
|
159
|
+
if frame_info:
|
|
160
|
+
hook_unique_id = f"lambda_{hash(frame_info.co_code)}"
|
|
161
|
+
else:
|
|
162
|
+
hook_unique_id = f"lambda_{id(hook_func)}"
|
|
163
|
+
return f"{hook_unique_id}@{hook_source}"
|
|
164
|
+
|
|
165
|
+
|
|
149
166
|
def _run_hook_module(hook_type, hook_module, obj, key=""):
|
|
150
167
|
"""
|
|
151
168
|
Run a hook function from hook_module.
|
|
@@ -153,11 +170,6 @@ def _run_hook_module(hook_type, hook_module, obj, key=""):
|
|
|
153
170
|
Given a @hook_type, a @hook_module and a settings @obj, load the function
|
|
154
171
|
and execute it if found.
|
|
155
172
|
"""
|
|
156
|
-
hook_source = hook_module.__file__
|
|
157
|
-
|
|
158
|
-
# check if already loaded
|
|
159
|
-
if hook_type in obj._loaded_hooks.get(hook_source, {}):
|
|
160
|
-
return
|
|
161
173
|
|
|
162
174
|
# check errors
|
|
163
175
|
if hook_module and getattr(hook_module, "_error", False):
|
|
@@ -165,9 +177,12 @@ def _run_hook_module(hook_type, hook_module, obj, key=""):
|
|
|
165
177
|
raise hook_module._error
|
|
166
178
|
|
|
167
179
|
# execute hook
|
|
180
|
+
hook_source = hook_module.__file__
|
|
168
181
|
hook_func = getattr(hook_module, hook_type, None)
|
|
169
182
|
if hook_func:
|
|
170
|
-
|
|
183
|
+
identifier = _get_unique_hook_id(hook_func, hook_source)
|
|
184
|
+
if hook_type not in obj._loaded_hooks.get(identifier, {}):
|
|
185
|
+
_run_hook_function(obj, hook_type, hook_func, hook_source, key)
|
|
171
186
|
|
|
172
187
|
|
|
173
188
|
def _run_hook_function(
|
|
@@ -183,15 +198,27 @@ def _run_hook_function(
|
|
|
183
198
|
It execute @hook_func, update the results into settings @obj and
|
|
184
199
|
add it to _loaded_hook registry ([@hook_source][@hook_type])
|
|
185
200
|
"""
|
|
201
|
+
# if the function has a _dynaconf_hook_source attribute set
|
|
202
|
+
# hook_source to it
|
|
203
|
+
hook_source = getattr(hook_func, "_dynaconf_hook_source", hook_source)
|
|
204
|
+
|
|
186
205
|
# optional settings argument
|
|
187
206
|
try:
|
|
188
207
|
hook_dict = hook_func(obj.dynaconf.clone())
|
|
189
208
|
except TypeError:
|
|
190
209
|
hook_dict = hook_func()
|
|
191
210
|
|
|
192
|
-
#
|
|
211
|
+
# mark as called so executors such as `load_file` can avoid calling it again
|
|
212
|
+
with suppress(AttributeError, TypeError):
|
|
213
|
+
# callable may not be writable, the caveat is that it will be called again in case of reload
|
|
214
|
+
# however, this must not be a problem since the function should be idempotent
|
|
215
|
+
# and documentation warns about this behavior.
|
|
216
|
+
hook_func._called = True
|
|
217
|
+
|
|
218
|
+
identifier = _get_unique_hook_id(hook_func, hook_source)
|
|
219
|
+
|
|
193
220
|
if hook_dict:
|
|
194
|
-
|
|
221
|
+
# update obj settings
|
|
195
222
|
merge = hook_dict.pop(
|
|
196
223
|
"dynaconf_merge", hook_dict.pop("DYNACONF_MERGE", False)
|
|
197
224
|
)
|
|
@@ -212,7 +239,7 @@ def _run_hook_function(
|
|
|
212
239
|
)
|
|
213
240
|
|
|
214
241
|
# add to registry
|
|
215
|
-
obj._loaded_hooks[
|
|
242
|
+
obj._loaded_hooks[identifier][hook_type] = hook_dict
|
|
216
243
|
|
|
217
244
|
|
|
218
245
|
def settings_loader(
|
|
@@ -57,13 +57,13 @@ def load_from_python_object(
|
|
|
57
57
|
file_merge = getattr(mod, "DYNACONF_MERGE", empty)
|
|
58
58
|
|
|
59
59
|
for setting in dir(mod):
|
|
60
|
+
setting_value = getattr(mod, setting)
|
|
60
61
|
# A setting var in a Python file should start with upper case
|
|
61
62
|
# valid: A_value=1, ABC_value=3 A_BBB__default=1
|
|
62
63
|
# invalid: a_value=1, MyValue=3
|
|
63
64
|
# This is to avoid loading functions, classes and built-ins
|
|
64
65
|
if setting.split("__")[0].isupper():
|
|
65
66
|
if key is None or key == setting:
|
|
66
|
-
setting_value = getattr(mod, setting)
|
|
67
67
|
obj.set(
|
|
68
68
|
setting,
|
|
69
69
|
setting_value,
|
|
@@ -71,6 +71,14 @@ def load_from_python_object(
|
|
|
71
71
|
merge=file_merge,
|
|
72
72
|
validate=validate,
|
|
73
73
|
)
|
|
74
|
+
# if setting is a post_hook function it will be a callable with
|
|
75
|
+
# the _dynaconf_hook attribute set to True
|
|
76
|
+
# then we want to add it to the post_hooks list on the obj.
|
|
77
|
+
elif callable(setting_value) and getattr(
|
|
78
|
+
setting_value, "_dynaconf_hook", False
|
|
79
|
+
):
|
|
80
|
+
if setting_value not in obj._post_hooks:
|
|
81
|
+
obj._post_hooks.append(setting_value)
|
|
74
82
|
|
|
75
83
|
obj._loaded_py_modules.append(mod.__name__)
|
|
76
84
|
obj._loaded_files.append(mod.__file__)
|
|
@@ -62,6 +62,10 @@ def get_client(obj):
|
|
|
62
62
|
credentials.token,
|
|
63
63
|
role=obj.VAULT_AUTH_ROLE_FOR_DYNACONF,
|
|
64
64
|
)
|
|
65
|
+
|
|
66
|
+
if obj.VAULT_TOKEN_RENEW_FOR_DYNACONF:
|
|
67
|
+
client.auth.token.renew_self()
|
|
68
|
+
|
|
65
69
|
assert client.is_authenticated(), (
|
|
66
70
|
"Vault authentication error: is VAULT_TOKEN_FOR_DYNACONF or "
|
|
67
71
|
"VAULT_ROLE_ID_FOR_DYNACONF defined?"
|