dycw-utilities 0.168.3__tar.gz → 0.168.5__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.

Potentially problematic release.


This version of dycw-utilities might be problematic. Click here for more details.

Files changed (223) hide show
  1. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/PKG-INFO +4 -4
  2. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/pyproject.toml +14 -15
  3. dycw_utilities-0.168.5/src/tests/test_pydantic.py +21 -0
  4. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/__init__.py +1 -1
  5. dycw_utilities-0.168.5/src/utilities/pydantic.py +11 -0
  6. dycw_utilities-0.168.3/src/tests/test_typed_settings.py +0 -321
  7. dycw_utilities-0.168.3/src/utilities/typed_settings.py +0 -152
  8. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/.gitignore +0 -0
  9. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/LICENSE +0 -0
  10. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/README.md +0 -0
  11. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/__init__.py +0 -0
  12. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/conftest.py +0 -0
  13. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/__init__.py +0 -0
  14. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_missing/__init__.py +0 -0
  15. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_missing/module.py +0 -0
  16. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/__init__.py +0 -0
  17. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/outer_1.py +0 -0
  18. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/outer_2.py +0 -0
  19. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  20. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  21. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  22. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  23. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_without/__init__.py +0 -0
  24. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_without/module_1.py +0 -0
  25. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/package_without/module_2.py +0 -0
  26. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/standalone.py +0 -0
  27. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/modules/with_imports.py +0 -0
  28. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  29. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  30. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  31. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  32. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  33. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  34. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  35. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  36. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_aeventkit.py +0 -0
  37. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_altair.py +0 -0
  38. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_asyncio.py +0 -0
  39. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_atomicwrites.py +0 -0
  40. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_atools.py +0 -0
  41. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_cachetools.py +0 -0
  42. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_click.py +0 -0
  43. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_concurrent.py +0 -0
  44. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_contextlib.py +0 -0
  45. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_contextvars.py +0 -0
  46. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_cryptography.py +0 -0
  47. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_cvxpy.py +0 -0
  48. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_dataclasses.py +0 -0
  49. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_docker.py +0 -0
  50. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_enum.py +0 -0
  51. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_errors.py +0 -0
  52. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_fastapi.py +0 -0
  53. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_fpdf2.py +0 -0
  54. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_functions.py +0 -0
  55. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_functools.py +0 -0
  56. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_getpass.py +0 -0
  57. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_git.py +0 -0
  58. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_gzip.py +0 -0
  59. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_hashlib.py +0 -0
  60. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_http.py +0 -0
  61. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_hypothesis.py +0 -0
  62. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_importlib.py +0 -0
  63. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_inflect.py +0 -0
  64. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_ipython.py +0 -0
  65. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_iterables.py +0 -0
  66. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_jinja2.py +0 -0
  67. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_json.py +0 -0
  68. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_jupyter.py +0 -0
  69. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_libcst.py +0 -0
  70. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_lightweight_charts.py +0 -0
  71. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_logging.py +0 -0
  72. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_math.py +0 -0
  73. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_memory_profiler.py +0 -0
  74. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_modules.py +0 -0
  75. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_more_itertools.py +0 -0
  76. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_numpy.py +0 -0
  77. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_objects/__init__.py +0 -0
  78. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_objects/objects.py +0 -0
  79. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_operator.py +0 -0
  80. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_optuna.py +0 -0
  81. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_orjson.py +0 -0
  82. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_os.py +0 -0
  83. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_parse.py +0 -0
  84. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pathlib.py +0 -0
  85. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pickle.py +0 -0
  86. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_platform.py +0 -0
  87. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_polars.py +0 -0
  88. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_polars_ols.py +0 -0
  89. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_postgres.py +0 -0
  90. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pottery.py +0 -0
  91. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pqdm.py +0 -0
  92. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_psutil.py +0 -0
  93. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pydantic_settings.py +0 -0
  94. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pydantic_settings_sops.py +0 -0
  95. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pyinstrument.py +0 -0
  96. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pytest.py +0 -0
  97. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pytest_randomly.py +0 -0
  98. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_pytest_regressions.py +0 -0
  99. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_random.py +0 -0
  100. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_re.py +0 -0
  101. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_redis.py +0 -0
  102. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_reprlib.py +0 -0
  103. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_scipy.py +0 -0
  104. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_sentinel.py +0 -0
  105. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_shelve.py +0 -0
  106. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_slack_sdk.py +0 -0
  107. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_socket.py +0 -0
  108. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_sqlalchemy.py +0 -0
  109. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_sqlalchemy_polars.py +0 -0
  110. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_statsmodels.py +0 -0
  111. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_string.py +0 -0
  112. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_tempfile.py +0 -0
  113. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_testbook.py +0 -0
  114. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_text.py +0 -0
  115. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_threading.py +0 -0
  116. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_timer.py +0 -0
  117. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_traceback.py +0 -0
  118. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_types.py +0 -0
  119. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_typing.py +0 -0
  120. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_typing_funcs/__init__.py +0 -0
  121. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_typing_funcs/no_future.py +0 -0
  122. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_typing_funcs/with_future.py +0 -0
  123. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_tzdata.py +0 -0
  124. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_tzlocal.py +0 -0
  125. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_uuid.py +0 -0
  126. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_version.py +0 -0
  127. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_warnings.py +0 -0
  128. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_whenever.py +0 -0
  129. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_zipfile.py +0 -0
  130. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/tests/test_zoneinfo.py +0 -0
  131. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/aeventkit.py +0 -0
  132. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/altair.py +0 -0
  133. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/asyncio.py +0 -0
  134. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/atomicwrites.py +0 -0
  135. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/atools.py +0 -0
  136. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/cachetools.py +0 -0
  137. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/click.py +0 -0
  138. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/concurrent.py +0 -0
  139. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/contextlib.py +0 -0
  140. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/contextvars.py +0 -0
  141. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/cryptography.py +0 -0
  142. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/cvxpy.py +0 -0
  143. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/dataclasses.py +0 -0
  144. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/docker.py +0 -0
  145. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/enum.py +0 -0
  146. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/errors.py +0 -0
  147. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/fastapi.py +0 -0
  148. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/fpdf2.py +0 -0
  149. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/functions.py +0 -0
  150. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/functools.py +0 -0
  151. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/getpass.py +0 -0
  152. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/git.py +0 -0
  153. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/gzip.py +0 -0
  154. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/hashlib.py +0 -0
  155. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/http.py +0 -0
  156. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/hypothesis.py +0 -0
  157. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/importlib.py +0 -0
  158. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/inflect.py +0 -0
  159. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/ipython.py +0 -0
  160. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/iterables.py +0 -0
  161. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/jinja2.py +0 -0
  162. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/json.py +0 -0
  163. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/jupyter.py +0 -0
  164. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/libcst.py +0 -0
  165. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/lightweight_charts.py +0 -0
  166. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/logging.py +0 -0
  167. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/math.py +0 -0
  168. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/memory_profiler.py +0 -0
  169. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/modules.py +0 -0
  170. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/more_itertools.py +0 -0
  171. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/numpy.py +0 -0
  172. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/operator.py +0 -0
  173. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/optuna.py +0 -0
  174. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/orjson.py +0 -0
  175. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/os.py +0 -0
  176. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/parse.py +0 -0
  177. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pathlib.py +0 -0
  178. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pickle.py +0 -0
  179. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/platform.py +0 -0
  180. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/polars.py +0 -0
  181. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/polars_ols.py +0 -0
  182. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/postgres.py +0 -0
  183. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pottery.py +0 -0
  184. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pqdm.py +0 -0
  185. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/psutil.py +0 -0
  186. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/py.typed +0 -0
  187. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pydantic_settings.py +0 -0
  188. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pydantic_settings_sops.py +0 -0
  189. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pyinstrument.py +0 -0
  190. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pytest.py +0 -0
  191. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pytest_plugins/__init__.py +0 -0
  192. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  193. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  194. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/pytest_regressions.py +0 -0
  195. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/random.py +0 -0
  196. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/re.py +0 -0
  197. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/redis.py +0 -0
  198. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/reprlib.py +0 -0
  199. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/scipy.py +0 -0
  200. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/sentinel.py +0 -0
  201. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/shelve.py +0 -0
  202. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/slack_sdk.py +0 -0
  203. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/socket.py +0 -0
  204. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/sqlalchemy.py +0 -0
  205. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/sqlalchemy_polars.py +0 -0
  206. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/statsmodels.py +0 -0
  207. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/string.py +0 -0
  208. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/tempfile.py +0 -0
  209. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/testbook.py +0 -0
  210. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/text.py +0 -0
  211. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/threading.py +0 -0
  212. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/timer.py +0 -0
  213. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/traceback.py +0 -0
  214. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/types.py +0 -0
  215. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/typing.py +0 -0
  216. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/tzdata.py +0 -0
  217. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/tzlocal.py +0 -0
  218. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/uuid.py +0 -0
  219. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/version.py +0 -0
  220. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/warnings.py +0 -0
  221. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/whenever.py +0 -0
  222. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/zipfile.py +0 -0
  223. {dycw_utilities-0.168.3 → dycw_utilities-0.168.5}/src/utilities/zoneinfo.py +0 -0
