dynaconf 3.3.0__tar.gz → 3.3.2__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 (173) hide show
  1. {dynaconf-3.3.0 → dynaconf-3.3.2}/CHANGELOG.md +41 -0
  2. {dynaconf-3.3.0 → dynaconf-3.3.2}/PKG-INFO +3 -3
  3. dynaconf-3.3.2/dynaconf/VERSION +1 -0
  4. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/base.py +22 -9
  5. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/cli.py +2 -2
  6. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/hooking.py +1 -1
  7. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/__init__.py +3 -2
  8. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/nodes.py +13 -65
  9. dynaconf-3.3.2/dynaconf/typed/compat.py +7 -0
  10. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/guards.py +1 -1
  11. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/main.py +1 -6
  12. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/types.py +1 -1
  13. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/utils.py +1 -1
  14. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/validators.py +1 -1
  15. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/utils/__init__.py +20 -1
  16. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/utils/files.py +5 -4
  17. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/utils/inspect.py +1 -1
  18. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/utils/parse_conf.py +1 -1
  19. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/validator.py +1 -1
  20. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/validator_conditions.py +1 -7
  21. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/PKG-INFO +3 -3
  22. {dynaconf-3.3.0 → dynaconf-3.3.2}/pyproject.toml +9 -6
  23. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_base.py +68 -11
  24. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_cli.py +73 -35
  25. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_endtoend.py +4 -1
  26. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_env_loader.py +2 -0
  27. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_inspect.py +2 -0
  28. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_loaders.py +2 -0
  29. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_nested_loading.py +2 -0
  30. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_nodes.py +96 -80
  31. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_typed.py +5 -6
  32. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_utils.py +68 -0
  33. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_validators.py +18 -0
  34. dynaconf-3.3.0/dynaconf/VERSION +0 -1
  35. dynaconf-3.3.0/dynaconf/typed/compat.py +0 -24
  36. {dynaconf-3.3.0 → dynaconf-3.3.2}/CONTRIBUTING.md +0 -0
  37. {dynaconf-3.3.0 → dynaconf-3.3.2}/CONTRIBUTORS.md +0 -0
  38. {dynaconf-3.3.0 → dynaconf-3.3.2}/LICENSE +0 -0
  39. {dynaconf-3.3.0 → dynaconf-3.3.2}/MANIFEST.in +0 -0
  40. {dynaconf-3.3.0 → dynaconf-3.3.2}/README.md +0 -0
  41. {dynaconf-3.3.0 → dynaconf-3.3.2}/RELEASING.md +0 -0
  42. {dynaconf-3.3.0 → dynaconf-3.3.2}/SECURITY.md +0 -0
  43. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/__init__.py +0 -0
  44. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/__main__.py +0 -0
  45. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/constants.py +0 -0
  46. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/contrib/__init__.py +0 -0
  47. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/contrib/django_dynaconf_v2.py +0 -0
  48. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/contrib/flask_dynaconf.py +0 -0
  49. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/default_settings.py +0 -0
  50. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/base.py +0 -0
  51. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/env_loader.py +0 -0
  52. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/ini_loader.py +0 -0
  53. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/json_loader.py +0 -0
  54. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/py_loader.py +0 -0
  55. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/redis_loader.py +0 -0
  56. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/toml_loader.py +0 -0
  57. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/vault_loader.py +0 -0
  58. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/loaders/yaml_loader.py +0 -0
  59. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/strategies/__init__.py +0 -0
  60. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/strategies/filtering.py +0 -0
  61. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/test_settings.py +0 -0
  62. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/__init__.py +0 -0
  63. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/typed/exceptions.py +0 -0
  64. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/utils/boxing.py +0 -0
  65. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/utils/functional.py +0 -0
  66. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/__init__.py +0 -0
  67. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/__init__.py +0 -0
  68. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/box.py +0 -0
  69. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/box_list.py +0 -0
  70. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/config_box.py +0 -0
  71. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/converters.py +0 -0
  72. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/exceptions.py +0 -0
  73. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/from_file.py +0 -0
  74. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/box/shorthand_box.py +0 -0
  75. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/__init__.py +0 -0
  76. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/_bashcomplete.py +0 -0
  77. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/_compat.py +0 -0
  78. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/_termui_impl.py +0 -0
  79. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/_textwrap.py +0 -0
  80. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/_unicodefun.py +0 -0
  81. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/_winconsole.py +0 -0
  82. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/core.py +0 -0
  83. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/decorators.py +0 -0
  84. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/exceptions.py +0 -0
  85. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/formatting.py +0 -0
  86. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/globals.py +0 -0
  87. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/parser.py +0 -0
  88. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/termui.py +0 -0
  89. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/testing.py +0 -0
  90. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/types.py +0 -0
  91. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/click/utils.py +0 -0
  92. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/__init__.py +0 -0
  93. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/cli.py +0 -0
  94. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/compat.py +0 -0
  95. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/ipython.py +0 -0
  96. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/main.py +0 -0
  97. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/parser.py +0 -0
  98. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/py.typed +0 -0
  99. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/dotenv/version.py +0 -0
  100. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/__init__.py +0 -0
  101. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/__init__.py +0 -0
  102. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/anchor.py +0 -0
  103. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/comments.py +0 -0
  104. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/compat.py +0 -0
  105. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/composer.py +0 -0
  106. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/configobjwalker.py +0 -0
  107. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/constructor.py +0 -0
  108. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/cyaml.py +0 -0
  109. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/dumper.py +0 -0
  110. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/emitter.py +0 -0
  111. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/error.py +0 -0
  112. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/events.py +0 -0
  113. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/loader.py +0 -0
  114. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/main.py +0 -0
  115. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/nodes.py +0 -0
  116. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/parser.py +0 -0
  117. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/py.typed +0 -0
  118. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/reader.py +0 -0
  119. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/representer.py +0 -0
  120. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/resolver.py +0 -0
  121. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/scalarbool.py +0 -0
  122. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/scalarfloat.py +0 -0
  123. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/scalarint.py +0 -0
  124. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/scalarstring.py +0 -0
  125. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/scanner.py +0 -0
  126. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/serializer.py +0 -0
  127. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/setup.py +0 -0
  128. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/timestamp.py +0 -0
  129. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/tokens.py +0 -0
  130. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/ruamel/yaml/util.py +0 -0
  131. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/toml/__init__.py +0 -0
  132. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/toml/decoder.py +0 -0
  133. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/toml/encoder.py +0 -0
  134. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/toml/ordered.py +0 -0
  135. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/toml/tz.py +0 -0
  136. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/tomllib/__init__.py +0 -0
  137. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/tomllib/_parser.py +0 -0
  138. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/tomllib/_re.py +0 -0
  139. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/tomllib/_types.py +0 -0
  140. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf/vendor/tomllib/_writer.py +0 -0
  141. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/SOURCES.txt +0 -0
  142. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/dependency_links.txt +0 -0
  143. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/entry_points.txt +0 -0
  144. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/not-zip-safe +0 -0
  145. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/requires.txt +0 -0
  146. {dynaconf-3.3.0 → dynaconf-3.3.2}/dynaconf.egg-info/top_level.txt +0 -0
  147. {dynaconf-3.3.0 → dynaconf-3.3.2}/setup.cfg +0 -0
  148. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_basic.py +0 -0
  149. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_compat.py +0 -0
  150. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_django.py +0 -0
  151. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_dynabox.py +0 -0
  152. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_envvar_prefix.py +0 -0
  153. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_feature_flag.py +0 -0
  154. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_flask.py +0 -0
  155. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_hooking.py +0 -0
  156. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_ini_loader.py +0 -0
  157. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_json_loader.py +0 -0
  158. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_py_loader.py +0 -0
  159. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_redis.py +0 -0
  160. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_release_utility.py +0 -0
  161. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_settings_loader.py +0 -0
  162. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_toml_loader.py +0 -0
  163. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_validators_conditions.py +0 -0
  164. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_vault.py +0 -0
  165. {dynaconf-3.3.0 → dynaconf-3.3.2}/tests/test_yaml_loader.py +0 -0
  166. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/box-LICENSE.txt +0 -0
  167. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/click-LICENSE.rst +0 -0
  168. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/licenses.sh +0 -0
  169. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/python-dotenv-LICENSE.txt +0 -0
  170. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/ruamel.yaml-LICENSE.txt +0 -0
  171. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/toml-LICENSE.txt +0 -0
  172. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/tomli-LICENSE.txt +0 -0
  173. {dynaconf-3.3.0 → dynaconf-3.3.2}/vendor_licenses/vendor_versions.txt +0 -0
