dycw-utilities 0.166.16__tar.gz → 0.166.18__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 (219) hide show
  1. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/PKG-INFO +3 -3
  2. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/pyproject.toml +6 -6
  3. dycw_utilities-0.166.18/src/tests/test_pydantic_settings.py +174 -0
  4. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pydantic_settings_sops.py +3 -2
  5. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/__init__.py +1 -1
  6. dycw_utilities-0.166.18/src/utilities/pydantic_settings.py +180 -0
  7. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pydantic_settings_sops.py +4 -5
  8. dycw_utilities-0.166.16/src/tests/test_pydantic_settings.py +0 -99
  9. dycw_utilities-0.166.16/src/utilities/pydantic_settings.py +0 -69
  10. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/.gitignore +0 -0
  11. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/LICENSE +0 -0
  12. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/README.md +0 -0
  13. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/__init__.py +0 -0
  14. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/conftest.py +0 -0
  15. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/__init__.py +0 -0
  16. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_missing/__init__.py +0 -0
  17. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_missing/module.py +0 -0
  18. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/__init__.py +0 -0
  19. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/outer_1.py +0 -0
  20. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/outer_2.py +0 -0
  21. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  22. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  23. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  24. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  25. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_without/__init__.py +0 -0
  26. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_without/module_1.py +0 -0
  27. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/package_without/module_2.py +0 -0
  28. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/standalone.py +0 -0
  29. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/modules/with_imports.py +0 -0
  30. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  31. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  32. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  33. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  34. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  35. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  36. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  37. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  38. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_aeventkit.py +0 -0
  39. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_altair.py +0 -0
  40. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_asyncio.py +0 -0
  41. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_atomicwrites.py +0 -0
  42. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_atools.py +0 -0
  43. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_cachetools.py +0 -0
  44. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_click.py +0 -0
  45. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_concurrent.py +0 -0
  46. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_contextlib.py +0 -0
  47. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_contextvars.py +0 -0
  48. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_cryptography.py +0 -0
  49. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_cvxpy.py +0 -0
  50. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_dataclasses.py +0 -0
  51. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_enum.py +0 -0
  52. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_errors.py +0 -0
  53. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_fastapi.py +0 -0
  54. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_fpdf2.py +0 -0
  55. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_functions.py +0 -0
  56. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_functools.py +0 -0
  57. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_getpass.py +0 -0
  58. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_git.py +0 -0
  59. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_gzip.py +0 -0
  60. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_hashlib.py +0 -0
  61. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_http.py +0 -0
  62. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_hypothesis.py +0 -0
  63. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_importlib.py +0 -0
  64. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_inflect.py +0 -0
  65. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_ipython.py +0 -0
  66. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_iterables.py +0 -0
  67. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_json.py +0 -0
  68. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_jupyter.py +0 -0
  69. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_libcst.py +0 -0
  70. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_lightweight_charts.py +0 -0
  71. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_logging.py +0 -0
  72. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_math.py +0 -0
  73. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_memory_profiler.py +0 -0
  74. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_modules.py +0 -0
  75. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_more_itertools.py +0 -0
  76. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_numpy.py +0 -0
  77. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_objects/__init__.py +0 -0
  78. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_objects/objects.py +0 -0
  79. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_operator.py +0 -0
  80. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_optuna.py +0 -0
  81. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_orjson.py +0 -0
  82. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_os.py +0 -0
  83. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_parse.py +0 -0
  84. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pathlib.py +0 -0
  85. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pickle.py +0 -0
  86. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_platform.py +0 -0
  87. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_polars.py +0 -0
  88. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_polars_ols.py +0 -0
  89. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_postgres.py +0 -0
  90. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pottery.py +0 -0
  91. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pqdm.py +0 -0
  92. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_psutil.py +0 -0
  93. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pyinstrument.py +0 -0
  94. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pytest.py +0 -0
  95. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pytest_randomly.py +0 -0
  96. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_pytest_regressions.py +0 -0
  97. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_random.py +0 -0
  98. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_re.py +0 -0
  99. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_redis.py +0 -0
  100. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_reprlib.py +0 -0
  101. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_scipy.py +0 -0
  102. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_sentinel.py +0 -0
  103. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_shelve.py +0 -0
  104. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_slack_sdk.py +0 -0
  105. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_socket.py +0 -0
  106. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_sqlalchemy.py +0 -0
  107. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_sqlalchemy_polars.py +0 -0
  108. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_statsmodels.py +0 -0
  109. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_string.py +0 -0
  110. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_tempfile.py +0 -0
  111. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_testbook.py +0 -0
  112. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_text.py +0 -0
  113. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_threading.py +0 -0
  114. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_timer.py +0 -0
  115. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_traceback.py +0 -0
  116. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_typed_settings.py +0 -0
  117. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_types.py +0 -0
  118. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_typing.py +0 -0
  119. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_typing_funcs/__init__.py +0 -0
  120. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_typing_funcs/no_future.py +0 -0
  121. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_typing_funcs/with_future.py +0 -0
  122. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_tzdata.py +0 -0
  123. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_tzlocal.py +0 -0
  124. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_uuid.py +0 -0
  125. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_version.py +0 -0
  126. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_warnings.py +0 -0
  127. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_whenever.py +0 -0
  128. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_zipfile.py +0 -0
  129. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/tests/test_zoneinfo.py +0 -0
  130. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/aeventkit.py +0 -0
  131. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/altair.py +0 -0
  132. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/asyncio.py +0 -0
  133. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/atomicwrites.py +0 -0
  134. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/atools.py +0 -0
  135. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/cachetools.py +0 -0
  136. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/click.py +0 -0
  137. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/concurrent.py +0 -0
  138. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/contextlib.py +0 -0
  139. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/contextvars.py +0 -0
  140. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/cryptography.py +0 -0
  141. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/cvxpy.py +0 -0
  142. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/dataclasses.py +0 -0
  143. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/enum.py +0 -0
  144. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/errors.py +0 -0
  145. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/fastapi.py +0 -0
  146. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/fpdf2.py +0 -0
  147. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/functions.py +0 -0
  148. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/functools.py +0 -0
  149. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/getpass.py +0 -0
  150. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/git.py +0 -0
  151. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/gzip.py +0 -0
  152. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/hashlib.py +0 -0
  153. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/http.py +0 -0
  154. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/hypothesis.py +0 -0
  155. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/importlib.py +0 -0
  156. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/inflect.py +0 -0
  157. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/ipython.py +0 -0
  158. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/iterables.py +0 -0
  159. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/json.py +0 -0
  160. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/jupyter.py +0 -0
  161. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/libcst.py +0 -0
  162. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/lightweight_charts.py +0 -0
  163. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/logging.py +0 -0
  164. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/math.py +0 -0
  165. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/memory_profiler.py +0 -0
  166. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/modules.py +0 -0
  167. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/more_itertools.py +0 -0
  168. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/numpy.py +0 -0
  169. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/operator.py +0 -0
  170. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/optuna.py +0 -0
  171. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/orjson.py +0 -0
  172. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/os.py +0 -0
  173. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/parse.py +0 -0
  174. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pathlib.py +0 -0
  175. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pickle.py +0 -0
  176. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/platform.py +0 -0
  177. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/polars.py +0 -0
  178. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/polars_ols.py +0 -0
  179. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/postgres.py +0 -0
  180. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pottery.py +0 -0
  181. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pqdm.py +0 -0
  182. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/psutil.py +0 -0
  183. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/py.typed +0 -0
  184. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pyinstrument.py +0 -0
  185. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pytest.py +0 -0
  186. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pytest_plugins/__init__.py +0 -0
  187. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  188. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  189. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/pytest_regressions.py +0 -0
  190. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/random.py +0 -0
  191. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/re.py +0 -0
  192. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/redis.py +0 -0
  193. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/reprlib.py +0 -0
  194. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/scipy.py +0 -0
  195. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/sentinel.py +0 -0
  196. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/shelve.py +0 -0
  197. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/slack_sdk.py +0 -0
  198. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/socket.py +0 -0
  199. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/sqlalchemy.py +0 -0
  200. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/sqlalchemy_polars.py +0 -0
  201. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/statsmodels.py +0 -0
  202. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/string.py +0 -0
  203. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/tempfile.py +0 -0
  204. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/testbook.py +0 -0
  205. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/text.py +0 -0
  206. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/threading.py +0 -0
  207. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/timer.py +0 -0
  208. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/traceback.py +0 -0
  209. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/typed_settings.py +0 -0
  210. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/types.py +0 -0
  211. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/typing.py +0 -0
  212. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/tzdata.py +0 -0
  213. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/tzlocal.py +0 -0
  214. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/uuid.py +0 -0
  215. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/version.py +0 -0
  216. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/warnings.py +0 -0
  217. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/whenever.py +0 -0
  218. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/zipfile.py +0 -0
  219. {dycw_utilities-0.166.16 → dycw_utilities-0.166.18}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.166.16