@@ -1,22 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.168.3
3
+ Version: 0.168.5
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
7
  Requires-Dist: atomicwrites<1.5,>=1.4.1
8
8
  Requires-Dist: typing-extensions<4.16,>=4.15.0
9
9
  Requires-Dist: tzlocal<5.4,>=5.3.1
10
- Requires-Dist: whenever<0.10,>=0.9.0
10
+ Requires-Dist: whenever<0.10,>=0.9.2
11
11
  Provides-Extra: logging
12
12
  Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
13
13
  Provides-Extra: test
14
14
  Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
15
- Requires-Dist: hypothesis<6.141,>=6.140.2; extra == 'test'
15
+ Requires-Dist: hypothesis<6.141,>=6.140.3; extra == 'test'
16
16
  Requires-Dist: pytest-asyncio<1.3,>=1.2.0; extra == 'test'
17
17
  Requires-Dist: pytest-cov<7.1,>=7.0.0; extra == 'test'
18
18
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
19
- Requires-Dist: pytest-lazy-fixtures<1.4,>=1.3.4; extra == 'test'
19
+ Requires-Dist: pytest-lazy-fixtures<1.5,>=1.4.0; extra == 'test'
20
20
  Requires-Dist: pytest-randomly<4.1,>=4.0.1; extra == 'test'