@@ -2,6 +2,47 @@ Changelog
2
2
  =========
3
3
 
4
4
  <!-- insertion marker -->
5
+ ## [3.3.2](https://github.com/dynaconf/dynaconf/releases/tag/3.3.2) - 2026-06-29
6
+
7
+ ### Bug Fixes
8
+
9
+
10
+ - Don't treat a string sharing a converter prefix as a cast (#1412). By @sarathfrancis90.
11
+
12
+
13
+ - Fixed 'dynaconf validate' edge cases with validation_file.toml (#1411). By @pedro-psb.
14
+
15
+
16
+ - Avoid RecursionError in from_env with validate_on_update (#1409). By @Redbot.
17
+
18
+
19
+ - Avoid crash merging a dynaconf_merge list into a missing key (#1410). By @sarathfrancis90.
20
+
21
+
22
+ ### Chore
23
+
24
+
25
+ - Fix check-release command from release utility. By @pedro-psb.
26
+
27
+
28
+ - Improve changelog generation output. By @pedro-psb.
29
+
30
+ ## [3.3.1](https://github.com/dynaconf/dynaconf/releases/tag/3.3.1) - 2026-06-26
31
+
32
+ ### Bug Fixes
33
+
34
+ - Remove call to deprecated functions on public APIs (#1404). *By Pedro Brochado*.
35
+ - Improve converage of DataList and DataDict (#1403). *By Pedro Brochado*.
36
+ - support Python 3.14 Union type repr and dict iteration changes. *By Pedro Brochado*.
37
+ - preserve nested dict key order when object_merge merges old into new. *By Vincent Gao*.
38
+ - prevent RecursionError on dotted set with bracket in first segment. *By Sarath Francis*.
39
+
40
+ ### Chore
41
+
42
+ - Remove support for EOL python 3.9 (#1396). *By Pedro Brochado*.
43
+ - Add support for python 3.14. *By Pedro Brochado*.
44
+ - bump version to 3.3.1-dev0. *By pedro-psb*.
45
+
5
46
  ## [3.3.0](https://github.com/dynaconf/dynaconf/releases/tag/3.3.0) - 2026-06-24
6
47
 
7
48
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dynaconf
3
- Version: 3.3.0
3
+ Version: 3.3.2
4
4
  Summary: The dynamic configurator for your Python Project
5
5
  Author-email: Bruno Rocha <rochacbruno@gmail.com>
6
6
  License: MIT
@@ -16,15 +16,15 @@ Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python
17
17
  Classifier: Programming Language :: Python :: 3
18
18
  Classifier: Programming Language :: Python :: 3 :: Only
19
- Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
23
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Topic :: Utilities
25
25
  Classifier: Topic :: Software Development :: Libraries
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
- Requires-Python: <3.14,>=3.9
27
+ Requires-Python: <3.15,>=3.10
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
30
  License-File: vendor_licenses/box-LICENSE.txt
@@ -0,0 +1 @@
1
+ 3.3.2
@@ -7,6 +7,7 @@ import os
7
7
  import re
8
8
  import warnings
9
9
  from collections import defaultdict
10
+ from collections.abc import Callable
10
11
  from contextlib import contextmanager
11
12
  from contextlib import suppress
12
13
  from dataclasses import dataclass
@@ -15,7 +16,6 @@ from functools import lru_cache
15
16
  from functools import wraps
16
17
  from pathlib import Path
17
18
  from typing import Any
18
- from typing import Callable
19
19
  from typing import Optional
20
20
  from typing import Union
21
21
 
@@ -40,6 +40,7 @@ from dynaconf.utils import missing
40
40
  from dynaconf.utils import normalize_kwargs
41
41
  from dynaconf.utils import object_merge
42
42
  from dynaconf.utils import RENAMED_VARS
43
+ from dynaconf.utils import to_dict
43
44
  from dynaconf.utils import upperfy
44
45
  from dynaconf.utils.files import find_file
45
46
  from dynaconf.utils.files import glob
@@ -502,7 +503,7 @@ class Settings:
502
503
  """
503
504
  ctx_mgr = suppress() if env is None else self.using_env(env)
504
505
  with ctx_mgr:
505
- data = self.store.to_dict().copy()
506
+ data = to_dict(self.store)
506
507
  # if not internal remove internal settings
507
508
  if not internal:
508
509
  for name in UPPER_DEFAULT_SETTINGS:
@@ -787,7 +788,7 @@ class Settings:
787
788
  new_data.update(
788
789
  {
789
790
  key: value
790
- for key, value in self.store.to_dict().copy().items()
791
+ for key, value in to_dict(self.store).items()
791
792
  if key.isupper() and key not in RENAMED_VARS
792
793
  }
793
794
  )
@@ -879,8 +880,9 @@ class Settings:
879
880
  return self.MAIN_ENV_FOR_DYNACONF.lower()
880
881
 
881
882
  if self.FORCE_ENV_FOR_DYNACONF is not None:
882
- self.ENV_FOR_DYNACONF = self.FORCE_ENV_FOR_DYNACONF
883
- return self.FORCE_ENV_FOR_DYNACONF
883
+ forced = self.FORCE_ENV_FOR_DYNACONF
884
+ self.set("ENV_FOR_DYNACONF", forced, validate=False)
885
+ return forced
884
886
 
885
887
  try:
886
888
  return self.loaded_envs[-1]
@@ -1107,11 +1109,17 @@ class Settings:
1107
1109
  full_path=split_keys,
1108
1110
  list_merge=list_merge, # when to use deep / shallow replace?
1109
1111
  )
1112
+ # `new_data` is keyed by the already-resolved top level key
1113
+ # (`split_keys[0]`). With index merge disabled a bracket is a literal
1114
+ # key, so that key can still contain `[` (e.g. `servers[0]`). Writing
1115
+ # it with dotted_lookup on would route it back into `_dotted_set` and
1116
+ # recurse forever, so set the resolved keys literally.
1110
1117
  self.update(
1111
1118
  data=new_data,
1112
1119
  tomlfy=tomlfy,
1113
1120
  validate=validate,
1114
1121
  tomlfy_filter=tomlfy_filter,
1122
+ dotted_lookup=False,
1115
1123
  **kwargs,
1116
1124
  )
1117
1125
 
@@ -1194,10 +1202,15 @@ class Settings:
1194
1202
  tomlfy_filter=tomlfy_filter,
1195
1203
  )
1196
1204
 
1197
- # Fix for #869 - The call to getattr trigger early evaluation
1198
- existing = (
1199
- self.store.get(key, None) if not isinstance(parsed, Lazy) else None
1200
- )
1205
+ # Fix for #869 - Evaluating an existing lazy value during set can
1206
+ # fail before a complete replacement value is stored.
1207
+ existing = None
1208
+ if not isinstance(parsed, Lazy):
1209
+ with suppress(AttributeError, KeyError):
1210
+ if isinstance(self.store, DataDict):
1211
+ existing = self.store.get(key, bypass_eval=True)
1212
+ else:
1213
+ existing = self.store.get(key)
1201
1214
 
1202
1215
  if getattr(parsed, "_dynaconf_insert", False):
1203
1216
  # `@insert` calls insert in a list by index
@@ -820,9 +820,9 @@ def validate(path): # pragma: no cover
820
820
  )
821
821
  sys.exit(1)
822
822
 
823
- # guarantee there is an environment
824
823
  validation_data = {k.lower(): v for k, v in validation_data.items()}
825
- if not validation_data.get("default"):
824
+ if not settings.ENVIRONMENTS_FOR_DYNACONF:
825
+ # flat file: top-level keys are field names, not environments
826
826
  validation_data = {"default": validation_data}
827
827
 
828
828
  success = True
@@ -1,10 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from dataclasses import dataclass
4
5
  from enum import Enum
5
6
  from functools import wraps
6
7
  from typing import Any
7
- from typing import Callable
8
8
 
9
9
  from dynaconf.base import Settings
10
10
  from dynaconf.loaders.base import SourceMetadata
@@ -2,8 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import importlib
4
4
  import os
5
+ from collections.abc import Callable
5
6
  from contextlib import suppress
6
- from typing import Callable
7
7
  from typing import TYPE_CHECKING
8
8
 
9
9
  from dynaconf import constants as ct
@@ -17,6 +17,7 @@ from dynaconf.loaders.base import SourceMetadata
17
17
  from dynaconf.nodes import DataDict
18
18
  from dynaconf.utils import deduplicate
19
19
  from dynaconf.utils import ensure_a_list
20
+ from dynaconf.utils import to_dict
20
21
  from dynaconf.utils.files import get_local_filename
21
22
  from dynaconf.utils.files import glob
22
23
  from dynaconf.utils.files import has_magic
@@ -443,7 +444,7 @@ def write(filename, data, env=None, merge=False):
443
444
  if not loader:
444
445
  raise OSError(f"{loader_name} cannot be found.")
445
446
 
446
- data = DataDict(data, box_settings={}).to_dict()
447
+ data = to_dict(DataDict(data, box_settings={}))
447
448
  if loader is not py_loader and env and env not in data:
448
449
  data = {env: data}
449
450
 
@@ -53,12 +53,6 @@ class DataDict(dict):
53
53
  self.__meta__ = NodeMetadata(core=core)
54
54
  convert_containers(self, self.items(), core)
55
55
 
56
- def update(self, data):
57
- super().update(ensure_containers(data, self.__meta__.core))
58
-
59
- def setdefault(self, k, v):
60
- return super().setdefault(k, ensure_containers(v, self.__meta__.core))
61
-
62
56
  def copy(self, bypass_eval=False):
63
57
  if not bypass_eval:
64
58
  return self.__class__(
@@ -115,11 +109,10 @@ class DataDict(dict):
115
109
  def items(self, bypass_eval=False):
116
110
  if not bypass_eval:
117
111
  yield from super().items()
118
- yield from ((k, self.get(k, bypass_eval=True)) for k in self.keys())
119
-
120
- def __setitem__(self, k, v):
121
- result = ensure_containers(v, self.__meta__.core)
122
- super().__setitem__(k, result)
112
+ else:
113
+ yield from (
114
+ (k, self.get(k, bypass_eval=True)) for k in self.keys()
115
+ )
123
116
 
124
117
  def __setattr__(self, k, v):
125
118
  # NOTE: We shouldnt use setattr to store items. If an item was assigned with setatttr
@@ -178,15 +171,7 @@ class DataDict(dict):
178
171
  box_deprecation_warning(
179
172
  "to_dict", "DataDict", "Use dict(data_dict) instead."
180
173
  ) # pragma: nocover
181
- out_dict = dict(self)
182
- for k, v in out_dict.items():
183
- if v is self:
184
- out_dict[k] = out_dict
185
- elif isinstance(v, DataDict):
186
- out_dict[k] = v.to_dict()
187
- elif isinstance(v, DataList):
188
- out_dict[k] = v.to_list()
189
- return out_dict
174
+ return ut.to_dict(self)
190
175
 
191
176
  def merge_update(self, __m=None, **kwargs): # pragma: nocover
192
177
  """Merge update with another dict"""
@@ -407,40 +392,22 @@ class DataList(list):
407
392
  def copy(self):
408
393
  return DataList((x for x in self), core=self.__meta__.core)
409
394
 
410
- def append(self, v):
411
- super().append(ensure_containers(v, self.__meta__.core))
412
-
413
- def insert(self, i, v):
414
- super().insert(i, ensure_containers(v, self.__meta__.core))
415
-
416
- def extend(self, data):
417
- super().extend(ensure_containers(data, self.__meta__.core))
418
-
419
395
  def __getitem__(self, index):
420
396
  result = super().__getitem__(index)
421
397
  return recursively_evaluate_lazy_format(result, self.__meta__.core)
422
398
 
423
- def __setitem__(self, k, v):
424
- super().__setitem__(k, ensure_containers(v, self.__meta__.core))
425
-
426
- def __add__(self, v):
427
- super().__add__(ensure_containers(v, self.__meta__.core))
428
-
429
- def __iadd__(self, v):
430
- return super().__iadd__(ensure_containers(v, self.__meta__.core))
431
-
432
399
  def __repr__(self):
433
400
  # NOTE: debatable choice: same representation of list
434
401
  return f"{list(self)!r}"
435
402
 
436
- # Box compatibility. Remove in 4.0
437
-
438
403
  def __copy__(self): # pragma: nocover
439
- box_deprecation_warning("__copy__", "DataList")
404
+ # Not part of the plain list API, but needed to propagate core.
405
+ # Consider removing in the future if we can
440
406
  return self.copy()
441
407
 
442
408
  def __deepcopy__(self, memo=None): # pragma: nocover
443
- box_deprecation_warning("__deepcopy__", "DataList")
409
+ # Not part of the plain list API, but needed to propagate core.
410
+ # Consider removing in the future if we can
444
411
  out = self.__class__(core=self.__meta__.core)
445
412
  memo = memo or {}
446
413
  memo[id(self)] = out
@@ -448,6 +415,8 @@ class DataList(list):
448
415
  out.append(copy.deepcopy(k, memo=memo))
449
416
  return out
450
417
 
418
+ # Box compatibility. Remove in 4.0
419
+
451
420
  def to_list(self): # pragma: nocover
452
421
  """
453
422
  Turn the DataList and sub DataLists back into a native python list.
@@ -457,17 +426,8 @@ class DataList(list):
457
426
  box_deprecation_warning(
458
427
  "to_list", "DataList", "Use list(data_list) instead."
459
428
  )
460
- new_list = []
461
- for x in self:
462
- if x is self:
463
- new_list.append(new_list)
464
- elif isinstance(x, DataDict):
465
- new_list.append(x.to_dict())
466
- elif isinstance(x, DataList):
467
- new_list.append(x.to_list())
468
- else:
469
- new_list.append(x)
470
- return new_list
429
+ # to_dict recursively converts to both dicts and lists
430
+ return ut.to_dict(self)
471
431
 
472
432
  def to_json(
473
433
  self,
@@ -800,18 +760,6 @@ def recursively_evaluate_lazy_format(value, settings):
800
760
  return value
801
761
 
802
762
 
803
- def ensure_containers(data, core):
804
- # NOTE: this is to ensure that the nodes nested dict and lists are always
805
- # converted to DataDict and DataList. However, that change is not compatible
806
- # with what we had with DynaBox/BoxList. Leaving here for awareness.
807
- #
808
- # if data.__class__ is dict:
809
- # return DataDict(data, core=core)
810
- # elif data.__class__ is list:
811
- # return DataList(data, core=core)
812
- return data
813
-
814
-
815
763
  def box_deprecation_warning(
816
764
  method_name: str, class_name: str, alternative: Optional[str] = None
817
765
  ):
@@ -0,0 +1,7 @@
1
+ from inspect import get_annotations
2
+ from types import UnionType
3
+
4
+ __all__ = [
5
+ "get_annotations",
6
+ "UnionType",
7
+ ]
@@ -88,7 +88,7 @@ def raise_for_optional_without_none(
88
88
 
89
89
  def raise_for_invalid_class_variable(cls):
90
90
  """typed.Dynaconf only allows typed variables or dynaconf_options attr"""
91
- for name, value in vars(cls).items():
91
+ for name, value in list(vars(cls).items()):
92
92
  if (
93
93
  name.startswith(("_", "dynaconf_options"))
94
94
  or name in cls.__annotations__
@@ -2,11 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  # WARNING: remove the import above when debugging on Python 3.12
4
4
  # otherwise type annotations will be stringified.
5
- import sys
6
5
  from dataclasses import asdict
7
6
  from dataclasses import dataclass
8
7
  from pathlib import Path
9
- from typing import Any
10
8
  from typing import cast
11
9
 
12
10
  from dynaconf.base import LazySettings
@@ -20,10 +18,7 @@ from . import types as ty
20
18
  from . import utils as ut
21
19
  from .compat import get_annotations
22
20
 
23
- if sys.version_info < (3, 10):
24
- dclass_args: dict[str, Any] = {}
25
- else:
26
- dclass_args = {"kw_only": True}
21
+ dclass_args = {"kw_only": True}
27
22
 
28
23
 
29
24
  @dataclass(**dclass_args)
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations # WARNING: remove this when debugging
2
2
 
3
+ from types import NoneType
3
4
  from typing import Annotated
4
5
  from typing import TypeVar
5
6
  from typing import Union
@@ -71,7 +72,6 @@ Str = str
71
72
  Int = int
72
73
  Float = float
73
74
  Bool = bool
74
- NoneType = type(None) # python 3.9 doesn't have types.NoneType
75
75
  List = list
76
76
  Tuple = tuple
77
77
  Dict = dict
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations # WARNING: remove this when debugging
2
2
 
3
+ from collections.abc import Callable
3
4
  from typing import Annotated
4
- from typing import Callable
5
5
  from typing import get_args
6
6
  from typing import get_origin
7
7
  from typing import Union
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations # WARNING: remove this when debugging
2
2
 
3
+ from collections.abc import Callable
3
4
  from collections.abc import Sequence
4
5
  from typing import Any
5
- from typing import Callable
6
6
 
7
7
  from dynaconf.utils.functional import Empty
8
8
  from dynaconf.utils.functional import empty
@@ -141,6 +141,16 @@ def object_merge(
141
141
  full_path=full_path[1:] if full_path else None,
142
142
  list_merge=list_merge,
143
143
  )
144
+
145
+ # Restore old key order: keys that exist in old keep old's
146
+ # relative order; keys that are new-only come last.
147
+ old_keys = list(safe_items(old))
148
+ old_key_set = {k for k, _ in old_keys}
149
+ new_only = [k for k in new if k not in old_key_set]
150
+ ordered = [k for k, _ in old_keys if k in new] + new_only
151
+ for k in ordered:
152
+ new[k] = new.pop(k)
153
+
144
154
  handle_metavalues(old, new, list_merge=list_merge)
145
155
 
146
156
  return new
@@ -227,7 +237,7 @@ def handle_metavalues(
227
237
  value.remove("dynaconf_merge_unique")
228
238
  unique = True
229
239
 
230
- for item in old.get(key)[::-1]:
240
+ for item in (old.get(key) or [])[::-1]:
231
241
  if unique and item in value:
232
242
  continue
233
243
  value.insert(0, item)
@@ -624,6 +634,15 @@ def prepare_json(data: Any) -> Any:
624
634
  return data
625
635
 
626
636
 
637
+ def to_dict(obj: Any) -> Any:
638
+ """Recursively convert dict/list subclasses to plain Python types."""
639
+ if isinstance(obj, dict):
640
+ return {k: to_dict(obj[k]) for k in obj}
641
+ elif isinstance(obj, list):
642
+ return [to_dict(x) for x in obj]
643
+ return obj
644
+
645
+
627
646
  def container_items(container: dict | list):
628
647
  if isinstance(container, dict):
629
648
  return container.items()
@@ -137,10 +137,11 @@ def glob(
137
137
  ):
138
138
  """Redefined std glob assuming some defaults.
139
139
  and fallback for diffente python versions."""
140
- glob_args = {"recursive": recursive}
141
- if sys.version_info >= (3, 10):
142
- glob_args["root_dir"] = root_dir
143
- glob_args["dir_fd"] = dir_fd
140
+ glob_args = {
141
+ "recursive": recursive,
142
+ "root_dir": root_dir,
143
+ "dir_fd": dir_fd,
144
+ }
144
145
  if sys.version_info >= (3, 11):
145
146
  glob_args["include_hidden"] = include_hidden
146
147
  return python_glob(pathname, **glob_args)
@@ -4,13 +4,13 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import sys
7
+ from collections.abc import Callable
7
8
  from contextlib import suppress
8
9
  from functools import partial
9
10
  from importlib.metadata import PackageNotFoundError
10
11
  from importlib.metadata import version
11
12
  from pathlib import PosixPath
12
13
  from typing import Any
13
- from typing import Callable
14
14
  from typing import Literal
15
15
  from typing import Protocol
16
16
  from typing import TextIO
@@ -761,7 +761,7 @@ def _parse_conf_data(data, tomlfy=False, box_settings=None):
761
761
  castenabled
762
762
  and data
763
763
  and isinstance(data, str)
764
- and data.startswith(tuple(converters.keys()))
764
+ and data.partition(" ")[0] in converters
765
765
  ):
766
766
  # Check combination token is used
767
767
  comb_token = re.match(
@@ -1,13 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections import defaultdict
4
+ from collections.abc import Callable
4
5
  from collections.abc import Sequence
5
6
  from contextlib import suppress
6
7
  from copy import deepcopy
7
8
  from itertools import chain
8
9
  from types import MappingProxyType
9
10
  from typing import Any
10
- from typing import Callable
11
11
  from typing import get_args
12
12
  from typing import TYPE_CHECKING
13
13
 
@@ -6,17 +6,11 @@ Implement basic assertions to be used in assertion action
6
6
  from __future__ import annotations
7
7
 
8
8
  import re
9
+ from types import UnionType
9
10
  from typing import get_args
10
11
  from typing import get_origin
11
12
  from typing import Union
12
13
 
13
- # NOTE: Remove when dropping 3.9
14
- try:
15
- from types import UnionType # type: ignore
16
- except ImportError:
17
- UnionType = Union # type: ignore
18
- # /NOTE
19
-
20
14
 
21
15
  def eq(value, other) -> bool:
22
16
  """Equals"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dynaconf
3
- Version: 3.3.0
3
+ Version: 3.3.2
4
4
  Summary: The dynamic configurator for your Python Project
5
5
  Author-email: Bruno Rocha <rochacbruno@gmail.com>
6
6
  License: MIT
@@ -16,15 +16,15 @@ Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python
17
17
  Classifier: Programming Language :: Python :: 3
18
18
  Classifier: Programming Language :: Python :: 3 :: Only
19
- Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
23
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Topic :: Utilities
25
25
  Classifier: Topic :: Software Development :: Libraries
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
- Requires-Python: <3.14,>=3.9
27
+ Requires-Python: <3.15,>=3.10
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
30
  License-File: vendor_licenses/box-LICENSE.txt
@@ -11,7 +11,7 @@ authors = [
11
11
  {name = "Bruno Rocha", email = "rochacbruno@gmail.com"},
12
12
  ]
13
13
  license = {text = "MIT"}
14
- requires-python = ">=3.9,<3.14"
14
+ requires-python = ">=3.10,<3.15"
15
15
  classifiers = [
16
16
  "Development Status :: 5 - Production/Stable",
17
17
  "Framework :: Django",
@@ -23,11 +23,11 @@ classifiers = [
23
23
  "Programming Language :: Python",
24
24
  "Programming Language :: Python :: 3",
25
25
  "Programming Language :: Python :: 3 :: Only",
26
- "Programming Language :: Python :: 3.9",
27
26
  "Programming Language :: Python :: 3.10",
28
27
  "Programming Language :: Python :: 3.11",
29
28
  "Programming Language :: Python :: 3.12",
30
29
  "Programming Language :: Python :: 3.13",
30
+ "Programming Language :: Python :: 3.14",
31
31
  "Topic :: Utilities",
32
32
  "Topic :: Software Development :: Libraries",
33
33
  "Topic :: Software Development :: Libraries :: Python Modules",
@@ -101,7 +101,7 @@ ba = "ba"
101
101
  # ====
102
102
 
103
103
  [tool.ruff]
104
- target-version = "py39"
104
+ target-version = "py310"
105
105
  line-length = 79
106
106
  exclude = ["dynaconf/vendor*"]
107
107
 
@@ -170,7 +170,6 @@ omit = [
170
170
  # ======
171
171
 
172
172
  [tool.pytest.ini_options]
173
- pythonpath = [".github/scripts"]
174
173
  markers = [
175
174
  "integration: marks tests as integration (deselect with '-m \"not integration\"')",
176
175
  ]
@@ -209,7 +208,7 @@ ignore_missing_imports = true
209
208
  # ===========
210
209
 
211
210
  [tool.bumpversion]
212
- current_version = "3.3.0"
211
+ current_version = "3.3.2"
213
212
  parse = """(?x)
214
213
  (?P<major>0|[1-9]\\d*)\\.
215
214
  (?P<minor>0|[1-9]\\d*)\\.
@@ -256,7 +255,7 @@ optional_value = "final"
256
255
  # ===
257
256
 
258
257
  [tool.tox]
259
- envlist = ["py39", "py310", "py311", "py312", "py313"]
258
+ envlist = ["py310", "py311", "py312", "py313"]
260
259
  whitelist_externals = ["make"]
261
260
 
262
261
  [tool.tox.testenv]
@@ -311,6 +310,7 @@ lint = [
311
310
  "typos>=1.42.3",
312
311
  ]
313
312
  test = [
313
+ "dynaconf-release-utility",
314
314
  "boto3>=1.40.67",
315
315
  "commentjson>=0.9.0",
316
316
  "configobj>=5.0.9",
@@ -342,3 +342,6 @@ release = [
342
342
  "toml>=0.10.2",
343
343
  "twine>=6.2.0",
344
344
  ]
345
+
346
+ [tool.uv.sources]
347
+ dynaconf-release-utility = { path = ".github/scripts/", editable = true }