3
+ Version: 0.166.18
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -18,13 +18,13 @@ Requires-Dist: pytest-cov<6.3,>=6.2.1; extra == 'test'
18
18
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
19
19
  Requires-Dist: pytest-lazy-fixtures<1.4,>=1.3.4; extra == 'test'
20
20
  Requires-Dist: pytest-randomly<3.17,>=3.16.0; extra == 'test'
21
- Requires-Dist: pytest-regressions<2.9,>=2.8.2; extra == 'test'
21
+ Requires-Dist: pytest-regressions<2.9,>=2.8.3; extra == 'test'
22
22
  Requires-Dist: pytest-repeat<0.10,>=0.9.4; extra == 'test'
23
23
  Requires-Dist: pytest-rerunfailures<16.1,>=16.0.1; extra == 'test'
24
24
  Requires-Dist: pytest-rng<1.1,>=1.0.0; extra == 'test'
25
25
  Requires-Dist: pytest-timeout<2.5,>=2.4.0; extra == 'test'
26
26
  Requires-Dist: pytest-xdist<3.9,>=3.8.0; extra == 'test'
27
- Requires-Dist: pytest<8.5,>=8.4.1; extra == 'test'
27
+ Requires-Dist: pytest<8.5,>=8.4.2; extra == 'test'
28
28
  Requires-Dist: testbook<0.5,>=0.4.2; extra == 'test'