21
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'
@@ -31,10 +31,10 @@ core = [
31
31
  "atomicwrites >=1.4.1, <1.5",
32
32
  "typing-extensions >=4.15.0, <4.16",
33
33
  "tzlocal >=5.3.1, <5.4",
34
- "whenever >=0.9.0, <0.10",
34
+ "whenever >=0.9.2, <0.10",
35
35
  ]
36
36
  cryptography = [
37
- "cryptography >=46.0.1, <46.1",
37
+ "cryptography >=46.0.2, <46.1",
38
38
  ]
39
39
  cvxpy = [
40
40
  "cvxpy >=1.7.3, <1.8",
@@ -52,7 +52,7 @@ dev = [
52
52
  "pytest-timeout >=2.4.0, <2.5",
53
53
  ]
54
54
  fastapi = [
55
- "fastapi >=0.117.1, <0.118",
55
+ "fastapi >=0.118.0, <0.119",
56
56
  ]
57
57
  fastapi-test = [
58
58
  "httpx",
@@ -71,7 +71,7 @@ http-test = [
71
71
  "orjson",
72
72
  ]
73
73
  hypothesis = [
74
- "hypothesis >=6.140.2, <6.141",
74
+ "hypothesis >=6.140.3, <6.141",
75
75
  ]
76
76
  hypothesis-test = [
77
77
  "libcst",
@@ -124,7 +124,7 @@ orjson-test = [
124
124
  "polars",
125
125
  ]
126
126
  polars = [
127
- "polars >=1.33.1, <1.34",
127
+ "polars >=1.34.0, <1.35",
128
128
  ]
129
129
  polars-ols = [
130
130
  "polars-ols >=0.3.5, <0.4",
@@ -149,6 +149,9 @@ pqdm = [
149
149
  psutil = [
150
150
  "psutil >=7.1.0, <7.2",
151
151
  ]
152
+ pydantic = [
153
+ "pydantic >=2.12.0, <2.13",
154
+ ]
152
155
  pydantic-settings = [
153
156
  "pydantic-settings >=2.11.0, <2.12",
154
157
  ]
@@ -198,7 +201,7 @@ sklearn = [
198
201
  "scikit-learn >=1.7.2, <1.8",
199
202
  ]
200
203
  slack-sdk = [
201
- "slack-sdk >=3.36.0, <3.37",
204
+ "slack-sdk >=3.37.0, <3.38",
202
205
  ]
203
206
  slack-sdk-test = [
204
207
  "aiohttp",
@@ -227,9 +230,6 @@ statsmodels = [
227
230
  testbook = [
228
231
  "testbook >=0.4.2, <0.5",
229
232
  ]
230
- typed-settings = [
231
- "typed-settings >=25.0.0, <25.1",
232
- ]
233
233
  tzdata = [
234
234
  "tzdata >=2025.2, <2025.3",
235
235
  ]
@@ -244,12 +244,12 @@ dependencies = [
244
244
  "atomicwrites >=1.4.1, <1.5",
245
245
  "typing-extensions >=4.15.0, <4.16",
246
246
  "tzlocal >=5.3.1, <5.4",
247
- "whenever >=0.9.0, <0.10",
247
+ "whenever >=0.9.2, <0.10",
248
248
  ]
249
249
  name = "dycw-utilities"
250
250
  readme = "README.md"
251
251
  requires-python = ">= 3.12"
252
- version = "0.168.3"
252
+ version = "0.168.5"
253
253
 
254
254
  [project.entry-points.pytest11]
255
255
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -261,12 +261,12 @@ logging = [
261
261
  ]
262
262
  test = [
263
263
  "dycw-pytest-only >=2.1.1, <2.2",
264
- "hypothesis >=6.140.2, <6.141",
264
+ "hypothesis >=6.140.3, <6.141",
265
265
  "pytest >=8.4.2, <8.5",
266
266
  "pytest-asyncio >=1.2.0, <1.3",
267
267
  "pytest-cov >=7.0.0, <7.1",
268
268
  "pytest-instafail >=0.5.0, <0.6",
269
- "pytest-lazy-fixtures >=1.3.4, <1.4",
269
+ "pytest-lazy-fixtures >=1.4.0, <1.5",
270
270
  "pytest-randomly >=4.0.1, <4.1",
271
271
  "pytest-regressions >=2.8.3, <2.9",
272
272
  "pytest-repeat >=0.9.4, <0.10",
@@ -282,7 +282,7 @@ test = [
282
282
  # bump-my-version
283
283
  [tool.bumpversion]
284
284
  allow_dirty = true
285
- current_version = "0.168.3"
285
+ current_version = "0.168.5"
286
286
 
287
287
  [[tool.bumpversion.files]]
288
288
  filename = "src/utilities/__init__.py"
@@ -486,7 +486,6 @@ select = [
486
486
  "S101", # assert
487
487
  "SLF001", # private-member-access
488
488
  ]
489
- "src/tests/test_typed_settings.py" = ["I002"] # missing-required-import
490
489
  "src/tests/test_typing_funcs/no_future.py" = ["I002"] # missing-required-import
491
490
 
492
491
  [tool.ruff.lint.flake8-tidy-imports]
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from pydantic import BaseModel
6
+
7
+ from utilities.pydantic import ExpandedPath
8
+
9
+ _ = ExpandedPath
10
+
11
+
12
+ class TestExpandedPath:
13
+ def test_main(self) -> None:
14
+ class Example(BaseModel):
15
+ path: ExpandedPath
16
+
17
+ _ = Example.model_rebuild()
18
+
19
+ result = Example(path=Path("~")).path
20
+ expected = Path.home()
21
+ assert result == expected
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.168.3"
3
+ __version__ = "0.168.5"
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated
5
+
6
+ from pydantic import BeforeValidator
7
+
8
+ ExpandedPath = Annotated[Path, BeforeValidator(lambda p: Path(p).expanduser())]
9
+
10
+
11
+ __all__ = ["ExpandedPath"]
@@ -1,321 +0,0 @@
1
- from collections.abc import Callable
2
- from dataclasses import dataclass
3
- from ipaddress import IPv4Address, IPv6Address
4
- from pathlib import Path
5
- from typing import Any, ClassVar, Self, assert_never, override
6
- from uuid import UUID
7
-
8
- import typed_settings
9
- from hypothesis import given
10
- from hypothesis.strategies import (
11
- DataObject,
12
- SearchStrategy,
13
- booleans,
14
- data,
15
- integers,
16
- ip_addresses,
17
- sampled_from,
18
- tuples,
19
- uuids,
20
- )
21
- from pytest import mark, param, raises
22
- from typed_settings import EnvLoader, FileLoader, TomlFormat
23
- from whenever import (
24
- Date,
25
- DateDelta,
26
- DateTimeDelta,
27
- MonthDay,
28
- PlainDateTime,
29
- Time,
30
- TimeDelta,
31
- YearMonth,
32
- ZonedDateTime,
33
- )
34
-
35
- from utilities.hypothesis import (
36
- date_deltas,
37
- date_time_deltas,
38
- dates,
39
- month_days,
40
- paths,
41
- plain_date_times,
42
- temp_paths,
43
- text_ascii,
44
- time_deltas,
45
- times,
46
- year_months,
47
- zoned_date_times,
48
- )
49
- from utilities.os import temp_environ
50
- from utilities.re import extract_group
51
- from utilities.sentinel import Sentinel, sentinel
52
- from utilities.text import strip_and_dedent
53
- from utilities.typed_settings import (
54
- ExtendedTSConverter,
55
- LoadSettingsError,
56
- load_settings,
57
- )
58
-
59
- app_names = text_ascii(min_size=1).map(str.lower)
60
-
61
-
62
- @dataclass(kw_only=True, slots=True)
63
- class _Case[T]:
64
- cls: type[T]
65
- strategy: SearchStrategy[T]
66
- serialize: Callable[[T], str]
67
-
68
-
69
- class TestExtendedTSConverter:
70
- cases: ClassVar[list[_Case]] = [
71
- _Case(cls=Date, strategy=dates(), serialize=str),
72
- _Case(cls=DateDelta, strategy=date_deltas(parsable=True), serialize=str),
73
- _Case(
74
- cls=DateTimeDelta, strategy=date_time_deltas(parsable=True), serialize=str
75
- ),
76
- _Case(cls=IPv4Address, strategy=ip_addresses(v=4), serialize=str),
77
- _Case(cls=IPv6Address, strategy=ip_addresses(v=6), serialize=str),
78
- _Case(cls=MonthDay, strategy=month_days(), serialize=str),
79
- _Case(cls=PlainDateTime, strategy=plain_date_times(), serialize=str),
80
- _Case(cls=Time, strategy=times(), serialize=str),
81
- _Case(cls=TimeDelta, strategy=time_deltas(), serialize=str),
82
- _Case(cls=UUID, strategy=uuids(), serialize=str),
83
- _Case(cls=YearMonth, strategy=year_months(), serialize=str),
84
- _Case(cls=ZonedDateTime, strategy=zoned_date_times(), serialize=str),
85
- ]
86
-
87
- @given(data=data())
88
- @mark.parametrize(("cls", "strategy"), [param(c.cls, c.strategy) for c in cases])
89
- def test_default(
90
- self, *, data: DataObject, cls: type[Any], strategy: SearchStrategy[Any]
91
- ) -> None:
92
- default = data.draw(strategy)
93
-
94
- @dataclass(frozen=True, kw_only=True, slots=True)
95
- class Settings:
96
- value: cls = default # pyright: ignore[reportInvalidTypeForm]
97
-
98
- loaded = typed_settings.load_settings(
99
- Settings, loaders=[], converter=ExtendedTSConverter()
100
- )
101
- assert loaded.value == default
102
-
103
- @given(data=data(), root=temp_paths(), app_name=app_names)
104
- @mark.parametrize(
105
- ("cls", "strategy", "serialize"),
106
- [param(c.cls, c.strategy, c.serialize) for c in cases],
107
- )
108
- def test_loaded(
109
- self,
110
- *,
111
- data: DataObject,
112
- root: Path,
113
- app_name: str,
114
- cls: type[Any],
115
- strategy: SearchStrategy[Any],
116
- serialize: Callable[[Any], str],
117
- ) -> None:
118
- default, value = data.draw(tuples(strategy, strategy))
119
-
120
- @dataclass(frozen=True, kw_only=True, slots=True)
121
- class Settings:
122
- value: cls = default # pyright: ignore[reportInvalidTypeForm]
123
-
124
- file = Path(root, "file.toml")
125
- _ = file.write_text(
126
- strip_and_dedent(f"""
127
- [{app_name}]
128
- value = '{serialize(value)}'
129
- """)
130
- )
131
- loaded = typed_settings.load_settings(
132
- Settings,
133
- loaders=[
134
- FileLoader(formats={"*.toml": TomlFormat(app_name)}, files=[file])
135
- ],
136
- converter=ExtendedTSConverter(),
137
- )
138
- assert loaded.value == value
139
-
140
- @given(
141
- root=temp_paths(),
142
- app_name=app_names,
143
- env_name=text_ascii(min_size=1).map(lambda text: f"TEST_{text}".upper()),
144
- env_value=text_ascii(min_size=1),
145
- )
146
- def test_path_env_var(
147
- self, *, root: str, app_name: str, env_name: str, env_value: str
148
- ) -> None:
149
- @dataclass(frozen=True, kw_only=True, slots=True)
150
- class Settings:
151
- value: Path
152
-
153
- file = Path(root, "file.toml")
154
- _ = file.write_text(
155
- strip_and_dedent(f"""
156
- [{app_name}]
157
- value = '${env_name}'
158
- """)
159
- )
160
- with temp_environ({env_name: env_value}):
161
- settings = typed_settings.load_settings(
162
- Settings,
163
- loaders=[
164
- FileLoader(formats={"*.toml": TomlFormat(app_name)}, files=[file])
165
- ],
166
- converter=ExtendedTSConverter(resolve_paths=False),
167
- )
168
- expected = Path(env_value)
169
- assert settings.value == expected
170
-
171
- @given(root=temp_paths(), app_name=app_names, path=paths(), resolve=booleans())
172
- def test_path_resolution(
173
- self, *, root: str, app_name: str, path: Path, resolve: bool
174
- ) -> None:
175
- @dataclass(frozen=True, kw_only=True, slots=True)
176
- class Settings:
177
- value: Path
178
-
179
- file = Path(root, "file.toml")
180
- _ = file.write_text(
181
- strip_and_dedent(f"""
182
- [{app_name}]
183
- value = '{path!s}'
184
- """)
185
- )
186
- settings = typed_settings.load_settings(
187
- Settings,
188
- loaders=[
189
- FileLoader(formats={"*.toml": TomlFormat(app_name)}, files=[file])
190
- ],
191
- converter=ExtendedTSConverter(resolve_paths=resolve),
192
- )
193
- match resolve:
194
- case True:
195
- expected = Path.cwd().joinpath(path)
196
- case False:
197
- expected = Path(path)
198
- case never:
199
- assert_never(never)
200
- assert settings.value == expected
201
-
202
-
203
- class TestLoadSettings:
204
- @given(root=temp_paths(), date_time=zoned_date_times(), app_name=app_names)
205
- def test_main(self, *, root: Path, date_time: ZonedDateTime, app_name: str) -> None:
206
- @dataclass(frozen=True, kw_only=True, slots=True)
207
- class Settings:
208
- date_time: ZonedDateTime
209
-
210
- file = root.joinpath("settings.toml")
211
- _ = file.write_text(
212
- strip_and_dedent(f"""
213
- [{app_name}]
214
- date_time = {str(date_time)!r}
215
- """)
216
- )
217
- settings = load_settings(Settings, app_name, start_dir=root)
218
- assert settings.date_time == date_time
219
-
220
- @given(
221
- prefix=app_names.map(lambda text: f"TEST_{text}".upper()),
222
- date_time=zoned_date_times(),
223
- app_name=app_names,
224
- )
225
- def test_loaders(
226
- self, *, prefix: str, date_time: ZonedDateTime, app_name: str
227
- ) -> None:
228
- key = f"{prefix}__DATE_TIME"
229
-
230
- @dataclass(frozen=True, kw_only=True, slots=True)
231
- class Settings:
232
- date_time: ZonedDateTime
233
-
234
- with temp_environ({key: str(date_time)}):
235
- settings = load_settings(
236
- Settings, app_name, loaders=[EnvLoader(prefix=f"{prefix}__")]
237
- )
238
- assert settings.date_time == date_time
239
-
240
- @given(root=temp_paths(), app_name=app_names)
241
- def test_converter_simple(self, *, root: Path, app_name: str) -> None:
242
- @dataclass(frozen=True, kw_only=True, slots=True)
243
- class Settings:
244
- sentinel: Sentinel
245
-
246
- file = root.joinpath("settings.toml")
247
- _ = file.write_text(
248
- strip_and_dedent(f"""
249
- [{app_name}]
250
- sentinel = 'sentinel'
251
- """)
252
- )
253
-
254
- def convert(text: str, /) -> Sentinel:
255
- if text == "sentinel":
256
- return sentinel
257
- raise ValueError
258
-
259
- settings = load_settings(
260
- Settings, app_name, start_dir=root, converters=[(Sentinel, convert)]
261
- )
262
- assert settings.sentinel is sentinel
263
-
264
- @given(data=data(), root=temp_paths(), n=integers(), app_name=app_names)
265
- def test_converter_dataclass(
266
- self, *, data: DataObject, root: Path, n: int, app_name: str
267
- ) -> None:
268
- @dataclass(repr=False, frozen=True, kw_only=True, slots=True)
269
- class Left:
270
- x: int
271
-
272
- @override
273
- def __str__(self) -> str:
274
- return f"left{self.x}"
275
-
276
- @classmethod
277
- def parse(cls, text: str, /) -> Self:
278
- x = extract_group(r"^left(.+?)$", text)
279
- return cls(x=int(x))
280
-
281
- @dataclass(frozen=True, kw_only=True, slots=True)
282
- class Right:
283
- y: int
284
-
285
- @override
286
- def __str__(self) -> str:
287
- return f"right{self.y}"
288
-
289
- @classmethod
290
- def parse(cls, text: str, /) -> Self:
291
- y = extract_group(r"^right(.+?)$", text)
292
- return cls(y=int(y))
293
-
294
- value = data.draw(sampled_from([Left(x=n), Right(y=n)]))
295
-
296
- @dataclass(frozen=True, kw_only=True, slots=True)
297
- class Settings:
298
- inner: Left | Right
299
-
300
- file = root.joinpath("settings.toml")
301
- _ = file.write_text(
302
- strip_and_dedent(f"""
303
- [{app_name}]
304
- inner = {str(value)!r}
305
- """)
306
- )
307
- settings = load_settings(
308
- Settings,
309
- app_name,
310
- start_dir=root,
311
- converters=[(Left, Left.parse), (Right, Right.parse)],
312
- )
313
- assert settings.inner == value
314
-
315
- @mark.parametrize("app_name", [param("app_"), param("app1"), param("app__name")])
316
- def test_error(self, *, app_name: str) -> None:
317
- @dataclass(frozen=True, kw_only=True, slots=True)
318
- class Settings: ...
319
-
320
- with raises(LoadSettingsError, match=r"Invalid app name; got '.+'"):
321
- _ = load_settings(Settings, app_name)
@@ -1,152 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass
4
- from functools import partial
5
- from ipaddress import IPv4Address, IPv6Address
6
- from os import environ
7
- from pathlib import Path
8
- from re import search
9
- from typing import TYPE_CHECKING, Any, assert_never, override
10
- from uuid import UUID
11
-
12
- import typed_settings
13
- from typed_settings import EnvLoader, FileLoader, find
14
- from typed_settings.converters import TSConverter
15
- from typed_settings.loaders import TomlFormat
16
- from whenever import (
17
- Date,
18
- DateDelta,
19
- DateTimeDelta,
20
- MonthDay,
21
- PlainDateTime,
22
- Time,
23
- TimeDelta,
24
- YearMonth,
25
- ZonedDateTime,
26
- )
27
-
28
- from utilities.iterables import always_iterable
29
- from utilities.pathlib import to_path
30
- from utilities.string import substitute_environ
31
-
32
- if TYPE_CHECKING:
33
- from collections.abc import Callable, Iterable
34
-
35
- from typed_settings.loaders import Loader
36
- from typed_settings.processors import Processor
37
-
38
- from utilities.types import MaybeCallablePathLike, MaybeIterable, PathLike
39
-
40
-
41
- type _ConverterItem = tuple[type[Any], Callable[..., Any]]
42
-
43
-
44
- ##
45
-
46
-
47
- class ExtendedTSConverter(TSConverter):
48
- """An extension of the TSConverter for custom types."""
49
-
50
- @override
51
- def __init__(
52
- self,
53
- *,
54
- resolve_paths: bool = True,
55
- strlist_sep: str | Callable[[str], list] | None = ":",
56
- extra: Iterable[_ConverterItem] = (),
57
- ) -> None:
58
- super().__init__(resolve_paths=resolve_paths, strlist_sep=strlist_sep)
59
- cases: list[_ConverterItem] = [
60
- (Date, Date.parse_iso),
61
- (DateDelta, DateDelta.parse_iso),
62
- (DateTimeDelta, DateTimeDelta.parse_iso),
63
- (IPv4Address, IPv4Address),
64
- (IPv6Address, IPv6Address),
65
- (MonthDay, MonthDay.parse_iso),
66
- (Path, partial(_parse_path, resolve=resolve_paths, pwd=Path.cwd())),
67
- (PlainDateTime, PlainDateTime.parse_iso),
68
- (Time, Time.parse_iso),
69
- (TimeDelta, TimeDelta.parse_iso),
70
- (UUID, UUID),
71
- (YearMonth, YearMonth.parse_iso),
72
- (ZonedDateTime, ZonedDateTime.parse_iso),
73
- *extra,
74
- ]
75
- extras = {cls: _make_converter(cls, func) for cls, func in cases}
76
- self.scalar_converters |= extras
77
-
78
-
79
- def _make_converter[T](
80
- cls: type[T], parser: Callable[[str], T], /
81
- ) -> Callable[[Any, type[Any]], Any]:
82
- def hook(value: T | str, _: type[T] = cls, /) -> Any:
83
- if not isinstance(value, (cls, str)): # pragma: no cover
84
- msg = f"Invalid type {type(value).__name__!r}; expected '{cls.__name__}' or 'str'"
85
- raise TypeError(msg)
86
- if isinstance(value, str):
87
- return parser(value)
88
- return value
89
-
90
- return hook
91
-
92
-
93
- def _parse_path(
94
- path: str, /, *, resolve: bool = False, pwd: MaybeCallablePathLike = Path.cwd
95
- ) -> Path:
96
- path = substitute_environ(path, **environ)
97
- match resolve:
98
- case True:
99
- return to_path(pwd).joinpath(path).resolve()
100
- case False:
101
- return Path(path)
102
- case never:
103
- assert_never(never)
104
-
105
-
106
- ##
107
-
108
-
109
- _BASE_DIR: Path = Path()
110
-
111
-
112
- def load_settings[T](
113
- cls: type[T],
114
- app_name: str,
115
- /,
116
- *,
117
- filenames: MaybeIterable[str] = "settings.toml",
118
- start_dir: PathLike | None = None,
119
- loaders: MaybeIterable[Loader] | None = None,
120
- processors: MaybeIterable[Processor] = (),
121
- converters: Iterable[_ConverterItem] = (),
122
- base_dir: Path = _BASE_DIR,
123
- ) -> T:
124
- if not search(r"^[A-Za-z]+(?:_[A-Za-z]+)*$", app_name):
125
- raise LoadSettingsError(appname=app_name)
126
- filenames_use = list(always_iterable(filenames))
127
- start_dir_use = None if start_dir is None else Path(start_dir)
128
- files = [find(filename, start_dir=start_dir_use) for filename in filenames_use]
129
- file_loader = FileLoader(formats={"*.toml": TomlFormat(app_name)}, files=files)
130
- env_loader = EnvLoader(f"{app_name.upper()}__", nested_delimiter="__")
131
- loaders_use: list[Loader] = [file_loader, env_loader]
132
- if loaders is not None:
133
- loaders_use.extend(always_iterable(loaders))
134
- return typed_settings.load_settings(
135
- cls,
136
- loaders_use,
137
- processors=list(always_iterable(processors)),
138
- converter=ExtendedTSConverter(extra=converters),
139
- base_dir=base_dir,
140
- )
141
-
142
-
143
- @dataclass(kw_only=True, slots=True)
144
- class LoadSettingsError(Exception):
145
- appname: str
146
-
147
- @override
148
- def __str__(self) -> str:
149
- return f"Invalid app name; got {self.appname!r}"
150
-
151
-
152
- __all__ = ["ExtendedTSConverter", "LoadSettingsError", "load_settings"]