dycw-utilities 0.131.12__tar.gz → 0.131.14__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 (226) hide show
  1. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/PKG-INFO +1 -1
  2. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/pyproject.toml +4 -2
  3. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_atools.py +6 -5
  4. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_cachetools.py +22 -9
  5. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_hypothesis.py +0 -80
  6. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_orjson.py +2 -1
  7. dycw_utilities-0.131.14/src/tests/test_period.py +259 -0
  8. dycw_utilities-0.131.14/src/tests/test_typed_settings.py +97 -0
  9. dycw_utilities-0.131.14/src/tests/test_tzdata.py +14 -0
  10. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_zoneinfo.py +3 -5
  11. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/__init__.py +1 -1
  12. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/atools.py +7 -9
  13. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/cachetools.py +8 -10
  14. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/hypothesis.py +0 -217
  15. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/orjson.py +1 -1
  16. dycw_utilities-0.131.14/src/utilities/period.py +154 -0
  17. dycw_utilities-0.131.14/src/utilities/typed_settings.py +61 -0
  18. dycw_utilities-0.131.14/src/utilities/tzdata.py +11 -0
  19. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/whenever2.py +4 -3
  20. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/zoneinfo.py +2 -2
  21. dycw_utilities-0.131.12/src/tests/test_period.py +0 -387
  22. dycw_utilities-0.131.12/src/tests/test_tzdata.py +0 -60
  23. dycw_utilities-0.131.12/src/utilities/period.py +0 -324
  24. dycw_utilities-0.131.12/src/utilities/tzdata.py +0 -63
  25. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/.gitignore +0 -0
  26. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/LICENSE +0 -0
  27. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/README.md +0 -0
  28. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/__init__.py +0 -0
  29. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/conftest.py +0 -0
  30. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/__init__.py +0 -0
  31. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_missing/__init__.py +0 -0
  32. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_missing/module.py +0 -0
  33. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/__init__.py +0 -0
  34. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/outer_1.py +0 -0
  35. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/outer_2.py +0 -0
  36. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  37. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  38. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  39. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  40. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_without/__init__.py +0 -0
  41. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_without/module_1.py +0 -0
  42. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_without/module_2.py +0 -0
  43. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/standalone.py +0 -0
  44. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/with_imports.py +0 -0
  45. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  46. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  47. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  48. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  49. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  50. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  51. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  52. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  53. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_aiolimiter.py +0 -0
  54. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_altair.py +0 -0
  55. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio.py +0 -0
  56. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio_classes/__init__.py +0 -0
  57. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio_classes/loopers.py +0 -0
  58. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio_classes/redis.py +0 -0
  59. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_atomicwrites.py +0 -0
  60. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_click.py +0 -0
  61. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_concurrent.py +0 -0
  62. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_contextlib.py +0 -0
  63. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_contextvars.py +0 -0
  64. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_cryptography.py +0 -0
  65. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_cvxpy.py +0 -0
  66. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_dataclasses.py +0 -0
  67. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_datetime.py +0 -0
  68. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_enum.py +0 -0
  69. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_errors.py +0 -0
  70. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_eventkit.py +0 -0
  71. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_fastapi.py +0 -0
  72. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_fpdf2.py +0 -0
  73. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_functions.py +0 -0
  74. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_functools.py +0 -0
  75. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_getpass.py +0 -0
  76. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_git.py +0 -0
  77. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_hashlib.py +0 -0
  78. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_http.py +0 -0
  79. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_importlib.py +0 -0
  80. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_inflect.py +0 -0
  81. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_ipython.py +0 -0
  82. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_iterables.py +0 -0
  83. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_jupyter.py +0 -0
  84. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_libcst.py +0 -0
  85. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_lightweight_charts.py +0 -0
  86. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_logging.py +0 -0
  87. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_luigi.py +0 -0
  88. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_math.py +0 -0
  89. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_memory_profiler.py +0 -0
  90. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_modules.py +0 -0
  91. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_more_itertools.py +0 -0
  92. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_numpy.py +0 -0
  93. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_operator.py +0 -0
  94. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_optuna.py +0 -0
  95. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_os.py +0 -0
  96. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_parse.py +0 -0
  97. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pathlib.py +0 -0
  98. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pickle.py +0 -0
  99. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_platform.py +0 -0
  100. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_polars.py +0 -0
  101. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_polars_ols.py +0 -0
  102. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pottery.py +0 -0
  103. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pqdm.py +0 -0
  104. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_psutil.py +0 -0
  105. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pydantic.py +0 -0
  106. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pyinstrument.py +0 -0
  107. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pyrsistent.py +0 -0
  108. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pytest.py +0 -0
  109. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pytest_regressions.py +0 -0
  110. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_python_dotenv.py +0 -0
  111. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_random.py +0 -0
  112. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_re.py +0 -0
  113. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_redis.py +0 -0
  114. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_reprlib.py +0 -0
  115. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_scipy.py +0 -0
  116. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_sentinel.py +0 -0
  117. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_shelve.py +0 -0
  118. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_slack_sdk.py +0 -0
  119. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_socket.py +0 -0
  120. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_sqlalchemy.py +0 -0
  121. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_sqlalchemy_polars.py +0 -0
  122. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_statsmodel.py +0 -0
  123. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_streamlit.py +0 -0
  124. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_string.py +0 -0
  125. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_tempfile.py +0 -0
  126. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_tenacity.py +0 -0
  127. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_text.py +0 -0
  128. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_threading.py +0 -0
  129. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_timer.py +0 -0
  130. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_traceback.py +0 -0
  131. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_types.py +0 -0
  132. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing.py +0 -0
  133. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing_funcs/__init__.py +0 -0
  134. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing_funcs/no_future.py +0 -0
  135. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing_funcs/with_future.py +0 -0
  136. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_tzlocal.py +0 -0
  137. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_uuid.py +0 -0
  138. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_version.py +0 -0
  139. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_warnings.py +0 -0
  140. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_whenever.py +0 -0
  141. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_whenever2.py +0 -0
  142. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_zipfile.py +0 -0
  143. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/aiolimiter.py +0 -0
  144. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/altair.py +0 -0
  145. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/asyncio.py +0 -0
  146. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/atomicwrites.py +0 -0
  147. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/click.py +0 -0
  148. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/concurrent.py +0 -0
  149. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/contextlib.py +0 -0
  150. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/contextvars.py +0 -0
  151. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/cryptography.py +0 -0
  152. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/cvxpy.py +0 -0
  153. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/dataclasses.py +0 -0
  154. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/datetime.py +0 -0
  155. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/enum.py +0 -0
  156. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/errors.py +0 -0
  157. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/eventkit.py +0 -0
  158. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/fastapi.py +0 -0
  159. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/fpdf2.py +0 -0
  160. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/functions.py +0 -0
  161. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/functools.py +0 -0
  162. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/getpass.py +0 -0
  163. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/git.py +0 -0
  164. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/hashlib.py +0 -0
  165. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/http.py +0 -0
  166. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/importlib.py +0 -0
  167. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/inflect.py +0 -0
  168. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/ipython.py +0 -0
  169. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/iterables.py +0 -0
  170. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/jupyter.py +0 -0
  171. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/libcst.py +0 -0
  172. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/lightweight_charts.py +0 -0
  173. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/logging.py +0 -0
  174. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/luigi.py +0 -0
  175. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/math.py +0 -0
  176. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/memory_profiler.py +0 -0
  177. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/modules.py +0 -0
  178. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/more_itertools.py +0 -0
  179. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/numpy.py +0 -0
  180. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/operator.py +0 -0
  181. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/optuna.py +0 -0
  182. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/os.py +0 -0
  183. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/parse.py +0 -0
  184. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pathlib.py +0 -0
  185. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pickle.py +0 -0
  186. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/platform.py +0 -0
  187. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/polars.py +0 -0
  188. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/polars_ols.py +0 -0
  189. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pottery.py +0 -0
  190. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pqdm.py +0 -0
  191. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/psutil.py +0 -0
  192. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/py.typed +0 -0
  193. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pydantic.py +0 -0
  194. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pyinstrument.py +0 -0
  195. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pyrsistent.py +0 -0
  196. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pytest.py +0 -0
  197. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pytest_regressions.py +0 -0
  198. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/python_dotenv.py +0 -0
  199. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/random.py +0 -0
  200. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/re.py +0 -0
  201. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/redis.py +0 -0
  202. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/reprlib.py +0 -0
  203. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/scipy.py +0 -0
  204. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/sentinel.py +0 -0
  205. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/shelve.py +0 -0
  206. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/slack_sdk.py +0 -0
  207. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/socket.py +0 -0
  208. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/sqlalchemy.py +0 -0
  209. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/sqlalchemy_polars.py +0 -0
  210. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/statsmodels.py +0 -0
  211. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/streamlit.py +0 -0
  212. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/string.py +0 -0
  213. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/tempfile.py +0 -0
  214. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/tenacity.py +0 -0
  215. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/text.py +0 -0
  216. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/threading.py +0 -0
  217. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/timer.py +0 -0
  218. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/traceback.py +0 -0
  219. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/types.py +0 -0
  220. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/typing.py +0 -0
  221. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/tzlocal.py +0 -0
  222. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/uuid.py +0 -0
  223. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/version.py +0 -0
  224. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/warnings.py +0 -0
  225. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/whenever.py +0 -0
  226. {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/zipfile.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.131.12
3
+ Version: 0.131.14
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.12"
98
+ version = "0.131.14"
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.12"
125
+ current_version = "0.131.14"
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]
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from asyncio import sleep
4
-
3
+ from utilities.asyncio import sleep_dur
5
4
  from utilities.atools import call_memoized