29
29
  Description-Content-Type: text/markdown
30
30
 
@@ -160,14 +160,14 @@ pyinstrument = [
160
160
  "pyinstrument >=5.1.0, <5.2",
161
161
  ]
162
162
  pytest = [
163
- "pytest >=8.4.1, <8.5",
163
+ "pytest >=8.4.2, <8.5",
164
164
  "pytest-asyncio >=1.1.0, <1.2",
165
165
  "pytest-randomly >=3.16.0, <3.17",
166
166
  "pytest-timeout >=2.4.0, <2.5",
167
167
  "pytest-xdist >=3.8.0, <3.9",
168
168
  ]
169
169
  pytest-regressions = [
170
- "pytest-regressions >=2.8.2, <2.9",
170
+ "pytest-regressions >=2.8.3, <2.9",
171
171
  ]
172
172
  pytest-regressions-test = [
173
173
  "orjson",
@@ -246,7 +246,7 @@ dependencies = [
246
246
  name = "dycw-utilities"
247
247
  readme = "README.md"
248
248
  requires-python = ">= 3.12"
249
- version = "0.166.16"
249
+ version = "0.166.18"
250
250
 
251
251
  [project.entry-points.pytest11]
252
252
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -259,13 +259,13 @@ logging = [
259
259
  test = [
260
260
  "dycw-pytest-only >=2.1.1, <2.2",
261
261
  "hypothesis >=6.138.13, <6.139",
262
- "pytest >=8.4.1, <8.5",
262
+ "pytest >=8.4.2, <8.5",
263
263
  "pytest-asyncio >=1.1.0, <1.2",
264
264
  "pytest-cov >=6.2.1, <6.3",
265
265
  "pytest-instafail >=0.5.0, <0.6",
266
266
  "pytest-lazy-fixtures >=1.3.4, <1.4",
267
267
  "pytest-randomly >=3.16.0, <3.17",
268
- "pytest-regressions >=2.8.2, <2.9",
268
+ "pytest-regressions >=2.8.3, <2.9",
269
269
  "pytest-repeat >=0.9.4, <0.10",
270
270
  "pytest-rerunfailures >=16.0.1, <16.1",
271
271
  "pytest-rng >=1.0.0, <1.1",
@@ -279,7 +279,7 @@ test = [
279
279
  # bump-my-version
280
280
  [tool.bumpversion]
281
281
  allow_dirty = true
282
- current_version = "0.166.16"
282
+ current_version = "0.166.18"
283
283
 
284
284
  [[tool.bumpversion.files]]
285
285
  filename = "src/utilities/__init__.py"
@@ -0,0 +1,174 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, ClassVar
5
+
6
+ import tomlkit
7
+ import yaml
8
+ from pydantic_settings import BaseSettings, SettingsConfigDict
9
+
10
+ from utilities.os import temp_environ
11
+ from utilities.pydantic_settings import (
12
+ CustomBaseSettings,
13
+ PathLikeOrWithSection,
14
+ load_settings,
15
+ )
16
+
17
+ if TYPE_CHECKING:
18
+ from collections.abc import Sequence
19
+ from pathlib import Path
20
+
21
+
22
+ class TestCustomBaseSettings:
23
+ def test_json(self, *, tmp_path: Path) -> None:
24
+ file = tmp_path.joinpath("settings.json")
25
+ _ = file.write_text(json.dumps({"x": 1}))
26
+
27
+ class Settings(CustomBaseSettings):
28
+ json_files: ClassVar[Sequence[PathLikeOrWithSection]] = [file]
29
+ x: int
30
+
31
+ settings = load_settings(Settings)
32
+ assert settings.x == 1
33
+
34
+ def test_json_section_str(self, *, tmp_path: Path) -> None:
35
+ file = tmp_path.joinpath("settings.json")
36
+ _ = file.write_text(json.dumps({"outer": {"x": 1}}))
37
+
38
+ class Settings(CustomBaseSettings):
39
+ json_files: ClassVar[Sequence[PathLikeOrWithSection]] = [(file, "outer")]
40
+ x: int
41
+
42
+ settings = load_settings(Settings)
43
+ assert settings.x == 1
44
+
45
+ def test_json_section_nested(self, *, tmp_path: Path) -> None:
46
+ file = tmp_path.joinpath("settings.json")
47
+ _ = file.write_text(json.dumps({"outer": {"middle": {"x": 1}}}))
48
+
49
+ class Settings(CustomBaseSettings):
50
+ json_files: ClassVar[Sequence[PathLikeOrWithSection]] = [
51
+ (file, ["outer", "middle"])
52
+ ]
53
+ x: int
54
+
55
+ settings = load_settings(Settings)
56
+ assert settings.x == 1
57
+
58
+ def test_toml(self, *, tmp_path: Path) -> None:
59
+ file = tmp_path.joinpath("settings.toml")
60
+ _ = file.write_text(tomlkit.dumps({"x": 1}))
61
+
62
+ class Settings(CustomBaseSettings):
63
+ toml_files: ClassVar[Sequence[PathLikeOrWithSection]] = [file]
64
+ x: int
65
+
66
+ settings = load_settings(Settings)
67
+ assert settings.x == 1
68
+
69
+ def test_toml_section_str(self, *, tmp_path: Path) -> None:
70
+ file = tmp_path.joinpath("settings.toml")
71
+ _ = file.write_text(tomlkit.dumps({"outer": {"x": 1}}))
72
+
73
+ class Settings(CustomBaseSettings):
74
+ toml_files: ClassVar[Sequence[PathLikeOrWithSection]] = [(file, "outer")]
75
+ x: int
76
+
77
+ settings = load_settings(Settings)
78
+ assert settings.x == 1
79
+
80
+ def test_toml_section_nested(self, *, tmp_path: Path) -> None:
81
+ file = tmp_path.joinpath("settings.toml")
82
+ _ = file.write_text(tomlkit.dumps({"outer": {"middle": {"x": 1}}}))
83
+
84
+ class Settings(CustomBaseSettings):
85
+ toml_files: ClassVar[Sequence[PathLikeOrWithSection]] = [
86
+ (file, ["outer", "middle"])
87
+ ]
88
+ x: int
89
+
90
+ settings = load_settings(Settings)
91
+ assert settings.x == 1
92
+
93
+ def test_yaml(self, *, tmp_path: Path) -> None:
94
+ file = tmp_path.joinpath("settings.yaml")
95
+ _ = file.write_text(yaml.dump({"x": 1}))
96
+
97
+ class Settings(CustomBaseSettings):
98
+ yaml_files: ClassVar[Sequence[PathLikeOrWithSection]] = [file]
99
+ x: int
100
+
101
+ settings = load_settings(Settings)
102
+ assert settings.x == 1
103
+
104
+ def test_yaml_section_str(self, *, tmp_path: Path) -> None:
105
+ file = tmp_path.joinpath("settings.yaml")
106
+ _ = file.write_text(yaml.dump({"outer": {"x": 1}}))
107
+
108
+ class Settings(CustomBaseSettings):
109
+ yaml_files: ClassVar[Sequence[PathLikeOrWithSection]] = [(file, "outer")]
110
+ x: int
111
+
112
+ settings = load_settings(Settings)
113
+ assert settings.x == 1
114
+
115
+ def test_yaml_section_nested(self, *, tmp_path: Path) -> None:
116
+ file = tmp_path.joinpath("settings.yaml")
117
+ _ = file.write_text(yaml.dump({"outer": {"middle": {"x": 1}}}))
118
+
119
+ class Settings(CustomBaseSettings):
120
+ yaml_files: ClassVar[Sequence[PathLikeOrWithSection]] = [
121
+ (file, ["outer", "middle"])
122
+ ]
123
+ x: int
124
+
125
+ settings = load_settings(Settings)
126
+ assert settings.x == 1
127
+
128
+ def test_env_var(self) -> None:
129
+ class Settings(CustomBaseSettings):
130
+ x: int
131
+
132
+ with temp_environ(x="1"):
133
+ settings = load_settings(Settings)
134
+ assert settings.x == 1
135
+
136
+ def test_env_var_with_prefix(self) -> None:
137
+ class Settings(CustomBaseSettings):
138
+ model_config = SettingsConfigDict(env_prefix="test_")
139
+ x: int
140
+
141
+ with temp_environ(test_x="1"):
142
+ settings = load_settings(Settings)
143
+ assert settings.x == 1
144
+
145
+ def test_env_var_with_nested(self) -> None:
146
+ class Settings(CustomBaseSettings):
147
+ inner: Inner
148
+
149
+ class Inner(BaseSettings):
150
+ x: int
151
+
152
+ _ = Settings.model_rebuild()
153
+
154
+ with temp_environ(inner__x="1"):
155
+ settings = load_settings(Settings)
156
+ assert settings.inner.x == 1
157
+
158
+ def test_env_var_with_prefix_and_nested(self) -> None:
159
+ class Settings(CustomBaseSettings):
160
+ model_config = SettingsConfigDict(env_prefix="test__")
161
+ inner: Inner
162
+
163
+ class Inner(BaseSettings):
164
+ x: int
165
+
166
+ _ = Settings.model_rebuild()
167
+ with temp_environ(test__inner__x="1"):
168
+ settings = load_settings(Settings)
169
+ assert settings.inner.x == 1
170
+
171
+ def test_no_files(self) -> None:
172
+ class Settings(CustomBaseSettings): ...
173
+
174
+ _ = load_settings(Settings)
@@ -13,9 +13,10 @@ from utilities.pydantic_settings_sops import SopsBaseSettings
13
13
  from utilities.re import extract_group
14
14
 
15
15
  if TYPE_CHECKING:
16
+ from collections.abc import Sequence
16
17
  from pathlib import Path
17
18
 
18
- from utilities.types import MaybeIterable, PathLike
19
+ from utilities.types import PathLike
19
20
 
20
21
 
21
22
  class TestSOPSBaseSettings:
@@ -39,7 +40,7 @@ class TestSOPSBaseSettings:
39
40
  _ = check_call(["sops", "encrypt", str(unencrypted_file)], stdout=file)
40
41
 
41
42
  class Settings(SopsBaseSettings):
42
- secret_files: ClassVar[MaybeIterable[PathLike]] = encrypted_file
43
+ secret_files: ClassVar[Sequence[PathLike]] = [encrypted_file]
43
44
  x: int
44
45
  y: int
45
46
 
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.166.16"
3
+ __version__ = "0.166.18"
@@ -0,0 +1,180 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import reduce
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING, Any, ClassVar, assert_never, override
6
+
7
+ from pydantic_settings import (
8
+ BaseSettings,
9
+ JsonConfigSettingsSource,
10
+ PydanticBaseSettingsSource,
11
+ SettingsConfigDict,
12
+ TomlConfigSettingsSource,
13
+ YamlConfigSettingsSource,
14
+ )
15
+ from pydantic_settings.sources import DEFAULT_PATH
16
+
17
+ from utilities.iterables import always_iterable
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Iterator, Sequence
21
+
22
+ from pydantic_settings.sources import PathType
23
+
24
+ from utilities.types import MaybeSequenceStr, PathLike
25
+
26
+
27
+ type PathLikeOrWithSection = PathLike | tuple[PathLike, MaybeSequenceStr]
28
+
29
+
30
+ class CustomBaseSettings(BaseSettings):
31
+ """Base settings for loading JSON files."""
32
+
33
+ # paths
34
+ json_files: ClassVar[Sequence[PathLikeOrWithSection]] = []
35
+ toml_files: ClassVar[Sequence[PathLikeOrWithSection]] = []
36
+ yaml_files: ClassVar[Sequence[PathLikeOrWithSection]] = []
37
+
38
+ # config
39
+ model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
40
+ env_nested_delimiter="__"
41
+ )
42
+
43
+ @classmethod
44
+ @override
45
+ def settings_customise_sources(
46
+ cls,
47
+ settings_cls: type[BaseSettings],
48
+ init_settings: PydanticBaseSettingsSource,
49
+ env_settings: PydanticBaseSettingsSource,
50
+ dotenv_settings: PydanticBaseSettingsSource,
51
+ file_secret_settings: PydanticBaseSettingsSource,
52
+ ) -> tuple[PydanticBaseSettingsSource, ...]:
53
+ _ = (init_settings, dotenv_settings, file_secret_settings)
54
+ return tuple(cls._yield_base_settings_sources(settings_cls, env_settings))
55
+
56
+ @classmethod
57
+ def _yield_base_settings_sources(
58
+ cls,
59
+ settings_cls: type[BaseSettings],
60
+ env_settings: PydanticBaseSettingsSource,
61
+ /,
62
+ ) -> Iterator[PydanticBaseSettingsSource]:
63
+ yield env_settings
64
+ for json in cls.json_files:
65
+ match json:
66
+ case Path() | str():
67
+ yield JsonConfigSettingsSource(settings_cls, json_file=json)
68
+ case Path() | str() as file, str() | list() | tuple() as section:
69
+ yield JsonConfigSectionSettingsSource(
70
+ settings_cls, json_file=file, section=section
71
+ )
72
+ case never:
73
+ assert_never(never)
74
+ for toml in cls.toml_files:
75
+ match toml:
76
+ case Path() | str():
77
+ yield TomlConfigSettingsSource(settings_cls, toml_file=toml)
78
+ case Path() | str() as file, str() | list() | tuple() as section:
79
+ yield TomlConfigSectionSettingsSource(
80
+ settings_cls, toml_file=file, section=section
81
+ )
82
+ case never:
83
+ assert_never(never)
84
+ for yaml in cls.yaml_files:
85
+ match yaml:
86
+ case Path() | str():
87
+ yield YamlConfigSettingsSource(settings_cls, yaml_file=yaml)
88
+ case Path() | str() as file, str() | list() | tuple() as section:
89
+ yield YamlConfigSectionSettingsSource(
90
+ settings_cls, yaml_file=file, section=section
91
+ )
92
+ case never:
93
+ assert_never(never)
94
+
95
+
96
+ def load_settings[T: BaseSettings](cls: type[T], /) -> T:
97
+ """Load a set of settings."""
98
+ return cls()
99
+
100
+
101
+ class JsonConfigSectionSettingsSource(JsonConfigSettingsSource):
102
+ @override
103
+ def __init__(
104
+ self,
105
+ settings_cls: type[BaseSettings],
106
+ json_file: PathType | None = DEFAULT_PATH,
107
+ json_file_encoding: str | None = None,
108
+ *,
109
+ section: MaybeSequenceStr,
110
+ ) -> None:
111
+ super().__init__(
112
+ settings_cls, json_file=json_file, json_file_encoding=json_file_encoding
113
+ )
114
+ self.section = section
115
+
116
+ @override
117
+ def __call__(self) -> dict[str, Any]:
118
+ return reduce(
119
+ lambda acc, el: acc.get(el, {}),
120
+ always_iterable(self.section),
121
+ super().__call__(),
122
+ )
123
+
124
+
125
+ class TomlConfigSectionSettingsSource(TomlConfigSettingsSource):
126
+ @override
127
+ def __init__(
128
+ self,
129
+ settings_cls: type[BaseSettings],
130
+ toml_file: PathType | None = DEFAULT_PATH,
131
+ *,
132
+ section: MaybeSequenceStr,
133
+ ) -> None:
134
+ super().__init__(settings_cls, toml_file=toml_file)
135
+ self.section = section
136
+
137
+ @override
138
+ def __call__(self) -> dict[str, Any]:
139
+ return reduce(
140
+ lambda acc, el: acc.get(el, {}),
141
+ always_iterable(self.section),
142
+ super().__call__(),
143
+ )
144
+
145
+
146
+ class YamlConfigSectionSettingsSource(YamlConfigSettingsSource):
147
+ @override
148
+ def __init__(
149
+ self,
150
+ settings_cls: type[BaseSettings],
151
+ yaml_file: PathType | None = DEFAULT_PATH,
152
+ yaml_file_encoding: str | None = None,
153
+ yaml_config_section: str | None = None,
154
+ *,
155
+ section: MaybeSequenceStr,
156
+ ) -> None:
157
+ super().__init__(
158
+ settings_cls,
159
+ yaml_file=yaml_file,
160
+ yaml_file_encoding=yaml_file_encoding,
161
+ yaml_config_section=yaml_config_section,
162
+ )
163
+ self.section = section
164
+
165
+ @override
166
+ def __call__(self) -> dict[str, Any]:
167
+ return reduce(
168
+ lambda acc, el: acc.get(el, {}),
169
+ always_iterable(self.section),
170
+ super().__call__(),
171
+ )
172
+
173
+
174
+ __all__ = [
175
+ "CustomBaseSettings",
176
+ "JsonConfigSectionSettingsSource",
177
+ "TomlConfigSectionSettingsSource",
178
+ "YamlConfigSectionSettingsSource",
179
+ "load_settings",
180
+ ]
@@ -4,22 +4,21 @@ from typing import TYPE_CHECKING, ClassVar, override
4
4
 
5
5
  from pydantic_settings_sops import SOPSConfigSettingsSource
6
6
 
7
- from utilities.iterables import always_iterable
8
7
  from utilities.pydantic_settings import CustomBaseSettings
9
8
 
10
9
  if TYPE_CHECKING:
11
- from collections.abc import Iterator
10
+ from collections.abc import Iterator, Sequence
12
11
 
13
12
  from pydantic_settings import BaseSettings, PydanticBaseSettingsSource
14
13
 
15
- from utilities.types import MaybeIterable, PathLike
14
+ from utilities.types import PathLike
16
15
 
17
16
 
18
17
  class SopsBaseSettings(CustomBaseSettings):
19
18
  """Base settings for loading secrets using `sops/age`."""
20
19
 
21
20
  # paths
22
- secret_files: ClassVar[MaybeIterable[PathLike]] = ()
21
+ secret_files: ClassVar[Sequence[PathLike]] = ()
23
22
 
24
23
  @classmethod
25
24
  @override
@@ -30,7 +29,7 @@ class SopsBaseSettings(CustomBaseSettings):
30
29
  /,
31
30
  ) -> Iterator[PydanticBaseSettingsSource]:
32
31
  yield from super()._yield_base_settings_sources(settings_cls, env_settings)
33
- for file in always_iterable(cls.secret_files):
32
+ for file in cls.secret_files:
34
33
  yield SOPSConfigSettingsSource(
35
34
  settings_cls, # pyright: ignore[reportArgumentType],
36
35
  json_file=file,
@@ -1,99 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- from typing import TYPE_CHECKING, ClassVar
5
-
6
- import tomlkit
7
- import yaml
8
- from pydantic_settings import BaseSettings, SettingsConfigDict
9
-
10
- from utilities.os import temp_environ
11
- from utilities.pydantic_settings import CustomBaseSettings, load_settings
12
-
13
- if TYPE_CHECKING:
14
- from pathlib import Path
15
-
16
- from utilities.types import MaybeIterable, PathLike
17
-
18
-
19
- class TestCustomBaseSettings:
20
- def test_json(self, *, tmp_path: Path) -> None:
21
- file = tmp_path.joinpath("settings.json")
22
- _ = file.write_text(json.dumps({"x": 1}))
23
-
24
- class Settings(CustomBaseSettings):
25
- json_files: ClassVar[MaybeIterable[PathLike]] = file
26
- x: int
27
-
28
- settings = load_settings(Settings)
29
- assert settings.x == 1
30
-
31
- def test_toml(self, *, tmp_path: Path) -> None:
32
- file = tmp_path.joinpath("settings.toml")
33
- _ = file.write_text(tomlkit.dumps({"x": 1}))
34
-
35
- class Settings(CustomBaseSettings):
36
- toml_files: ClassVar[MaybeIterable[PathLike]] = file
37
- x: int
38
-
39
- settings = load_settings(Settings)
40
- assert settings.x == 1
41
-
42
- def test_yaml(self, *, tmp_path: Path) -> None:
43
- file = tmp_path.joinpath("settings.yaml")
44
- _ = file.write_text(yaml.dump({"x": 1}))
45
-
46
- class Settings(CustomBaseSettings):
47
- yaml_files: ClassVar[MaybeIterable[PathLike]] = file
48
- x: int
49
-
50
- settings = load_settings(Settings)
51
- assert settings.x == 1
52
-
53
- def test_env_var(self) -> None:
54
- class Settings(CustomBaseSettings):
55
- x: int
56
-
57
- with temp_environ(x="1"):
58
- settings = load_settings(Settings)
59
- assert settings.x == 1
60
-
61
- def test_env_var_with_prefix(self) -> None:
62
- class Settings(CustomBaseSettings):
63
- model_config = SettingsConfigDict(env_prefix="test_")
64
- x: int
65
-
66
- with temp_environ(test_x="1"):
67
- settings = load_settings(Settings)
68
- assert settings.x == 1
69
-
70
- def test_env_var_with_nested(self) -> None:
71
- class Settings(CustomBaseSettings):
72
- inner: Inner
73
-
74
- class Inner(BaseSettings):
75
- x: int
76
-
77
- _ = Settings.model_rebuild()
78
-
79
- with temp_environ(inner__x="1"):
80
- settings = load_settings(Settings)
81
- assert settings.inner.x == 1
82
-
83
- def test_env_var_with_prefix_and_nested(self) -> None:
84
- class Settings(CustomBaseSettings):
85
- model_config = SettingsConfigDict(env_prefix="test__")
86
- inner: Inner
87
-
88
- class Inner(BaseSettings):
89
- x: int
90
-
91
- _ = Settings.model_rebuild()
92
- with temp_environ(test__inner__x="1"):
93
- settings = load_settings(Settings)
94
- assert settings.inner.x == 1
95
-
96
- def test_no_files(self) -> None:
97
- class Settings(CustomBaseSettings): ...
98
-
99
- _ = load_settings(Settings)
@@ -1,69 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, ClassVar, override
4
-
5
- from pydantic_settings import (
6
- BaseSettings,
7
- JsonConfigSettingsSource,
8
- PydanticBaseSettingsSource,
9
- SettingsConfigDict,
10
- TomlConfigSettingsSource,
11
- YamlConfigSettingsSource,
12
- )
13
-
14
- from utilities.iterables import always_iterable
15
-
16
- if TYPE_CHECKING:
17
- from collections.abc import Iterator
18
-
19
- from utilities.types import MaybeIterable, PathLike
20
-
21
-
22
- class CustomBaseSettings(BaseSettings):
23
- """Base settings for loading JSON files."""
24
-
25
- # paths
26
- json_files: ClassVar[MaybeIterable[PathLike]] = ()
27
- toml_files: ClassVar[MaybeIterable[PathLike]] = ()
28
- yaml_files: ClassVar[MaybeIterable[PathLike]] = ()
29
-
30
- # config
31
- model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
32
- env_nested_delimiter="__"
33
- )
34
-
35
- @classmethod
36
- @override
37
- def settings_customise_sources(
38
- cls,
39
- settings_cls: type[BaseSettings],
40
- init_settings: PydanticBaseSettingsSource,
41
- env_settings: PydanticBaseSettingsSource,
42
- dotenv_settings: PydanticBaseSettingsSource,
43
- file_secret_settings: PydanticBaseSettingsSource,
44
- ) -> tuple[PydanticBaseSettingsSource, ...]:
45
- _ = (init_settings, dotenv_settings, file_secret_settings)
46
- return tuple(cls._yield_base_settings_sources(settings_cls, env_settings))
47
-
48
- @classmethod
49
- def _yield_base_settings_sources(
50
- cls,
51
- settings_cls: type[BaseSettings],
52
- env_settings: PydanticBaseSettingsSource,
53
- /,
54
- ) -> Iterator[PydanticBaseSettingsSource]:
55
- yield env_settings
56
- for file in always_iterable(cls.json_files):
57
- yield JsonConfigSettingsSource(settings_cls, json_file=file)
58
- for file in always_iterable(cls.toml_files):
59
- yield TomlConfigSettingsSource(settings_cls, toml_file=file)
60
- for file in always_iterable(cls.yaml_files):
61
- yield YamlConfigSettingsSource(settings_cls, yaml_file=file)
62
-
63
-
64
- def load_settings[T: BaseSettings](cls: type[T], /) -> T:
65
- """Load a set of settings."""
66
- return cls()
67
-
68
-
69
- __all__ = ["CustomBaseSettings", "load_settings"]