dycw-utilities 0.131.13__tar.gz → 0.131.15__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 (223) hide show
  1. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/PKG-INFO +1 -1
  2. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/pyproject.toml +4 -2
  3. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_hypothesis.py +0 -25
  4. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_luigi.py +16 -31
  5. dycw_utilities-0.131.15/src/tests/test_typed_settings.py +97 -0
  6. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/__init__.py +1 -1
  7. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/hypothesis.py +0 -81
  8. dycw_utilities-0.131.15/src/utilities/luigi.py +195 -0
  9. dycw_utilities-0.131.15/src/utilities/typed_settings.py +61 -0
  10. dycw_utilities-0.131.13/src/utilities/luigi.py +0 -228
  11. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/.gitignore +0 -0
  12. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/LICENSE +0 -0
  13. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/README.md +0 -0
  14. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/__init__.py +0 -0
  15. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/conftest.py +0 -0
  16. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/__init__.py +0 -0
  17. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_missing/__init__.py +0 -0
  18. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_missing/module.py +0 -0
  19. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/__init__.py +0 -0
  20. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/outer_1.py +0 -0
  21. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/outer_2.py +0 -0
  22. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  23. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  24. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  25. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  26. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_without/__init__.py +0 -0
  27. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_without/module_1.py +0 -0
  28. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/package_without/module_2.py +0 -0
  29. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/standalone.py +0 -0
  30. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/modules/with_imports.py +0 -0
  31. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  32. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  33. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  34. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  35. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  36. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  37. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  38. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  39. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_aiolimiter.py +0 -0
  40. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_altair.py +0 -0
  41. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_asyncio.py +0 -0
  42. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_asyncio_classes/__init__.py +0 -0
  43. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_asyncio_classes/loopers.py +0 -0
  44. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_asyncio_classes/redis.py +0 -0
  45. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_atomicwrites.py +0 -0
  46. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_atools.py +0 -0
  47. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_cachetools.py +0 -0
  48. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_click.py +0 -0
  49. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_concurrent.py +0 -0
  50. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_contextlib.py +0 -0
  51. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_contextvars.py +0 -0
  52. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_cryptography.py +0 -0
  53. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_cvxpy.py +0 -0
  54. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_dataclasses.py +0 -0
  55. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_datetime.py +0 -0
  56. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_enum.py +0 -0
  57. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_errors.py +0 -0
  58. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_eventkit.py +0 -0
  59. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_fastapi.py +0 -0
  60. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_fpdf2.py +0 -0
  61. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_functions.py +0 -0
  62. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_functools.py +0 -0
  63. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_getpass.py +0 -0
  64. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_git.py +0 -0
  65. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_hashlib.py +0 -0
  66. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_http.py +0 -0
  67. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_importlib.py +0 -0
  68. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_inflect.py +0 -0
  69. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_ipython.py +0 -0
  70. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_iterables.py +0 -0
  71. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_jupyter.py +0 -0
  72. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_libcst.py +0 -0
  73. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_lightweight_charts.py +0 -0
  74. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_logging.py +0 -0
  75. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_math.py +0 -0
  76. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_memory_profiler.py +0 -0
  77. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_modules.py +0 -0
  78. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_more_itertools.py +0 -0
  79. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_numpy.py +0 -0
  80. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_operator.py +0 -0
  81. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_optuna.py +0 -0
  82. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_orjson.py +0 -0
  83. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_os.py +0 -0
  84. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_parse.py +0 -0
  85. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pathlib.py +0 -0
  86. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_period.py +0 -0
  87. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pickle.py +0 -0
  88. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_platform.py +0 -0
  89. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_polars.py +0 -0
  90. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_polars_ols.py +0 -0
  91. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pottery.py +0 -0
  92. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pqdm.py +0 -0
  93. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_psutil.py +0 -0
  94. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pydantic.py +0 -0
  95. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pyinstrument.py +0 -0
  96. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pyrsistent.py +0 -0
  97. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pytest.py +0 -0
  98. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_pytest_regressions.py +0 -0
  99. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_python_dotenv.py +0 -0
  100. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_random.py +0 -0
  101. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_re.py +0 -0
  102. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_redis.py +0 -0
  103. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_reprlib.py +0 -0
  104. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_scipy.py +0 -0
  105. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_sentinel.py +0 -0
  106. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_shelve.py +0 -0
  107. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_slack_sdk.py +0 -0
  108. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_socket.py +0 -0
  109. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_sqlalchemy.py +0 -0
  110. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_sqlalchemy_polars.py +0 -0
  111. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_statsmodel.py +0 -0
  112. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_streamlit.py +0 -0
  113. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_string.py +0 -0
  114. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_tempfile.py +0 -0
  115. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_tenacity.py +0 -0
  116. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_text.py +0 -0
  117. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_threading.py +0 -0
  118. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_timer.py +0 -0
  119. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_traceback.py +0 -0
  120. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_types.py +0 -0
  121. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_typing.py +0 -0
  122. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_typing_funcs/__init__.py +0 -0
  123. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_typing_funcs/no_future.py +0 -0
  124. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_typing_funcs/with_future.py +0 -0
  125. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_tzdata.py +0 -0
  126. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_tzlocal.py +0 -0
  127. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_uuid.py +0 -0
  128. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_version.py +0 -0
  129. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_warnings.py +0 -0
  130. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_whenever.py +0 -0
  131. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_whenever2.py +0 -0
  132. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_zipfile.py +0 -0
  133. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/tests/test_zoneinfo.py +0 -0
  134. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/aiolimiter.py +0 -0
  135. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/altair.py +0 -0
  136. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/asyncio.py +0 -0
  137. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/click.py +0 -0
  141. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/concurrent.py +0 -0
  142. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/contextlib.py +0 -0
  143. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/contextvars.py +0 -0
  144. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/cryptography.py +0 -0
  145. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/cvxpy.py +0 -0
  146. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/dataclasses.py +0 -0
  147. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/datetime.py +0 -0
  148. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/enum.py +0 -0
  149. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/errors.py +0 -0
  150. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/eventkit.py +0 -0
  151. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/fastapi.py +0 -0
  152. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/fpdf2.py +0 -0
  153. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/functions.py +0 -0
  154. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/functools.py +0 -0
  155. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/getpass.py +0 -0
  156. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/git.py +0 -0
  157. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/hashlib.py +0 -0
  158. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/http.py +0 -0
  159. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/importlib.py +0 -0
  160. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/inflect.py +0 -0
  161. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/ipython.py +0 -0
  162. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/iterables.py +0 -0
  163. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/jupyter.py +0 -0
  164. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/libcst.py +0 -0
  165. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/lightweight_charts.py +0 -0
  166. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/logging.py +0 -0
  167. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/math.py +0 -0
  168. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/memory_profiler.py +0 -0
  169. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/modules.py +0 -0
  170. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/more_itertools.py +0 -0
  171. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/numpy.py +0 -0
  172. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/operator.py +0 -0
  173. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/optuna.py +0 -0
  174. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/orjson.py +0 -0
  175. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/os.py +0 -0
  176. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/parse.py +0 -0
  177. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pathlib.py +0 -0
  178. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/period.py +0 -0
  179. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pickle.py +0 -0
  180. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/platform.py +0 -0
  181. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/polars.py +0 -0
  182. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/polars_ols.py +0 -0
  183. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pottery.py +0 -0
  184. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pqdm.py +0 -0
  185. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/psutil.py +0 -0
  186. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/py.typed +0 -0
  187. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pydantic.py +0 -0
  188. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pyinstrument.py +0 -0
  189. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pyrsistent.py +0 -0
  190. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pytest.py +0 -0
  191. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/pytest_regressions.py +0 -0
  192. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/python_dotenv.py +0 -0
  193. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/random.py +0 -0
  194. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/re.py +0 -0
  195. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/redis.py +0 -0
  196. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/reprlib.py +0 -0
  197. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/scipy.py +0 -0
  198. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/sentinel.py +0 -0
  199. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/shelve.py +0 -0
  200. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/slack_sdk.py +0 -0
  201. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/socket.py +0 -0
  202. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/sqlalchemy.py +0 -0
  203. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/sqlalchemy_polars.py +0 -0
  204. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/statsmodels.py +0 -0
  205. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/streamlit.py +0 -0
  206. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/string.py +0 -0
  207. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/tempfile.py +0 -0
  208. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/tenacity.py +0 -0
  209. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/text.py +0 -0
  210. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/threading.py +0 -0
  211. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/timer.py +0 -0
  212. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/traceback.py +0 -0
  213. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/types.py +0 -0
  214. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/typing.py +0 -0
  215. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/tzdata.py +0 -0
  216. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/tzlocal.py +0 -0
  217. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/uuid.py +0 -0
  218. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/version.py +0 -0
  219. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/warnings.py +0 -0
  220. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/whenever.py +0 -0
  221. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/whenever2.py +0 -0
  222. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/zipfile.py +0 -0
  223. {dycw_utilities-0.131.13 → dycw_utilities-0.131.15}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.131.13
