dynaconf 3.2.6__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.
Files changed (157) hide show
  1. {dynaconf-3.2.6 → dynaconf-3.2.8}/CHANGELOG.md +35 -0
  2. {dynaconf-3.2.6 → dynaconf-3.2.8}/PKG-INFO +43 -11
  3. dynaconf-3.2.8/dynaconf/VERSION +1 -0
  4. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/__init__.py +2 -0
  5. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/base.py +114 -49
  6. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/cli.py +129 -50
  7. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/default_settings.py +1 -0
  8. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/hooking.py +32 -0
  9. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/__init__.py +105 -42
  10. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/env_loader.py +29 -13
  11. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/ini_loader.py +17 -2
  12. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/json_loader.py +17 -2
  13. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/py_loader.py +19 -3
  14. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/redis_loader.py +9 -2
  15. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/toml_loader.py +17 -3
  16. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/vault_loader.py +4 -0
  17. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/yaml_loader.py +17 -2
  18. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/utils/__init__.py +50 -7
  19. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/utils/inspect.py +150 -6
  20. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/utils/parse_conf.py +60 -2
  21. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/validator.py +25 -7
  22. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/_termui_impl.py +4 -4
  23. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/_unicodefun.py +1 -1
  24. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/core.py +3 -3
  25. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/exceptions.py +2 -2
  26. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/formatting.py +3 -3
  27. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/parser.py +1 -1
  28. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/termui.py +1 -1
  29. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/testing.py +1 -1
  30. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/types.py +6 -6
  31. dynaconf-3.2.8/dynaconf/vendor/dotenv/py.typed +1 -0
  32. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/error.py +6 -7
  33. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/events.py +6 -11
  34. dynaconf-3.2.8/dynaconf/vendor/ruamel/yaml/py.typed +0 -0
  35. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarstring.py +2 -3
  36. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/tokens.py +18 -19
  37. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/PKG-INFO +43 -11
  38. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/SOURCES.txt +27 -0
  39. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/entry_points.txt +0 -1
  40. dynaconf-3.2.8/tests/test_base.py +1633 -0
  41. dynaconf-3.2.8/tests/test_basic.py +7 -0
  42. dynaconf-3.2.8/tests/test_cli.py +892 -0
  43. dynaconf-3.2.8/tests/test_compat.py +56 -0
  44. dynaconf-3.2.8/tests/test_django.py +44 -0
  45. dynaconf-3.2.8/tests/test_dynabox.py +103 -0
  46. dynaconf-3.2.8/tests/test_endtoend.py +67 -0
  47. dynaconf-3.2.8/tests/test_env_loader.py +523 -0
  48. dynaconf-3.2.8/tests/test_envvar_prefix.py +46 -0
  49. dynaconf-3.2.8/tests/test_feature_flag.py +34 -0
  50. dynaconf-3.2.8/tests/test_flask.py +165 -0
  51. dynaconf-3.2.8/tests/test_hooking.py +268 -0
  52. dynaconf-3.2.8/tests/test_ini_loader.py +208 -0
  53. dynaconf-3.2.8/tests/test_inspect.py +1009 -0
  54. dynaconf-3.2.8/tests/test_json_loader.py +230 -0
  55. dynaconf-3.2.8/tests/test_nested_loading.py +385 -0
  56. dynaconf-3.2.8/tests/test_py_loader.py +275 -0
  57. dynaconf-3.2.8/tests/test_redis.py +124 -0
  58. dynaconf-3.2.8/tests/test_settings_loader.py +167 -0
  59. dynaconf-3.2.8/tests/test_toml_loader.py +268 -0
  60. dynaconf-3.2.8/tests/test_utils.py +556 -0
  61. dynaconf-3.2.8/tests/test_validators.py +887 -0
  62. dynaconf-3.2.8/tests/test_validators_conditions.py +64 -0
  63. dynaconf-3.2.8/tests/test_vault.py +131 -0
  64. dynaconf-3.2.8/tests/test_yaml_loader.py +580 -0
  65. dynaconf-3.2.6/dynaconf/VERSION +0 -1
  66. {dynaconf-3.2.6 → dynaconf-3.2.8}/3.x-release-notes.md +0 -0
  67. {dynaconf-3.2.6 → dynaconf-3.2.8}/CONTRIBUTING.md +0 -0
  68. {dynaconf-3.2.6 → dynaconf-3.2.8}/CONTRIBUTORS.md +0 -0
  69. {dynaconf-3.2.6 → dynaconf-3.2.8}/LICENSE +0 -0
  70. {dynaconf-3.2.6 → dynaconf-3.2.8}/MANIFEST.in +0 -0
  71. {dynaconf-3.2.6 → dynaconf-3.2.8}/README.md +0 -0
  72. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/constants.py +0 -0
  73. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/contrib/__init__.py +0 -0
  74. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/contrib/django_dynaconf_v2.py +0 -0
  75. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/contrib/flask_dynaconf.py +0 -0
  76. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/loaders/base.py +0 -0
  77. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/strategies/__init__.py +0 -0
  78. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/strategies/filtering.py +0 -0
  79. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/test_settings.py +0 -0
  80. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/utils/boxing.py +0 -0
  81. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/utils/files.py +0 -0
  82. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/utils/functional.py +0 -0
  83. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/validator_conditions.py +0 -0
  84. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/__init__.py +0 -0
  85. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/__init__.py +0 -0
  86. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/box.py +0 -0
  87. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/box_list.py +0 -0
  88. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/config_box.py +0 -0
  89. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/converters.py +0 -0
  90. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/exceptions.py +0 -0
  91. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/from_file.py +0 -0
  92. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/box/shorthand_box.py +0 -0
  93. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/__init__.py +0 -0
  94. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/_bashcomplete.py +0 -0
  95. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/_compat.py +0 -0
  96. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/_textwrap.py +0 -0
  97. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/_winconsole.py +0 -0
  98. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/decorators.py +0 -0
  99. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/globals.py +0 -0
  100. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/click/utils.py +0 -0
  101. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/__init__.py +0 -0
  102. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/cli.py +0 -0
  103. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/compat.py +0 -0
  104. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/ipython.py +0 -0
  105. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/main.py +0 -0
  106. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/parser.py +0 -0
  107. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/dotenv/version.py +0 -0
  108. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/__init__.py +0 -0
  109. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/__init__.py +0 -0
  110. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/anchor.py +0 -0
  111. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/comments.py +0 -0
  112. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/compat.py +0 -0
  113. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/composer.py +0 -0
  114. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/configobjwalker.py +0 -0
  115. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/constructor.py +0 -0
  116. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/cyaml.py +0 -0
  117. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/dumper.py +0 -0
  118. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/emitter.py +0 -0
  119. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/loader.py +0 -0
  120. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/main.py +0 -0
  121. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/nodes.py +0 -0
  122. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/parser.py +0 -0
  123. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/reader.py +0 -0
  124. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/representer.py +0 -0
  125. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/resolver.py +0 -0
  126. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarbool.py +0 -0
  127. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarfloat.py +0 -0
  128. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scalarint.py +0 -0
  129. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/scanner.py +0 -0
  130. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/serializer.py +0 -0
  131. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/setup.py +0 -0
  132. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/timestamp.py +0 -0
  133. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/ruamel/yaml/util.py +0 -0
  134. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/toml/__init__.py +0 -0
  135. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/toml/decoder.py +0 -0
  136. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/toml/encoder.py +0 -0
  137. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/toml/ordered.py +0 -0
  138. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/toml/tz.py +0 -0
  139. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/__init__.py +0 -0
  140. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_parser.py +0 -0
  141. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_re.py +0 -0
  142. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_types.py +0 -0
  143. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf/vendor/tomllib/_writer.py +0 -0
  144. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/dependency_links.txt +0 -0
  145. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/not-zip-safe +0 -0
  146. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/requires.txt +0 -0
  147. {dynaconf-3.2.6 → dynaconf-3.2.8}/dynaconf.egg-info/top_level.txt +0 -0
  148. {dynaconf-3.2.6 → dynaconf-3.2.8}/setup.cfg +0 -0
  149. {dynaconf-3.2.6 → dynaconf-3.2.8}/setup.py +0 -0
  150. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/box-LICENSE.txt +0 -0
  151. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/click-LICENSE.rst +0 -0
  152. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/licenses.sh +0 -0
  153. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/python-dotenv-LICENSE.txt +0 -0
  154. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/ruamel.yaml-LICENSE.txt +0 -0
  155. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/toml-LICENSE.txt +0 -0
  156. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/tomli-LICENSE.txt +0 -0
  157. {dynaconf-3.2.6 → dynaconf-3.2.8}/vendor_licenses/vendor_versions.txt +0 -0