5
+ from utilities.whenever2 import SECOND
6
6
 
7
7
 
8
8
  class TestCallMemoized:
@@ -20,6 +20,7 @@ class TestCallMemoized:
20
20
 
21
21
  async def test_refresh(self) -> None:
22
22
  counter = 0
23
+ delta = 0.05 * SECOND
23
24
 
24
25
  async def increment() -> int:
25
26
  nonlocal counter
@@ -27,9 +28,9 @@ class TestCallMemoized:
27
28
  return counter
28
29
 
29
30
  for _ in range(2):
30
- assert (await call_memoized(increment, 0.05)) == 1
31
+ assert (await call_memoized(increment, delta)) == 1
31
32
  assert counter == 1
32
- await sleep(0.1)
33
+ await sleep_dur(duration=2 * delta)
33
34
  for _ in range(2):
34
- assert (await call_memoized(increment, 0.05)) == 2
35
+ assert (await call_memoized(increment, delta)) == 2
35
36
  assert counter == 2
@@ -1,20 +1,32 @@
1
1
  from __future__ import annotations
2
2
 
3
- from time import sleep
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  from hypothesis import example, given
6
- from hypothesis.strategies import floats, integers, none
6
+ from hypothesis.strategies import integers, none
7
7
 
8
+ from utilities.asyncio import sleep_dur
8
9
  from utilities.cachetools import TTLSet, cache