3
+ Version: 0.131.15
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -61,6 +61,7 @@ dev = [
61
61
  "streamlit >= 1.45.0, < 1.46",
62
62
  "tenacity >= 8.5.0, < 9.0", # limited by luigi
63
63
  "tomlkit >= 0.13.2, < 0.14",
64
+ "typed-settings >= 24.6.0, < 24.7",
64
65
  "tzdata >= 2025.2, < 2025.3",
65
66
  "uvicorn >= 0.34.1, < 0.35",
66
67
  "vegafusion >= 2.0.2, < 2.1",
@@ -94,7 +95,7 @@ dependencies = [
94
95
  name = "dycw-utilities"
95
96
  readme = "README.md"
96
97
  requires-python = ">= 3.12"
97
- version = "0.131.13"
98
+ version = "0.131.15"
98
99
 
99
100
  [project.optional-dependencies]
100
101
  logging = [
@@ -121,7 +122,7 @@ test = [
121
122
  # bump-my-version
122
123
  [tool.bumpversion]
123
124
  allow_dirty = true
124
- current_version = "0.131.13"
125
+ current_version = "0.131.15"
125
126
 
126
127
  [[tool.bumpversion.files]]
127
128
  filename = "src/utilities/__init__.py"
@@ -319,6 +320,7 @@ select = [
319
320
  "S101", # assert
320
321
  "SLF001", # private-member-access
321
322
  ]
323
+ "src/tests/test_typed_settings.py" = ["I002"] # missing-required-import
322
324
  "src/tests/test_typing_funcs/no_future.py" = ["I002"] # missing-required-import
323
325
 
324
326
  [tool.ruff.lint.flake8-tidy-imports]
@@ -43,7 +43,6 @@ from utilities.datetime import (
43
43
  )
44
44
  from utilities.functions import ensure_int
45
45
  from utilities.hypothesis import (
46
- MaybeSearchStrategy,
47
46
  PlainDateTimesError,
48
47
  Shape,
49
48
  ZonedDateTimesError,
@@ -67,7 +66,6 @@ from utilities.hypothesis import (
67
66
  int64s,
68
67
  int_arrays,
69
68
  lists_fixed_length,
70
- min_and_max_datetimes,
71
69
  months,
72
70
  namespace_mixins,
73
71
  numbers,
@@ -704,29 +702,6 @@ class TestListsFixedLength:
704
702
  assert sorted(result) == result
705
703
 
706
704
 
707
- class TestMinAndMaxDateTimes:
708
- @given(
709
- data=data(),
710
- min_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
711
- max_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
712
- )
713
- def test_main(
714
- self,
715
- *,
716
- data: DataObject,
717
- min_value: MaybeSearchStrategy[dt.datetime | None],
718
- max_value: MaybeSearchStrategy[dt.datetime | None],
719
- ) -> None:
720
- min_datetime, max_datetime = data.draw(
721
- min_and_max_datetimes(min_value=min_value, max_value=max_value)
722
- )
723
- assert min_datetime <= max_datetime
724
- if isinstance(min_value, dt.datetime):
725
- assert min_datetime == min_value
726
- if isinstance(max_value, dt.datetime):
727
- assert max_datetime == max_value
728
-
729
-
730
705
  class TestMonths:
731
706
  @given(data=data())
732
707
  def test_main(self, *, data: DataObject) -> None:
@@ -1,28 +1,25 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
- from typing import TYPE_CHECKING, Any, cast, override
4
+ from typing import TYPE_CHECKING, Any, Literal, cast, override
5
5
 
6
6
  from hypothesis import given
7
- from hypothesis.strategies import DataObject, booleans, data, sampled_from, times
8
- from luigi import BoolParameter, Parameter, Task
7
+ from hypothesis.strategies import booleans
8
+ from luigi import BoolParameter, Task
9
+ from pytest import mark, param
9
10
 
10
- from utilities.hypothesis import namespace_mixins, temp_paths, zoned_datetimes
11
+ from utilities.hypothesis import namespace_mixins, temp_paths, zoned_datetimes_whenever
11
12
  from utilities.luigi import (
12
- DateHourParameter,
13
- DateMinuteParameter,
14
- DateSecondParameter,
15
13
  ExternalFile,
16
14
  ExternalTask,
17
15
  PathTarget,
18
- TimeParameter,
16
+ ZonedDateTimeParameter,
19
17
  _ExternalTaskDummyTarget,
20
18
  build,
21
19
  )
22
- from utilities.whenever import serialize_time, serialize_zoned_datetime
23
20
 
24
21
  if TYPE_CHECKING:
25
- import datetime as dt
22
+ from whenever import ZonedDateTime
26
23
 
27
24
 
28
25
  class TestBuild:
@@ -34,20 +31,17 @@ class TestBuild:
34
31
 
35
32
 
36
33
  class TestDateTimeParameter:
37
- @given(
38
- data=data(),
39
- param_cls=sampled_from([
40
- DateHourParameter,
41
- DateMinuteParameter,
42
- DateSecondParameter,
43
- ]),
44
- datetime=zoned_datetimes(),
45
- )
34
+ @given(datetime=zoned_datetimes_whenever())
35
+ @mark.parametrize("type_", [param("datetime"), param("str")])
46
36
  def test_main(
47
- self, *, data: DataObject, param_cls: type[Parameter], datetime: dt.datetime
37
+ self, *, datetime: ZonedDateTime, type_: Literal["datetime", "str"]
48
38
  ) -> None:
49
- param = param_cls()
50
- input_ = data.draw(sampled_from([datetime, serialize_zoned_datetime(datetime)]))
39
+ param = ZonedDateTimeParameter()
40
+ match type_:
41
+ case "datetime":
42
+ input_ = datetime
43
+ case "str":
44
+ input_ = datetime.format_common_iso()
51
45
  norm = param.normalize(input_)
52
46
  assert param.parse(param.serialize(norm)) == norm
53
47
 
@@ -90,12 +84,3 @@ class TestPathTarget:
90
84
  assert not target.exists()
91
85
  path.touch()
92
86
  assert target.exists()
93
-
94
-
95
- class TestTimeParameter:
96
- @given(data=data(), time=times())
97
- def test_main(self, *, data: DataObject, time: dt.time) -> None:
98
- param = TimeParameter()
99
- input_ = data.draw(sampled_from([time, serialize_time(time)]))
100
- norm = param.normalize(input_)
101
- assert param.parse(param.serialize(norm)) == time
@@ -0,0 +1,97 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass
3
+ from operator import eq
4
+ from pathlib import Path
5
+ from typing import TypeVar
6
+
7
+ from hypothesis import given
8
+ from hypothesis.strategies import DataObject, SearchStrategy, data, tuples
9
+ from pytest import mark, param
10
+ from typed_settings import FileLoader, TomlFormat, load_settings
11
+ from whenever import Date, DateDelta, PlainDateTime, Time, TimeDelta, ZonedDateTime
12
+
13
+ from utilities.hypothesis import (
14
+ date_deltas_whenever,
15
+ dates_whenever,
16
+ plain_datetimes_whenever,
17
+ temp_paths,
18
+ text_ascii,
19
+ time_deltas_whenever,
20
+ times_whenever,
21
+ zoned_datetimes_whenever,
22
+ )
23
+ from utilities.typed_settings import ExtendedTSConverter
24
+
25
+ app_names = text_ascii(min_size=1).map(str.lower)
26
+
27
+
28
+ _T = TypeVar("_T")
29
+
30
+
31
+ class TestExtendedTSConverter:
32
+ @given(data=data(), root=temp_paths(), appname=text_ascii(min_size=1))
33
+ @mark.parametrize(
34
+ ("test_cls", "strategy", "serialize"),
35
+ [
36
+ param(Date, dates_whenever(), Date.format_common_iso),
37
+ param(
38
+ DateDelta,
39
+ date_deltas_whenever(parsable=True),
40
+ DateDelta.format_common_iso,
41
+ ),
42
+ param(
43
+ PlainDateTime,
44
+ plain_datetimes_whenever(),
45
+ PlainDateTime.format_common_iso,
46
+ ),
47
+ param(Time, times_whenever(), Time.format_common_iso),
48
+ param(TimeDelta, time_deltas_whenever(), TimeDelta.format_common_iso),
49
+ param(
50
+ ZonedDateTime,
51
+ zoned_datetimes_whenever(),
52
+ ZonedDateTime.format_common_iso,
53
+ ),
54
+ ],
55
+ )
56
+ def test_main(
57
+ self,
58
+ *,
59
+ data: DataObject,
60
+ root: Path,
61
+ appname: str,
62
+ test_cls: type[_T],
63
+ strategy: SearchStrategy[_T],
64
+ serialize: Callable[[_T], str],
65
+ ) -> None:
66
+ default, value = data.draw(tuples(strategy, strategy))
67
+ self._run_test(test_cls, default, root, appname, serialize, value, eq)
68
+
69
+ def _run_test(
70
+ self,
71
+ test_cls: type[_T],
72
+ default: _T,
73
+ root: Path,
74
+ appname: str,
75
+ serialize: Callable[[_T], str],
76
+ value: _T,
77
+ equal: Callable[[_T, _T], bool],
78
+ /,
79
+ ) -> None:
80
+ @dataclass(frozen=True, kw_only=True, slots=True)
81
+ class Settings:
82
+ value: test_cls = default # pyright: ignore[reportInvalidTypeForm]
83
+
84
+ settings_default = load_settings(
85
+ Settings, loaders=[], converter=ExtendedTSConverter()
86
+ )
87
+ assert settings_default.value == default
88
+ _ = hash(settings_default)
89
+ file = Path(root, "file.toml")
90
+ with file.open(mode="w") as fh:
91
+ _ = fh.write(f'[{appname}]\nvalue = "{serialize(value)}"')
92
+ settings_loaded = load_settings(
93
+ Settings,
94
+ loaders=[FileLoader(formats={"*.toml": TomlFormat(appname)}, files=[file])],
95
+ converter=ExtendedTSConverter(),
96
+ )
97
+ assert equal(settings_loaded.value, value)
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.131.13"
3
+ __version__ = "0.131.15"
@@ -716,85 +716,6 @@ def lists_fixed_length(
716
716
  ##
717
717
 
718
718
 
719
- @composite
720
- def min_and_max_datetimes(
721
- draw: DrawFn,
722
- /,
723
- *,
724
- min_value: MaybeSearchStrategy[dt.datetime | None] = None,
725
- max_value: MaybeSearchStrategy[dt.datetime | None] = None,
726
- time_zone: MaybeSearchStrategy[ZoneInfo | timezone] = UTC,
727
- round_: MathRoundMode | None = None,
728
- timedelta: dt.timedelta | None = None,
729
- rel_tol: float | None = None,
730
- abs_tol: float | None = None,
731
- valid: bool = False,
732
- ) -> tuple[dt.datetime, dt.datetime]:
733
- """Strategy for generating min/max datetimes."""
734
- match min_value, max_value:
735
- case None, None:
736
- return draw(
737
- pairs(
738
- zoned_datetimes(
739
- time_zone=time_zone,
740
- round_=round_,
741
- timedelta=timedelta,
742
- rel_tol=rel_tol,
743
- abs_tol=abs_tol,
744
- valid=valid,
745
- ),
746
- sorted=True,
747
- )
748
- )
749
- case None, dt.datetime():
750
- min_value_ = draw(
751
- zoned_datetimes(
752
- max_value=max_value,
753
- time_zone=time_zone,
754
- round_=round_,
755
- timedelta=timedelta,
756
- rel_tol=rel_tol,
757
- abs_tol=abs_tol,
758
- valid=valid,
759
- )
760
- )
761
- return min_value_, max_value
762
- case dt.datetime(), None:
763
- max_value_ = draw(
764
- zoned_datetimes(
765
- min_value=min_value,
766
- time_zone=time_zone,
767
- round_=round_,
768
- timedelta=timedelta,
769
- rel_tol=rel_tol,
770
- abs_tol=abs_tol,
771
- valid=valid,
772
- )
773
- )
774
- return min_value, max_value_
775
- case dt.datetime(), dt.datetime():
776
- _ = assume(min_value <= max_value)
777
- return min_value, max_value
778
- case _, _:
779
- strategy = zoned_datetimes(
780
- time_zone=time_zone,
781
- round_=round_,
782
- timedelta=timedelta,
783
- rel_tol=rel_tol,
784
- abs_tol=abs_tol,
785
- valid=valid,
786
- )
787
- min_value_ = draw2(draw, min_value, strategy)
788
- max_value_ = draw2(draw, max_value, strategy)
789
- _ = assume(min_value_ <= max_value_)
790
- return min_value_, max_value_
791
- case _ as never:
792
- assert_never(never)
793
-
794
-
795
- ##
796
-
797
-
798
719
  @composite
799
720
  def months(
800
721
  draw: DrawFn,
@@ -1561,14 +1482,12 @@ __all__ = [
1561
1482
  "int64s",
1562
1483
  "int_arrays",
1563
1484
  "lists_fixed_length",
1564
- "min_and_max_datetimes",
1565
1485
  "months",
1566
1486
  "namespace_mixins",
1567
1487
  "numbers",
1568
1488
  "pairs",
1569
1489
  "paths",
1570
1490
  "plain_datetimes",
1571
- "plain_datetimes",
1572
1491
  "plain_datetimes_whenever",
1573
1492
  "random_states",
1574
1493
  "sentinels",
@@ -0,0 +1,195 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from pathlib import Path
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ Any,
8
+ Literal,
9
+ TypeVar,
10
+ assert_never,
11
+ cast,
12
+ overload,
13
+ override,
14
+ )
15
+
16
+ import luigi
17
+ from luigi import Parameter, PathParameter, Target, Task
18
+ from luigi import build as _build
19
+ from luigi.parameter import ParameterVisibility, _no_value
20
+ from whenever import ZonedDateTime
21
+
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Callable, Iterable
24
+
25
+ from luigi.execution_summary import LuigiRunResult
26
+
27
+ from utilities.types import DateTimeRoundUnit, LogLevel, PathLike, ZonedDateTimeLike
28
+
29
+
30
+ _T = TypeVar("_T")
31
+
32
+
33
+ # parameters
34
+
35
+
36
+ class ZonedDateTimeParameter(Parameter):
37
+ """A parameter which takes the value of a zoned datetime."""
38
+
39
+ _unit: DateTimeRoundUnit
40
+ _increment: int
41
+
42
+ @override
43
+ def __init__(
44
+ self,
45
+ default: Any = _no_value,
46
+ is_global: bool = False,
47
+ significant: bool = True,
48
+ description: str | None = None,
49
+ config_path: None = None,
50
+ positional: bool = True,
51
+ always_in_help: bool = False,
52
+ batch_method: Callable[[Iterable[_T]], _T] | None = None,
53
+ visibility: ParameterVisibility = ParameterVisibility.PUBLIC,
54
+ *,
55
+ unit: DateTimeRoundUnit = "second",
56
+ increment: int = 1,
57
+ ) -> None:
58
+ super().__init__(
59
+ default,
60
+ is_global,
61
+ significant,
62
+ description,
63
+ config_path,
64
+ positional,
65
+ always_in_help,
66
+ batch_method,
67
+ visibility,
68
+ )
69
+ self._unit = unit
70
+ self._increment = increment
71
+
72
+ @override
73
+ def normalize(self, x: ZonedDateTimeLike) -> ZonedDateTime:
74
+ match x:
75
+ case ZonedDateTime() as date_time:
76
+ ...
77
+ case str() as text:
78
+ date_time = ZonedDateTime.parse_common_iso(text)
79
+ case _ as never:
80
+ assert_never(never)
81
+ return date_time.round(self._unit, increment=self._increment, mode="floor")
82
+
83
+ @override
84
+ def parse(self, x: str) -> ZonedDateTime:
85
+ return ZonedDateTime.parse_common_iso(x)
86
+
87
+ @override
88
+ def serialize(self, x: ZonedDateTime) -> str:
89
+ return x.format_common_iso()
90
+
91
+
92
+ # targets
93
+
94
+
95
+ class PathTarget(Target):
96
+ """A local target whose `path` attribute is a Pathlib instance."""
97
+
98
+ def __init__(self, path: PathLike, /) -> None:
99
+ super().__init__()
100
+ self.path = Path(path)
101
+
102
+ @override
103
+ def exists(self) -> bool: # pyright: ignore[reportIncompatibleMethodOverride]
104
+ """Check if the target exists."""
105
+ return self.path.exists()
106
+
107
+
108
+ # tasks
109
+
110
+
111
+ class ExternalTask(ABC, luigi.ExternalTask):
112
+ """An external task with `exists()` defined here."""
113
+
114
+ @abstractmethod
115
+ def exists(self) -> bool:
116
+ """Predicate on which the external task is deemed to exist."""
117
+ msg = f"{self=}" # pragma: no cover
118
+ raise NotImplementedError(msg) # pragma: no cover
119
+
120
+ @override
121
+ def output(self) -> _ExternalTaskDummyTarget: # pyright: ignore[reportIncompatibleMethodOverride]
122
+ return _ExternalTaskDummyTarget(self)
123
+
124
+
125
+ class _ExternalTaskDummyTarget(Target):
126
+ """Dummy target for `ExternalTask`."""
127
+
128
+ def __init__(self, task: ExternalTask, /) -> None:
129
+ super().__init__()
130
+ self._task = task
131
+
132
+ @override
133
+ def exists(self) -> bool: # pyright: ignore[reportIncompatibleMethodOverride]
134
+ return self._task.exists()
135
+
136
+
137
+ class ExternalFile(ExternalTask):
138
+ """Await an external file on the local disk."""
139
+
140
+ path: Path = cast("Any", PathParameter())
141
+
142
+ @override
143
+ def exists(self) -> bool:
144
+ return self.path.exists()
145
+
146
+
147
+ # functions
148
+
149
+
150
+ @overload
151
+ def build(
152
+ task: Iterable[Task],
153
+ /,
154
+ *,
155
+ detailed_summary: Literal[False] = False,
156
+ local_scheduler: bool = False,
157
+ log_level: LogLevel | None = None,
158
+ workers: int | None = None,
159
+ ) -> bool: ...
160
+ @overload
161
+ def build(
162
+ task: Iterable[Task],
163
+ /,
164
+ *,
165
+ detailed_summary: Literal[True],
166
+ local_scheduler: bool = False,
167
+ log_level: LogLevel | None = None,
168
+ workers: int | None = None,
169
+ ) -> LuigiRunResult: ...
170
+ def build(
171
+ task: Iterable[Task],
172
+ /,
173
+ *,
174
+ detailed_summary: bool = False,
175
+ local_scheduler: bool = False,
176
+ log_level: LogLevel | None = None,
177
+ workers: int | None = None,
178
+ ) -> bool | LuigiRunResult:
179
+ """Build a set of tasks."""
180
+ return _build(
181
+ task,
182
+ detailed_summary=detailed_summary,
183
+ local_scheduler=local_scheduler,
184
+ **({} if log_level is None else {"log_level": log_level}),
185
+ **({} if workers is None else {"workers": workers}),
186
+ )
187
+
188
+
189
+ __all__ = [
190
+ "ExternalFile",
191
+ "ExternalTask",
192
+ "PathTarget",
193
+ "ZonedDateTimeParameter",
194
+ "build",
195
+ ]
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, TypeVar, override
4
+
5
+ from typed_settings.converters import TSConverter
6
+ from whenever import (
7
+ Date,
8
+ DateDelta,
9
+ DateTimeDelta,
10
+ PlainDateTime,
11
+ Time,
12
+ TimeDelta,
13
+ ZonedDateTime,
14
+ )
15
+
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Callable
18
+
19
+
20
+ _T = TypeVar("_T")
21
+
22
+
23
+ class ExtendedTSConverter(TSConverter):
24
+ """An extension of the TSConverter for custom types."""
25
+
26
+ @override
27
+ def __init__(
28
+ self,
29
+ *,
30
+ resolve_paths: bool = True,
31
+ strlist_sep: str | Callable[[str], list] | None = ":",
32
+ ) -> None:
33
+ super().__init__(resolve_paths=resolve_paths, strlist_sep=strlist_sep)
34
+ cases: list[tuple[type[Any], Callable[..., Any]]] = [
35
+ (Date, Date.parse_common_iso),
36
+ (DateDelta, DateDelta.parse_common_iso),
37
+ (DateTimeDelta, DateTimeDelta.parse_common_iso),
38
+ (PlainDateTime, PlainDateTime.parse_common_iso),
39
+ (Time, Time.parse_common_iso),
40
+ (TimeDelta, TimeDelta.parse_common_iso),
41
+ (ZonedDateTime, ZonedDateTime.parse_common_iso),
42
+ ]
43
+ extras = {cls: _make_converter(cls, func) for cls, func in cases}
44
+ self.scalar_converters |= extras
45
+
46
+
47
+ def _make_converter(
48
+ cls: type[_T], parser: Callable[[str], _T], /
49
+ ) -> Callable[[Any, type[Any]], Any]:
50
+ def hook(value: _T | str, _: type[_T] = cls, /) -> Any:
51
+ if not isinstance(value, (cls, str)): # pragma: no cover
52
+ msg = f"Invalid type {type(value).__name__!r}; expected '{cls.__name__}' or 'str'"
53
+ raise TypeError(msg)
54
+ if isinstance(value, str):
55
+ return parser(value)
56
+ return value
57
+
58
+ return hook
59
+
60
+
61
+ __all__ = ["ExtendedTSConverter"]