@@ -2,6 +2,41 @@ 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
+
19
+ ## [3.2.7](https://github.com/dynaconf/dynaconf/releases/tag/3.2.7) - 2025-01-21
20
+
21
+ ### Bug Fixes
22
+
23
+ - lazy validator's default value would evaluate early (#1198). *By Pedro Brochado*.
24
+ - Fixed an error that would raise when using get_history() with lazy values (#1184) (#1185). *By Pedro Brochado*.
25
+ - Fixed Redis loader when ENV prefix is `None`.
26
+ - Populate object method now takes `internal` attribute to filter out internal variables.
27
+ - On CLI `json.dumps` defaults to `repr` for types that cannot be serialized.
28
+ - Added an identifier to validator calls of `set` method
29
+ - Fix django app discovery using DJANGO_SETTINGS_MODULE variable
30
+
31
+ ### Features
32
+
33
+ - Added `@insert` token to call `list.insert`
34
+ - Allow env loader to load from multiple prefixes
35
+ - Allow multiple composable current environments
36
+ - Track more data on `load_file` method
37
+ - Added `--json` to dynaconf list CLI
38
+
39
+
5
40
  ## [3.2.6](https://github.com/dynaconf/dynaconf/releases/tag/3.2.6) - 2024-07-19
6
41
 
7
42
  ## [3.2.5](https://github.com/pedro-psb/dynaconf/releases/tag/3.2.5) - 2024-03-18
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: dynaconf
3
- Version: 3.2.6
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
  <!-- [![Dynaconf](docs/img/logo_400.svg?sanitize=true)](http://dynaconf.com) -->
48
81
 
@@ -180,4 +213,3 @@ Main discussions happens on [Discussions Tab](https://github.com/dynaconf/dynaco
180
213
  If you are looking for something similar to Dynaconf to use in your Rust projects: https://github.com/rubik/hydroconf
181
214
 
182
215
  And a special thanks to [Caneco](https://twitter.com/caneco) for the logo.
183
-
@@ -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
  ]
@@ -968,6 +968,15 @@ class Settings:
968
968
  self.store.get(key, None) if not isinstance(parsed, Lazy) else None
969
969
  )
970
970
 
971
+ if getattr(parsed, "_dynaconf_insert", False):
972
+ # `@insert` calls insert in a list by index
973
+ if existing and isinstance(existing, list):
974
+ source_metadata = source_metadata._replace(merged=True)
975
+ existing.insert(parsed.index, parsed.unwrap())
976
+ parsed = existing
977
+ else:
978
+ parsed = [parsed.unwrap()]
979
+
971
980
  if getattr(parsed, "_dynaconf_del", None):
972
981
  self.unset(key, force=True) # `@del` in a first level var.
973
982
  return
@@ -1152,6 +1161,10 @@ class Settings:
1152
1161
  """Clean end Execute all loaders"""
1153
1162
  self.clean()
1154
1163
  self._loaded_hooks.clear()
1164
+ for hook in self._post_hooks:
1165
+ with suppress(AttributeError, TypeError):
1166
+ hook._called = False
1167
+
1155
1168
  self.execute_loaders(env, silent)
1156
1169
 
1157
1170
  def execute_loaders(
@@ -1208,7 +1221,13 @@ class Settings:
1208
1221
  last_loader.load(self, env, silent, key)
1209
1222
 
1210
1223
  def load_file(
1211
- self, path=None, env=None, silent=True, key=None, validate=empty
1224
+ self,
1225
+ path=None,
1226
+ env=None,
1227
+ silent=True,
1228
+ key=None,
1229
+ validate=empty,
1230
+ run_hooks=True,
1212
1231
  ):
1213
1232
  """Programmatically load files from ``path``.
1214
1233
 
@@ -1217,63 +1236,95 @@ class Settings:
1217
1236
  - Directory of the last loaded file
1218
1237
  - CWD
1219
1238
 
1220
- :param path: A single filename or a file list
1239
+ :param path: A single filename, a glob or a file list
1221
1240
  :param env: Which env to load from file (default current_env)
1222
1241
  :param silent: Should raise errors?
1223
1242
  :param key: Load a single key?
1224
1243
  :param validate: Should trigger validation?
1244
+ :param run_hooks: Should run collected hooks?
1225
1245
  """
1246
+ files = ensure_a_list(path)
1247
+ if not files: # a glob pattern may return empty
1248
+ return
1249
+
1226
1250
  if validate is empty:
1227
1251
  validate = self.get("VALIDATE_ON_UPDATE_FOR_DYNACONF")
1228
1252
 
1229
1253
  env = (env or self.current_env).upper()
1230
- files = ensure_a_list(path)
1231
- if files:
1232
- already_loaded = set()
1233
- for _filename in files:
1254
+
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
+
1234
1298
  # load_file() will handle validation later
1235
1299
  with suppress(ValidationError):
1236
- if py_loader.try_to_load_from_py_module_name(
1237
- obj=self, name=_filename, silent=True
1238
- ):
1239
- # if it was possible to load from module name
1240
- # continue the loop.
1241
- continue
1242
-
1243
- root_dir = str(self._root_path or os.getcwd())
1244
-
1245
- # Issue #494
1246
- if (
1247
- isinstance(_filename, Path)
1248
- and str(_filename.parent) in root_dir
1249
- ): # pragma: no cover
1250
- filepath = str(_filename)
1251
- else:
1252
- filepath = os.path.join(root_dir, str(_filename))
1253
-
1254
- paths = [
1255
- p for p in sorted(glob(filepath)) if ".local." not in p
1256
- ]
1257
- local_paths = [
1258
- p for p in sorted(glob(filepath)) if ".local." in p
1259
- ]
1260
-
1261
- # Handle possible *.globs sorted alphanumeric
1262
- for path in paths + local_paths:
1263
- if path in already_loaded: # pragma: no cover
1264
- continue
1265
-
1266
- # load_file() will handle validation later
1267
- with suppress(ValidationError):
1268
- settings_loader(
1269
- obj=self,
1270
- env=env,
1271
- silent=silent,
1272
- key=key,
1273
- filename=path,
1274
- validate=validate,
1275
- )
1276
- already_loaded.add(path)
1300
+ source_metadata = SourceMetadata(
1301
+ loader=f"load_file@{caller.filename}:{caller.lineno}",
1302
+ identifier=path,
1303
+ env=env,
1304
+ )
1305
+ settings_loader(
1306
+ obj=self,
1307
+ env=env,
1308
+ silent=silent,
1309
+ key=key,
1310
+ filename=path,
1311
+ validate=validate,
1312
+ identifier=source_metadata,
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
+ )
1277
1328
 
1278
1329
  # handle param `validate`
1279
1330
  if validate is True:
@@ -1357,19 +1408,33 @@ class Settings:
1357
1408
  value = self.get_fresh(key)
1358
1409
  return value is True or value in true_values
1359
1410
 
1360
- def populate_obj(self, obj, keys=None, ignore=None):
1411
+ def populate_obj(
1412
+ self,
1413
+ obj,
1414
+ keys=None,
1415
+ ignore=None,
1416
+ internal=False,
1417
+ convert_to_dict=False,
1418
+ ):
1361
1419
  """Given the `obj` populate it using self.store items.
1362
1420
 
1363
1421
  :param obj: An object to be populated, a class instance.
1364
1422
  :param keys: A list of keys to be included.
1365
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.
1366
1427
  """
1428
+ data = self.to_dict(internal=internal) if convert_to_dict else self
1367
1429
  keys = keys or self.keys()
1368
1430
  for key in keys:
1369
1431
  key = upperfy(key)
1432
+ if not internal:
1433
+ if key in UPPER_DEFAULT_SETTINGS:
1434
+ continue
1370
1435
  if ignore and key in ignore:
1371
1436
  continue
1372
- value = self.get(key, empty)
1437
+ value = data.get(key, empty)
1373
1438
  if value is not empty:
1374
1439
  setattr(obj, key, value)
1375
1440
 
@@ -1,22 +1,25 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ import inspect as python_inspect
4
5
  import json
5
6
  import os
6
7
  import pprint
7
8
  import sys
8
9
  import warnings
9
10
  import webbrowser
11
+ from contextlib import redirect_stdout
10
12
  from contextlib import suppress
11
13
  from pathlib import Path
12
- from typing import TYPE_CHECKING
13
14
 
14
15
  from dynaconf import constants
15
16
  from dynaconf import default_settings
16
17
  from dynaconf import LazySettings
17
18
  from dynaconf import loaders
18
19
  from dynaconf import settings as legacy_settings
20
+ from dynaconf.base import Settings
19
21
  from dynaconf.loaders.py_loader import get_module
22
+ from dynaconf.utils import prepare_json
20
23
  from dynaconf.utils import upperfy
21
24
  from dynaconf.utils.files import read_file
22
25
  from dynaconf.utils.functional import empty
@@ -25,6 +28,7 @@ from dynaconf.utils.inspect import EnvNotFoundError
25
28
  from dynaconf.utils.inspect import inspect_settings
26
29
  from dynaconf.utils.inspect import KeyNotFoundError
27
30
  from dynaconf.utils.inspect import OutputFormatError
31
+ from dynaconf.utils.inspect import print_debug_info
28
32
  from dynaconf.utils.parse_conf import parse_conf_data
29
33
  from dynaconf.utils.parse_conf import unparse_conf_data
30
34
  from dynaconf.validator import ValidationError
@@ -33,9 +37,6 @@ from dynaconf.vendor import click
33
37
  from dynaconf.vendor import toml
34
38
  from dynaconf.vendor import tomllib
35
39
 
36
- if TYPE_CHECKING: # pragma: no cover
37
- from dynaconf.base import Settings # noqa: F401
38
-
39
40
  os.environ["PYTHONIOENCODING"] = "utf-8"
40
41
 
41
42
  CWD = None
@@ -54,7 +55,14 @@ def set_settings(ctx, instance=None):
54
55
 
55
56
  settings = None
56
57
 
57
- _echo_enabled = ctx.invoked_subcommand not in ["get", "inspect", None]
58
+ _echo_enabled = ctx.invoked_subcommand not in [
59
+ "get",
60
+ "inspect",
61
+ "debug-info",
62
+ None,
63
+ ]
64
+ if "--json" in click.get_os_args():
65
+ _echo_enabled = False
58
66
 
59
67
  if instance is not None:
60
68
  if ctx.invoked_subcommand in ["init"]:
@@ -79,19 +87,16 @@ def set_settings(ctx, instance=None):
79
87
  )
80
88
  elif "DJANGO_SETTINGS_MODULE" in os.environ: # pragma: no cover
81
89
  sys.path.insert(0, os.path.abspath(os.getcwd()))
82
- try:
83
- # Django extension v2
84
- from django.conf import settings # noqa
85
- import dynaconf # noqa: F401
86
- import django
87
-
88
- # see https://docs.djangoproject.com/en/4.2/ref/applications/
89
- # at #troubleshooting
90
- django.setup()
90
+ import django # noqa
91
91
 
92
- settings.DYNACONF.configure()
93
- except AttributeError:
94
- settings = LazySettings()
92
+ django.setup() # ensure django is setup to avoid AppRegistryNotReady
93
+ settings_module = import__django_settings(
94
+ os.environ["DJANGO_SETTINGS_MODULE"]
95
+ )
96
+ for member in python_inspect.getmembers(settings_module):
97
+ if isinstance(member[1], (LazySettings, Settings)):
98
+ settings = member[1]
99
+ break
95
100
 
96
101
  if settings is not None and _echo_enabled:
97
102
  click.echo(
@@ -117,6 +122,18 @@ def set_settings(ctx, instance=None):
117
122
  settings = LazySettings()
118
123
 
119
124
 
125
+ def import__django_settings(django_settings_module):
126
+ """Import the Django settings module from the string importable path."""
127
+ try:
128
+ with redirect_stdout(None):
129
+ module = importlib.import_module(django_settings_module)
130
+ except ImportError as e:
131
+ raise click.UsageError(e)
132
+ except FileNotFoundError:
133
+ return
134
+ return module
135
+
136
+
120
137
  def import_settings(dotted_path):
121
138
  """Import settings instance from python dotted path.
122
139
 
@@ -130,8 +147,10 @@ def import_settings(dotted_path):
130
147
  raise click.UsageError(
131
148
  f"invalid path to settings instance: {dotted_path}"
132
149
  )
150
+
133
151
  try:
134
- module = importlib.import_module(module)
152
+ with redirect_stdout(None):
153
+ module = importlib.import_module(module)
135
154
  except ImportError as e:
136
155
  raise click.UsageError(e)
137
156
  except FileNotFoundError:
@@ -485,7 +504,7 @@ def get(key, default, env, unparse):
485
504
  result = unparse_conf_data(result)
486
505
 
487
506
  if isinstance(result, (dict, list, tuple)):
488
- result = json.dumps(result, sort_keys=True)
507
+ result = json.dumps(prepare_json(result), sort_keys=True, default=repr)
489
508
 
490
509
  click.echo(result, nl=False)
491
510
 
@@ -530,7 +549,24 @@ def get(key, default, env, unparse):
530
549
  default=False,
531
550
  help="Output file is flat (do not include [env] name)",
532
551
  )
533
- def _list(env, key, more, loader, _all=False, output=None, flat=False):
552
+ @click.option(
553
+ "--json",
554
+ "_json",
555
+ "-j",
556
+ is_flag=True,
557
+ default=False,
558
+ help="Prints out data serialized as JSON",
559
+ )
560
+ def _list(
561
+ env,
562
+ key,
563
+ more,
564
+ loader,
565
+ _all=False,
566
+ output=None,
567
+ flat=False,
568
+ _json=False,
569
+ ):
534
570
  """
535
571
  Lists user defined settings or all (including internal configs).
536
572
 
@@ -552,14 +588,15 @@ def _list(env, key, more, loader, _all=False, output=None, flat=False):
552
588
  if cur_env == "main":
553
589
  flat = True
554
590
 
555
- click.echo(
556
- click.style(
557
- f"Working in {cur_env} environment ",
558
- bold=True,
559
- bg="bright_blue",
560
- fg="bright_white",
591
+ if not _json:
592
+ click.echo(
593
+ click.style(
594
+ f"Working in {cur_env} environment ",
595
+ bold=True,
596
+ bg="bright_blue",
597
+ fg="bright_white",
598
+ )
561
599
  )
562
- )
563
600
 
564
601
  if not loader:
565
602
  data = settings.as_dict(env=env, internal=_all)
@@ -585,14 +622,20 @@ def _list(env, key, more, loader, _all=False, output=None, flat=False):
585
622
  return f"{key}{data_type} {value}"
586
623
 
587
624
  if not key:
588
- datalines = "\n".join(
589
- format_setting(k, v)
590
- for k, v in data.items()
591
- if k not in data.get("RENAMED_VARS", [])
592
- )
593
- (click.echo_via_pager if more else click.echo)(datalines)
625
+ if not _json:
626
+ datalines = "\n".join(
627
+ format_setting(k, v)
628
+ for k, v in data.items()
629
+ if k not in data.get("RENAMED_VARS", [])
630
+ )
631
+ (click.echo_via_pager if more else click.echo)(datalines)
594
632
  if output:
595
- loaders.write(output, data, env=not flat and cur_env)
633
+ loaders.write(output, prepare_json(data), env=not flat and cur_env)
634
+ if _json:
635
+ json_data = json.dumps(
636
+ prepare_json(data), sort_keys=True, default=repr
637
+ )
638
+ click.echo(json_data, nl=False)
596
639
  else:
597
640
  key = upperfy(key)
598
641
 
@@ -605,9 +648,16 @@ def _list(env, key, more, loader, _all=False, output=None, flat=False):
605
648
  click.secho("Key not found", bg="red", fg="white", err=True)
606
649
  return
607
650
 
608
- click.echo(format_setting(key, value))
651
+ if not _json:
652
+ click.echo(format_setting(key, value))
609
653
  if output:
610
- loaders.write(output, {key: value}, env=not flat and cur_env)
654
+ loaders.write(
655
+ output, prepare_json({key: value}), env=not flat and cur_env
656
+ )
657
+ if _json:
658
+ click.echo(
659
+ json.dumps(prepare_json({key: value}), default=repr), nl=True
660
+ )
611
661
 
612
662
  if env:
613
663
  settings.setenv()
@@ -813,7 +863,10 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
813
863
  @main.command()
814
864
  @click.option("--key", "-k", help="Filters result by key.")
815
865
  @click.option(
816
- "--env", "-e", help="Filters result by environment.", default=None
866
+ "--env",
867
+ "-e",
868
+ help="Filters result by environment on --report-mode=inspect.",
869
+ default=None,
817
870
  )
818
871
  @click.option(
819
872
  "--format",
@@ -826,7 +879,7 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
826
879
  "--old-first",
827
880
  "new_first",
828
881
  "-s",
829
- help="Invert history sorting to 'old-first'",
882
+ help="Invert history sorting to 'old-first' on --report-mode=inspect.",
830
883
  default=True,
831
884
  is_flag=True,
832
885
  )
@@ -836,7 +889,7 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
836
889
  "-n",
837
890
  default=None,
838
891
  type=int,
839
- help="Limits how many history entries are shown.",
892
+ help="Limits how many history entries are shown on --report-mode=inspect.",
840
893
  )
841
894
  @click.option(
842
895
  "--all",
@@ -846,28 +899,54 @@ INSPECT_FORMATS = list(builtin_dumpers.keys())
846
899
  is_flag=True,
847
900
  help="Show dynaconf internal settings?",
848
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
+ )
849
915
  def inspect(
850
- key, env, format, new_first, history_limit, _all
916
+ key, env, format, new_first, history_limit, _all, report_mode, verbose
851
917
  ): # pragma: no cover
852
918
  """
853
919
  Inspect the loading history of the given settings instance.
854
920
 
855
921
  Filters by key and environment, otherwise shows all.
856
922
  """
857
- try:
858
- inspect_settings(
923
+
924
+ if report_mode == "debug":
925
+ print_debug_info(
859
926
  settings,
860
- key=key,
861
- env=env or None,
862
927
  dumper=format,
863
- new_first=new_first,
864
- include_internal=_all,
865
- history_limit=history_limit,
866
- print_report=True,
928
+ verbosity=verbose,
929
+ key=key,
867
930
  )
868
931
  click.echo()
869
- except (KeyNotFoundError, EnvNotFoundError, OutputFormatError) as err:
870
- click.echo(err)
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")
871
950
  sys.exit(1)
872
951
 
873
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"]