10
+ from utilities.hypothesis import time_deltas_whenever
11
+ from utilities.whenever2 import SECOND
12
+
13
+ if TYPE_CHECKING:
14
+ from whenever import TimeDelta
9
15
 
10
16
 
11
17
  class TestCache:
12
18
  @example(max_size=None, max_duration=None)
13
- @example(max_size=None, max_duration=1.0)
19
+ @example(max_size=None, max_duration=SECOND)
14
20
  @example(max_size=1, max_duration=None)
15
- @example(max_size=1, max_duration=1.0)
16
- @given(max_size=integers(1, 10) | none(), max_duration=floats(0.1, 10.0) | none())
17
- def test_main(self, *, max_size: int, max_duration: float) -> None:
21
+ @example(max_size=1, max_duration=SECOND)
22
+ @given(
23
+ max_size=integers(1, 10) | none(),
24
+ max_duration=time_deltas_whenever(
25
+ min_value=0.1 * SECOND, max_value=10.0 * SECOND
26
+ )
27
+ | none(),
28
+ )
29
+ def test_main(self, *, max_size: int, max_duration: TimeDelta) -> None:
18
30
  counter = 0
19
31
 
20
32
  @cache(max_size=max_size, max_duration=max_duration)
@@ -48,10 +60,11 @@ class TestTTLSet:
48
60
  set_ = TTLSet(range(3))
49
61
  assert len(set_) == 3
50
62
 
51
- def test_max_duration(self) -> None:
52
- set_ = TTLSet(range(3), max_duration=0.1)
63
+ async def test_max_duration(self) -> None:
64
+ delta = 0.1 * SECOND
65
+ set_ = TTLSet(range(3), max_duration=delta)
53
66
  assert set_ == {0, 1, 2}
54
- sleep(0.2)
67
+ await sleep_dur(duration=2 * delta)
55
68
  assert set_ == set()
56
69
 
57
70
  def test_max_size(self) -> None:
@@ -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,9 +66,6 @@ from utilities.hypothesis import (
67
66
  int64s,
68
67
  int_arrays,
69
68
  lists_fixed_length,
70
- min_and_max_datetimes,
71
- min_and_maybe_max_datetimes,
72
- min_and_maybe_max_sizes,
73
69
  months,
74
70
  namespace_mixins,
75
71
  numbers,
@@ -706,82 +702,6 @@ class TestListsFixedLength:
706
702
  assert sorted(result) == result
707
703
 
708
704
 
709
- class TestMinAndMaxDateTimes:
710
- @given(
711
- data=data(),
712
- min_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
713
- max_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
714
- )
715
- def test_main(
716
- self,
717
- *,
718
- data: DataObject,
719
- min_value: MaybeSearchStrategy[dt.datetime | None],
720
- max_value: MaybeSearchStrategy[dt.datetime | None],
721
- ) -> None:
722
- min_datetime, max_datetime = data.draw(
723
- min_and_max_datetimes(min_value=min_value, max_value=max_value)
724
- )
725
- assert min_datetime <= max_datetime
726
- if isinstance(min_value, dt.datetime):
727
- assert min_datetime == min_value
728
- if isinstance(max_value, dt.datetime):
729
- assert max_datetime == max_value
730
-
731
-
732
- class TestMinAndMaybeMaxDateTimes:
733
- @given(
734
- data=data(),
735
- min_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
736
- max_value=zoned_datetimes()
737
- | none()
738
- | sentinels()
739
- | just(zoned_datetimes() | none() | sentinels()),
740
- )
741
- def test_main(
742
- self,
743
- *,
744
- data: DataObject,
745
- min_value: MaybeSearchStrategy[dt.datetime | None],
746
- max_value: MaybeSearchStrategy[dt.datetime | None | Sentinel],
747
- ) -> None:
748
- min_datetime, max_datetime = data.draw(
749
- min_and_maybe_max_datetimes(min_value=min_value, max_value=max_value)
750
- )
751
- assert (max_datetime is None) or (min_datetime <= max_datetime)
752
- if isinstance(min_value, dt.datetime):
753
- assert min_datetime == min_value
754
- if isinstance(max_value, dt.datetime) or (max_value is None):
755
- assert max_datetime == max_value
756
-
757
-
758
- class TestMinAndMaybeMaxSizes:
759
- @given(
760
- data=data(),
761
- min_value=integers(min_value=0) | none() | just(integers(min_value=0) | none()),
762
- max_value=integers(min_value=0)
763
- | none()
764
- | sentinels()
765
- | just(integers(min_value=0) | none() | sentinels()),
766
- )
767
- def test_main(
768
- self,
769
- *,
770
- data: DataObject,
771
- min_value: MaybeSearchStrategy[int | None],
772
- max_value: MaybeSearchStrategy[int | None | Sentinel],
773
- ) -> None:
774
- min_size, max_size = data.draw(
775
- min_and_maybe_max_sizes(min_value=min_value, max_value=max_value)
776
- )
777
- assert min_size >= 0
778
- assert (max_size is None) or (min_size <= max_size)
779
- if isinstance(min_value, int):
780
- assert min_size == min_value
781
- if isinstance(max_value, int) or (max_value is None):
782
- assert max_size == max_value
783
-
784
-
785
705
  class TestMonths:
786
706
  @given(data=data())
787
707
  def test_main(self, *, data: DataObject) -> None:
@@ -81,6 +81,7 @@ from utilities.polars import check_polars_dataframe, zoned_datetime
81
81
  from utilities.sentinel import Sentinel, sentinel
82
82
  from utilities.types import LogLevel, MaybeIterable, PathLike
83
83
  from utilities.typing import get_args
84
+ from utilities.tzlocal import LOCAL_TIME_ZONE
84
85
  from utilities.whenever2 import MINUTE, SECOND, get_now
85
86
 
86
87
  if TYPE_CHECKING:
@@ -169,7 +170,7 @@ class TestGetLogRecords:
169
170
  "level": UInt64,
170
171
  "path_name": String,
171
172
  "line_num": UInt64,
172
- "datetime": zoned_datetime(time_zone="local"),
173
+ "datetime": zoned_datetime(time_zone=LOCAL_TIME_ZONE),
173
174
  "func_name": String,
174
175
  "stack_info": String,
175
176
  "extra": Object,
@@ -0,0 +1,259 @@
1
+ from __future__ import annotations
2
+
3
+ from re import search
4
+ from typing import TYPE_CHECKING
5
+
6
+ from hypothesis import HealthCheck, given, settings
7
+ from hypothesis.strategies import DataObject, data, sampled_from
8
+ from pytest import raises
9
+
10
+ from utilities.hypothesis import (
11
+ assume_does_not_raise,
12
+ date_deltas_whenever,
13
+ dates_whenever,
14
+ pairs,
15
+ plain_datetimes_whenever,
16
+ time_deltas_whenever,
17
+ zoned_datetimes_whenever,
18
+ )
19
+ from utilities.period import (
20
+ DatePeriod,
21
+ ZonedDateTimePeriod,
22
+ _PeriodAsDict,
23
+ _PeriodInvalidError,
24
+ _PeriodTimeZoneError,
25
+ )
26
+ from utilities.tzdata import USCentral, USEastern
27
+ from utilities.whenever2 import DAY
28
+ from utilities.zoneinfo import UTC, get_time_zone_name
29
+
30
+ if TYPE_CHECKING:
31
+ from collections.abc import Callable
32
+
33
+ from whenever import Date, DateDelta, PlainDateTime, TimeDelta, ZonedDateTime
34
+
35
+
36
+ class TestDatePeriod:
37
+ @given(dates=pairs(dates_whenever(), sorted=True), delta=date_deltas_whenever())
38
+ @settings(suppress_health_check={HealthCheck.filter_too_much})
39
+ def test_add(self, *, dates: tuple[Date, Date], delta: DateDelta) -> None:
40
+ start, end = dates
41
+ period = DatePeriod(start, end)
42
+ with assume_does_not_raise(ValueError, match="Resulting date out of range"):
43
+ result = period + delta
44
+ expected = DatePeriod(start + delta, end + delta)
45
+ assert result == expected
46
+
47
+ @given(date=dates_whenever(), dates=pairs(dates_whenever(), sorted=True))
48
+ def test_contains(self, *, date: Date, dates: tuple[Date, Date]) -> None:
49
+ start, end = dates
50
+ period = DatePeriod(start, end)
51
+ result = date in period
52
+ expected = start <= date <= end
53
+ assert result is expected
54
+
55
+ @given(dates=pairs(dates_whenever(), sorted=True))
56
+ def test_delta(self, *, dates: tuple[Date, Date]) -> None:
57
+ start, end = dates
58
+ period = DatePeriod(start, end)
59
+ assert period.delta == (end - start)
60
+
61
+ @given(dates=pairs(dates_whenever(), sorted=True))
62
+ def test_hashable(self, *, dates: tuple[Date, Date]) -> None:
63
+ start, end = dates
64
+ period = DatePeriod(start, end)
65
+ _ = hash(period)
66
+
67
+ @given(dates=pairs(dates_whenever(), sorted=True), func=sampled_from([repr, str]))
68
+ def test_repr(self, *, dates: tuple[Date, Date], func: Callable[..., str]) -> None:
69
+ start, end = dates
70
+ period = DatePeriod(start, end)
71
+ result = func(period)
72
+ assert search(r"^DatePeriod\(\d{4}-\d{2}-\d{2}, \d{4}-\d{2}-\d{2}\)$", result)
73
+
74
+ @given(
75
+ dates1=pairs(dates_whenever(), sorted=True),
76
+ dates2=pairs(dates_whenever(), sorted=True),
77
+ )
78
+ def test_sortable(
79
+ self, *, dates1: tuple[Date, Date], dates2: tuple[Date, Date]
80
+ ) -> None:
81
+ start1, end1 = dates1
82
+ start2, end2 = dates2
83
+ period1 = DatePeriod(start1, end1)
84
+ period2 = DatePeriod(start2, end2)
85
+ _ = sorted([period1, period2])
86
+
87
+ @given(dates=pairs(dates_whenever(), sorted=True), delta=date_deltas_whenever())
88
+ @settings(suppress_health_check={HealthCheck.filter_too_much})
89
+ def test_sub(self, *, dates: tuple[Date, Date], delta: DateDelta) -> None:
90
+ start, end = dates
91
+ period = DatePeriod(start, end)
92
+ with assume_does_not_raise(ValueError, match="Resulting date out of range"):
93
+ result = period - delta
94
+ expected = DatePeriod(start - delta, end - delta)
95
+ assert result == expected
96
+
97
+ @given(dates=pairs(dates_whenever(), sorted=True))
98
+ def test_to_dict(self, *, dates: tuple[Date, Date]) -> None:
99
+ start, end = dates
100
+ period = DatePeriod(start, end)
101
+ result = period.to_dict()
102
+ expected = _PeriodAsDict(start=start, end=end)
103
+ assert result == expected
104
+
105
+ @given(dates=pairs(dates_whenever(), unique=True, sorted=True))
106
+ def test_error_period_invalid(self, *, dates: tuple[Date, Date]) -> None:
107
+ start, end = dates
108
+ with raises(_PeriodInvalidError, match="Invalid period; got .* > .*"):
109
+ _ = DatePeriod(end, start)
110
+
111
+
112
+ class TestZonedDateTimePeriod:
113
+ @given(
114
+ datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
115
+ delta=time_deltas_whenever(),
116
+ )
117
+ @settings(suppress_health_check={HealthCheck.filter_too_much})
118
+ def test_add(
119
+ self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime], delta: TimeDelta
120
+ ) -> None:
121
+ start, end = datetimes
122
+ period = ZonedDateTimePeriod(start, end)
123
+ with assume_does_not_raise(ValueError, match="Instant is out of range"):
124
+ result = period + delta
125
+ expected = ZonedDateTimePeriod(start + delta, end + delta)
126
+ assert result == expected
127
+
128
+ @given(
129
+ datetime=zoned_datetimes_whenever(),
130
+ datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
131
+ )
132
+ def test_contains(
133
+ self, *, datetime: ZonedDateTime, datetimes: tuple[ZonedDateTime, ZonedDateTime]
134
+ ) -> None:
135
+ start, end = datetimes
136
+ period = ZonedDateTimePeriod(start, end)
137
+ result = datetime in period
138
+ expected = start <= datetime <= end
139
+ assert result is expected
140
+
141
+ @given(
142
+ datetime=zoned_datetimes_whenever(),
143
+ datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
144
+ )
145
+ def test_contain_datetime(
146
+ self, *, datetime: ZonedDateTime, datetimes: tuple[ZonedDateTime, ZonedDateTime]
147
+ ) -> None:
148
+ start, end = datetimes
149
+ period = ZonedDateTimePeriod(start, end)
150
+ result = datetime in period
151
+ expected = start <= datetime <= end
152
+ assert result is expected
153
+
154
+ @given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
155
+ def test_delta(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
156
+ start, end = datetimes
157
+ period = ZonedDateTimePeriod(start, end)
158
+ assert period.delta == (end - start)
159
+
160
+ @given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
161
+ def test_hashable(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
162
+ start, end = datetimes
163
+ period = ZonedDateTimePeriod(start, end)
164
+ _ = hash(period)
165
+
166
+ @given(
167
+ data=data(),
168
+ datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
169
+ func=sampled_from([repr, str]),
170
+ )
171
+ def test_repr(
172
+ self,
173
+ *,
174
+ data: DataObject,
175
+ datetimes: tuple[ZonedDateTime, ZonedDateTime],
176
+ func: Callable[..., str],
177
+ ) -> None:
178
+ start, end = datetimes
179
+ datetimes = data.draw(pairs(zoned_datetimes_whenever(), sorted=True))
180
+ start, end = datetimes
181
+ period = ZonedDateTimePeriod(start, end)
182
+ result = func(period)
183
+ assert search(
184
+ r"^ZonedDateTimePeriod\(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?, \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?\[.+\]\)$",
185
+ result,
186
+ )
187
+
188
+ @given(
189
+ dates1=pairs(zoned_datetimes_whenever(), sorted=True),
190
+ dates2=pairs(zoned_datetimes_whenever(), sorted=True),
191
+ )
192
+ def test_sortable(
193
+ self,
194
+ *,
195
+ dates1: tuple[ZonedDateTime, ZonedDateTime],
196
+ dates2: tuple[ZonedDateTime, ZonedDateTime],
197
+ ) -> None:
198
+ start1, end1 = dates1
199
+ start2, end2 = dates2
200
+ period1 = ZonedDateTimePeriod(start1, end1)
201
+ period2 = ZonedDateTimePeriod(start2, end2)
202
+ _ = sorted([period1, period2])
203
+
204
+ @given(
205
+ datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
206
+ delta=time_deltas_whenever(),
207
+ )
208
+ @settings(suppress_health_check={HealthCheck.filter_too_much})
209
+ def test_sub(
210
+ self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime], delta: TimeDelta
211
+ ) -> None:
212
+ start, end = datetimes
213
+ period = ZonedDateTimePeriod(start, end)
214
+ with assume_does_not_raise(ValueError, match="Instant is out of range"):
215
+ result = period - delta
216
+ expected = ZonedDateTimePeriod(start - delta, end - delta)
217
+ assert result == expected
218
+
219
+ @given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
220
+ def test_to_dict(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
221
+ start, end = datetimes
222
+ period = ZonedDateTimePeriod(start, end)
223
+ result = period.to_dict()
224
+ expected = _PeriodAsDict(start=start, end=end)
225
+ assert result == expected
226
+
227
+ @given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
228
+ def test_to_tz(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
229
+ start, end = datetimes
230
+ period = ZonedDateTimePeriod(start, end)
231
+ with assume_does_not_raise(OverflowError, match="date value out of range"):
232
+ result = period.to_tz(UTC)
233
+ assert result.time_zone == UTC
234
+ name = get_time_zone_name(UTC)
235
+ expected = ZonedDateTimePeriod(start.to_tz(name), end.to_tz(name))
236
+ assert result == expected
237
+
238
+ @given(datetimes=pairs(zoned_datetimes_whenever(), unique=True, sorted=True))
239
+ @settings(suppress_health_check={HealthCheck.filter_too_much})
240
+ def test_error_period_invalid(
241
+ self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]
242
+ ) -> None:
243
+ start, end = datetimes
244
+ with raises(_PeriodInvalidError, match="Invalid period; got .* > .*"):
245
+ _ = ZonedDateTimePeriod(end, start)
246
+
247
+ @given(datetimes=pairs(plain_datetimes_whenever(), sorted=True))
248
+ def test_error_period_time_zone(
249
+ self, *, datetimes: tuple[PlainDateTime, PlainDateTime]
250
+ ) -> None:
251
+ plain_start, plain_end = datetimes
252
+ with assume_does_not_raise(OverflowError, match="date value out of range"):
253
+ start = (plain_start - DAY).assume_tz(USCentral.key)
254
+ end = (plain_end + DAY).assume_tz(USEastern.key)
255
+ with raises(
256
+ _PeriodTimeZoneError,
257
+ match="Period must contain exactly one time zone; got .* and .*",
258
+ ):
259
+ _ = ZonedDateTimePeriod(start, end)
@@ -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)
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from zoneinfo import ZoneInfo
4
+
5
+ from hypothesis import given
6
+ from hypothesis.strategies import sampled_from
7
+
8
+ from utilities.tzdata import HongKong, Tokyo, USCentral, USEastern
9
+
10
+
11
+ class TestTimeZones:
12
+ @given(time_zone=sampled_from([HongKong, Tokyo, USCentral, USEastern]))
13
+ def test_main(self, *, time_zone: ZoneInfo) -> None:
14
+ assert isinstance(time_zone, ZoneInfo)
@@ -10,7 +10,7 @@ from pytest import raises
10
10
 
11
11
  from utilities.hypothesis import zoned_datetimes
12
12
  from utilities.tzdata import HongKong, Tokyo
13
- from utilities.tzlocal import get_local_time_zone
13
+ from utilities.tzlocal import LOCAL_TIME_ZONE, LOCAL_TIME_ZONE_NAME
14
14
  from utilities.zoneinfo import (
15
15
  UTC,
16
16
  _EnsureTimeZoneInvalidTZInfoError,
@@ -45,8 +45,7 @@ class TestEnsureTimeZone:
45
45
 
46
46
  def test_local(self) -> None:
47
47
  result = ensure_time_zone("local")
48
- expected = get_local_time_zone()
49
- assert result is expected
48
+ assert result is LOCAL_TIME_ZONE
50
49
 
51
50
  @given(data=data(), time_zone=timezones())
52
51
  def test_zoned_datetime(self, *, data: DataObject, time_zone: ZoneInfo) -> None:
@@ -78,8 +77,7 @@ class TestGetTimeZoneName:
78
77
 
79
78
  def test_local(self) -> None:
80
79
  result = get_time_zone_name("local")
81
- expected = get_local_time_zone().key
82
- assert result is expected
80
+ assert result == LOCAL_TIME_ZONE_NAME
83
81
 
84
82
 
85
83
  class TestTimeZones:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.131.12"
3
+ __version__ = "0.131.14"