dycw-utilities 0.133.0__tar.gz → 0.133.1__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 (218) hide show
  1. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/PKG-INFO +1 -1
  2. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/pyproject.toml +4 -2
  3. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_click.py +3 -0
  4. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_hypothesis.py +13 -1
  5. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_typed_settings.py +3 -0
  6. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_whenever.py +74 -0
  7. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/__init__.py +1 -1
  8. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/click.py +26 -1
  9. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/hypothesis.py +36 -0
  10. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/typed_settings.py +2 -0
  11. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/whenever.py +129 -2
  12. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/.gitignore +0 -0
  13. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/LICENSE +0 -0
  14. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/README.md +0 -0
  15. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/__init__.py +0 -0
  16. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/conftest.py +0 -0
  17. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/__init__.py +0 -0
  18. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_missing/__init__.py +0 -0
  19. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_missing/module.py +0 -0
  20. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/__init__.py +0 -0
  21. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/outer_1.py +0 -0
  22. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/outer_2.py +0 -0
  23. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  24. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  25. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  26. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  27. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_without/__init__.py +0 -0
  28. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_without/module_1.py +0 -0
  29. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/package_without/module_2.py +0 -0
  30. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/standalone.py +0 -0
  31. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/modules/with_imports.py +0 -0
  32. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  33. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  34. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  35. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  36. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  37. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  38. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  39. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  40. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_aiolimiter.py +0 -0
  41. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_altair.py +0 -0
  42. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_arq.py +0 -0
  43. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_asyncio.py +0 -0
  44. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_asyncio_classes/__init__.py +0 -0
  45. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_asyncio_classes/loopers.py +0 -0
  46. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_asyncio_classes/redis.py +0 -0
  47. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_atomicwrites.py +0 -0
  48. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_atools.py +0 -0
  49. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_cachetools.py +0 -0
  50. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_concurrent.py +0 -0
  51. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_contextlib.py +0 -0
  52. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_contextvars.py +0 -0
  53. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_cryptography.py +0 -0
  54. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_cvxpy.py +0 -0
  55. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_dataclasses.py +0 -0
  56. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_enum.py +0 -0
  57. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_errors.py +0 -0
  58. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_eventkit.py +0 -0
  59. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_fastapi.py +0 -0
  60. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_fpdf2.py +0 -0
  61. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_functions.py +0 -0
  62. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_functools.py +0 -0
  63. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_getpass.py +0 -0
  64. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_git.py +0 -0
  65. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_hashlib.py +0 -0
  66. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_http.py +0 -0
  67. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_importlib.py +0 -0
  68. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_inflect.py +0 -0
  69. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_ipython.py +0 -0
  70. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_iterables.py +0 -0
  71. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_jupyter.py +0 -0
  72. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_libcst.py +0 -0
  73. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_lightweight_charts.py +0 -0
  74. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_logging.py +0 -0
  75. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_luigi.py +0 -0
  76. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_math.py +0 -0
  77. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_memory_profiler.py +0 -0
  78. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_modules.py +0 -0
  79. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_more_itertools.py +0 -0
  80. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_numpy.py +0 -0
  81. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_objects/__init__.py +0 -0
  82. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_objects/objects.py +0 -0
  83. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_operator.py +0 -0
  84. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_optuna.py +0 -0
  85. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_orjson.py +0 -0
  86. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_os.py +0 -0
  87. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_parse.py +0 -0
  88. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pathlib.py +0 -0
  89. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_period.py +0 -0
  90. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pickle.py +0 -0
  91. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_platform.py +0 -0
  92. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_polars.py +0 -0
  93. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_polars_ols.py +0 -0
  94. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pottery.py +0 -0
  95. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pqdm.py +0 -0
  96. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_psutil.py +0 -0
  97. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pydantic.py +0 -0
  98. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pyinstrument.py +0 -0
  99. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pytest.py +0 -0
  100. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_pytest_regressions.py +0 -0
  101. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_python_dotenv.py +0 -0
  102. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_random.py +0 -0
  103. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_re.py +0 -0
  104. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_redis.py +0 -0
  105. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_reprlib.py +0 -0
  106. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_scipy.py +0 -0
  107. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_sentinel.py +0 -0
  108. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_shelve.py +0 -0
  109. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_slack_sdk.py +0 -0
  110. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_socket.py +0 -0
  111. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_sqlalchemy.py +0 -0
  112. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_statsmodels.py +0 -0
  114. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_string.py +0 -0
  116. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_tempfile.py +0 -0
  117. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_text.py +0 -0
  118. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_threading.py +0 -0
  119. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_timer.py +0 -0
  120. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_traceback.py +0 -0
  121. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_types.py +0 -0
  122. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_typing.py +0 -0
  123. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_typing_funcs/__init__.py +0 -0
  124. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_typing_funcs/no_future.py +0 -0
  125. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_typing_funcs/with_future.py +0 -0
  126. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_tzdata.py +0 -0
  127. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_tzlocal.py +0 -0
  128. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_uuid.py +0 -0
  129. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_version.py +0 -0
  130. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_warnings.py +0 -0
  131. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_zipfile.py +0 -0
  132. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/tests/test_zoneinfo.py +0 -0
  133. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/aiolimiter.py +0 -0
  134. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/altair.py +0 -0
  135. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/arq.py +0 -0
  136. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/asyncio.py +0 -0
  137. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/concurrent.py +0 -0
  141. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/contextlib.py +0 -0
  142. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/contextvars.py +0 -0
  143. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/cryptography.py +0 -0
  144. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/cvxpy.py +0 -0
  145. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/dataclasses.py +0 -0
  146. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/enum.py +0 -0
  147. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/errors.py +0 -0
  148. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/eventkit.py +0 -0
  149. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/fastapi.py +0 -0
  150. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/fpdf2.py +0 -0
  151. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/functions.py +0 -0
  152. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/functools.py +0 -0
  153. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/getpass.py +0 -0
  154. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/git.py +0 -0
  155. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/hashlib.py +0 -0
  156. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/http.py +0 -0
  157. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/importlib.py +0 -0
  158. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/inflect.py +0 -0
  159. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/ipython.py +0 -0
  160. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/iterables.py +0 -0
  161. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/jupyter.py +0 -0
  162. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/libcst.py +0 -0
  163. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/lightweight_charts.py +0 -0
  164. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/logging.py +0 -0
  165. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/luigi.py +0 -0
  166. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/math.py +0 -0
  167. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/memory_profiler.py +0 -0
  168. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/modules.py +0 -0
  169. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/more_itertools.py +0 -0
  170. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/numpy.py +0 -0
  171. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/operator.py +0 -0
  172. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/optuna.py +0 -0
  173. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/orjson.py +0 -0
  174. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/os.py +0 -0
  175. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/parse.py +0 -0
  176. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pathlib.py +0 -0
  177. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/period.py +0 -0
  178. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pickle.py +0 -0
  179. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/platform.py +0 -0
  180. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/polars.py +0 -0
  181. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/polars_ols.py +0 -0
  182. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pottery.py +0 -0
  183. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pqdm.py +0 -0
  184. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/psutil.py +0 -0
  185. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/py.typed +0 -0
  186. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pydantic.py +0 -0
  187. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pyinstrument.py +0 -0
  188. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pytest.py +0 -0
  189. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/pytest_regressions.py +0 -0
  190. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/python_dotenv.py +0 -0
  191. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/random.py +0 -0
  192. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/re.py +0 -0
  193. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/redis.py +0 -0
  194. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/reprlib.py +0 -0
  195. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/scipy.py +0 -0
  196. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/sentinel.py +0 -0
  197. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/shelve.py +0 -0
  198. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/slack_sdk.py +0 -0
  199. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/socket.py +0 -0
  200. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/sqlalchemy.py +0 -0
  201. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/sqlalchemy_polars.py +0 -0
  202. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/statsmodels.py +0 -0
  203. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/streamlit.py +0 -0
  204. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/string.py +0 -0
  205. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/tempfile.py +0 -0
  206. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/text.py +0 -0
  207. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/threading.py +0 -0
  208. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/timer.py +0 -0
  209. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/traceback.py +0 -0
  210. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/types.py +0 -0
  211. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/typing.py +0 -0
  212. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/tzdata.py +0 -0
  213. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/tzlocal.py +0 -0
  214. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/uuid.py +0 -0
  215. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/version.py +0 -0
  216. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/warnings.py +0 -0
  217. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/zipfile.py +0 -0
  218. {dycw_utilities-0.133.0 → dycw_utilities-0.133.1}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.133.0
3
+ Version: 0.133.1
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -30,6 +30,7 @@ dataclasses-test = ["orjson", "polars-lts-cpu"]
30
30
  dev = [
31
31
  "coloredlogs >= 15.0.1, < 15.1",
32
32
  "coverage-conditional-plugin >= 0.9.0, < 0.10",
33
+ "dycw-pytest-only >= 2.1.1, < 2.2",
33
34
  "pyright[nodejs] >= 1.1.401, < 1.2",
34
35
  "pytest-cov >= 6.1.1, < 6.2",
35
36
  ]
@@ -103,7 +104,7 @@ dependencies = [
103
104
  name = "dycw-utilities"
104
105
  readme = "README.md"
105
106
  requires-python = ">= 3.12"
106
- version = "0.133.0"
107
+ version = "0.133.1"
107
108
 
108
109
  [project.optional-dependencies]
109
110
  logging = [
@@ -129,7 +130,7 @@ test = [
129
130
  # bump-my-version
130
131
  [tool.bumpversion]
131
132
  allow_dirty = true
132
- current_version = "0.133.0"
133
+ current_version = "0.133.1"
133
134
 
134
135
  [[tool.bumpversion.files]]
135
136
  filename = "src/utilities/__init__.py"
@@ -238,6 +239,7 @@ filterwarnings = [
238
239
  "error",
239
240
  "ignore:.*utcfromtimestamp.* is deprecated and scheduled for removal in a future version:DeprecationWarning", # luigi
240
241
  "ignore:Exception ignored in.* <coroutine object .* at .*>:pytest.PytestUnraisableExceptionWarning",
242
+ "ignore:Exception in thread Thread-.*:pytest.PytestUnhandledThreadExceptionWarning",
241
243
  "ignore:Implicitly cleaning up <TemporaryDirectory '.*'>:ResourceWarning",
242
244
  "ignore:ResourceTracker called reentrantly for resource cleanup, which is unsupported:UserWarning",
243
245
  "ignore:Task .* without outputs has no custom complete.* method:UserWarning", # luigi
@@ -34,6 +34,7 @@ from utilities.click import (
34
34
  ExistingDirPath,
35
35
  ExistingFilePath,
36
36
  FilePath,
37
+ Freq,
37
38
  FrozenSetChoices,
38
39
  FrozenSetEnums,
39
40
  FrozenSetStrs,
@@ -51,6 +52,7 @@ from utilities.hypothesis import (
51
52
  date_deltas,
52
53
  date_time_deltas,
53
54
  dates,
55
+ freqs,
54
56
  months,
55
57
  pairs,
56
58
  plain_datetimes,
@@ -209,6 +211,7 @@ class TestParameters:
209
211
  attrgetter("name"),
210
212
  True,
211
213
  ),
214
+ param(Freq(), "FREQ", freqs(), utilities.whenever.Freq.serialize, True),
212
215
  param(
213
216
  FrozenSetChoices(["a", "b", "c"]),
214
217
  "FROZENSET[Choice(['a', 'b', 'c'])]",
@@ -41,6 +41,7 @@ from utilities.hypothesis import (
41
41
  Shape,
42
42
  _Draw2DefaultGeneratedSentinelError,
43
43
  _Draw2InputResolvedToSentinelError,
44
+ _freq_units,
44
45
  assume_does_not_raise,
45
46
  bool_arrays,
46
47
  date_deltas,
@@ -51,6 +52,7 @@ from utilities.hypothesis import (
51
52
  float64s,
52
53
  float_arrays,
53
54
  floats_extra,
55
+ freqs,
54
56
  git_repos,
55
57
  hashables,
56
58
  int32s,
@@ -107,6 +109,7 @@ from utilities.version import Version
107
109
  from utilities.whenever import (
108
110
  DATE_TWO_DIGIT_YEAR_MAX,
109
111
  DATE_TWO_DIGIT_YEAR_MIN,
112
+ Freq,
110
113
  Month,
111
114
  to_days,
112
115
  to_nanos,
@@ -118,7 +121,7 @@ if TYPE_CHECKING:
118
121
  from zoneinfo import ZoneInfo
119
122
 
120
123
  from utilities.tempfile import TemporaryDirectory
121
- from utilities.types import Number
124
+ from utilities.types import DateTimeRoundUnit, Number
122
125
 
123
126
 
124
127
  class TestAssumeDoesNotRaise:
@@ -486,6 +489,15 @@ class TestFloatsExtra:
486
489
  assert x == round(x)
487
490
 
488
491
 
492
+ class TestFreqs:
493
+ @given(data=data(), unit=_freq_units() | none())
494
+ def test_main(self, *, data: DataObject, unit: DateTimeRoundUnit | None) -> None:
495
+ freq = data.draw(freqs(unit=unit))
496
+ assert isinstance(freq, Freq)
497
+ if unit is not None:
498
+ assert freq.unit == unit
499
+
500
+
489
501
  class TestGitRepos:
490
502
  @given(data=data())
491
503
  @settings_with_reduced_examples()
@@ -23,6 +23,7 @@ from utilities.hypothesis import (
23
23
  date_deltas,
24
24
  date_time_deltas,
25
25
  dates,
26
+ freqs,
26
27
  plain_datetimes,
27
28
  temp_paths,
28
29
  text_ascii,
@@ -37,6 +38,7 @@ from utilities.typed_settings import (
37
38
  LoadSettingsError,
38
39
  load_settings,
39
40
  )
41
+ from utilities.whenever import Freq
40
42
 
41
43
  app_names = text_ascii(min_size=1).map(str.lower)
42
44
 
@@ -56,6 +58,7 @@ class TestExtendedTSConverter:
56
58
  date_time_deltas(parsable=True),
57
59
  DateTimeDelta.format_common_iso,
58
60
  ),
61
+ param(Freq, freqs(), Freq.serialize),
59
62
  param(IPv4Address, ip_addresses(v=4), IPv4Address),
60
63
  param(IPv6Address, ip_addresses(v=6), IPv6Address),
61
64
  param(PlainDateTime, plain_datetimes(), PlainDateTime.format_common_iso),
@@ -30,12 +30,15 @@ from utilities.hypothesis import (
30
30
  assume_does_not_raise,
31
31
  date_deltas,
32
32
  dates,
33
+ freqs,
33
34
  months,
34
35
  pairs,
35
36
  sentinels,
36
37
  zoned_datetimes,
37
38
  )
38
39
  from utilities.sentinel import Sentinel, sentinel
40
+ from utilities.types import DateTimeRoundUnit
41
+ from utilities.typing import get_literal_elements
39
42
  from utilities.tzdata import HongKong, Tokyo
40
43
  from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
41
44
  from utilities.whenever import (
@@ -64,12 +67,16 @@ from utilities.whenever import (
64
67
  ZERO_DAYS,
65
68
  ZONED_DATE_TIME_MAX,
66
69
  ZONED_DATE_TIME_MIN,
70
+ Freq,
67
71
  MeanDateTimeError,
68
72
  MinMaxDateError,
69
73
  Month,
70
74
  ToDaysError,
71
75
  ToNanosError,
72
76
  WheneverLogRecord,
77
+ _FreqDayIncrementError,
78
+ _FreqIncrementError,
79
+ _FreqParseError,
73
80
  _MinMaxDateMaxDateError,
74
81
  _MinMaxDateMinDateError,
75
82
  _MinMaxDatePeriodError,
@@ -128,6 +135,73 @@ class TestFormatCompact:
128
135
  assert parsed == expected
129
136
 
130
137
 
138
+ class TestFreq:
139
+ @given(freq=freqs())
140
+ def test_main(self, *, freq: Freq) -> None:
141
+ _ = get_now().round(unit=freq.unit, increment=freq.increment, mode="floor")
142
+
143
+ @given(unit=sampled_from(get_literal_elements(DateTimeRoundUnit)))
144
+ def test_abbreviate_and_expand(self, *, unit: DateTimeRoundUnit) -> None:
145
+ result = Freq._expand(Freq._abbreviate(unit))
146
+ assert result == unit
147
+
148
+ @given(freqs=pairs(freqs()))
149
+ def test_eq(self, *, freqs: tuple[Freq, Freq]) -> None:
150
+ x, y = freqs
151
+ result = x == y
152
+ assert isinstance(result, bool)
153
+
154
+ @given(freq=freqs())
155
+ def test_eq_non_freq(self, *, freq: Freq) -> None:
156
+ result = freq == 0
157
+ assert not result
158
+
159
+ @given(freq=freqs())
160
+ def test_hashable(self, *, freq: Freq) -> None:
161
+ _ = hash(freq)
162
+
163
+ @given(freq=freqs())
164
+ def test_repr(self, *, freq: Freq) -> None:
165
+ _ = repr(freq)
166
+
167
+ @given(freq=freqs())
168
+ def test_serialize_and_parse(self, *, freq: Freq) -> None:
169
+ result = Freq.parse(freq.serialize())
170
+ assert result == freq
171
+
172
+ def test_error_day(self) -> None:
173
+ with raises(
174
+ _FreqDayIncrementError,
175
+ match="Increment must be 1 for the 'day' unit; got 2",
176
+ ):
177
+ _ = Freq(unit="day", increment=2)
178
+
179
+ def test_error_hour(self) -> None:
180
+ with raises(
181
+ _FreqIncrementError,
182
+ match="Increment must be a proper divisor of 24 for the 'hour' unit; got 5",
183
+ ):
184
+ _ = Freq(unit="hour", increment=5)
185
+
186
+ def test_error_minute(self) -> None:
187
+ with raises(
188
+ _FreqIncrementError,
189
+ match="Increment must be a proper divisor of 60 for the 'minute' unit; got 7",
190
+ ):
191
+ _ = Freq(unit="minute", increment=7)
192
+
193
+ def test_error_milliseond(self) -> None:
194
+ with raises(
195
+ _FreqIncrementError,
196
+ match="Increment must be a proper divisor of 1000 for the 'millisecond' unit; got 3",
197
+ ):
198
+ _ = Freq(unit="millisecond", increment=3)
199
+
200
+ def test_error_parse(self) -> None:
201
+ with raises(_FreqParseError, match="Unable to parse frequency; got 's'"):
202
+ _ = Freq.parse("s")
203
+
204
+
131
205
  class TestFromTimeStamp:
132
206
  @given(
133
207
  datetime=zoned_datetimes(time_zone=timezones()).map(lambda d: d.round("second"))
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.133.0"
3
+ __version__ = "0.133.1"
@@ -29,7 +29,7 @@ from utilities.types import (
29
29
  TimeLike,
30
30
  ZonedDateTimeLike,
31
31
  )
32
- from utilities.whenever import _MonthParseCommonISOError
32
+ from utilities.whenever import FreqLike, _FreqParseError, _MonthParseCommonISOError
33
33
 
34
34
  if TYPE_CHECKING:
35
35
  from collections.abc import Iterable, Sequence
@@ -177,6 +177,30 @@ class Enum(ParamType, Generic[TEnum]):
177
177
  return _make_metavar(param, desc)
178
178
 
179
179
 
180
+ class Freq(ParamType):
181
+ """An frequency-valued parameter."""
182
+
183
+ @override
184
+ def __repr__(self) -> str:
185
+ return "FREQ"
186
+
187
+ @override
188
+ def convert(
189
+ self, value: FreqLike, param: Parameter | None, ctx: Context | None
190
+ ) -> utilities.whenever.Freq:
191
+ """Convert a value into the `Freq` type."""
192
+ match value:
193
+ case utilities.whenever.Freq():
194
+ return value
195
+ case str():
196
+ try:
197
+ return utilities.whenever.Freq.parse(value)
198
+ except _FreqParseError as error:
199
+ self.fail(str(error), param, ctx)
200
+ case _ as never:
201
+ assert_never(never)
202
+
203
+
180
204
  class IPv4Address(ParamType):
181
205
  """An IPv4 address-valued parameter."""
182
206
 
@@ -519,6 +543,7 @@ __all__ = [
519
543
  "ExistingDirPath",
520
544
  "ExistingFilePath",
521
545
  "FilePath",
546
+ "Freq",
522
547
  "FrozenSetChoices",
523
548
  "FrozenSetEnums",
524
549
  "FrozenSetParameter",
@@ -77,6 +77,8 @@ from utilities.pathlib import temp_cwd
77
77
  from utilities.platform import IS_WINDOWS
78
78
  from utilities.sentinel import Sentinel, sentinel
79
79
  from utilities.tempfile import TEMP_DIR, TemporaryDirectory
80
+ from utilities.types import DateTimeRoundUnit
81
+ from utilities.typing import get_literal_elements
80
82
  from utilities.version import Version
81
83
  from utilities.whenever import (
82
84
  DATE_DELTA_MAX,
@@ -100,6 +102,7 @@ from utilities.whenever import (
100
102
  TIME_DELTA_MIN,
101
103
  TIME_MAX,
102
104
  TIME_MIN,
105
+ Freq,
103
106
  Month,
104
107
  to_date_time_delta,
105
108
  to_days,
@@ -502,6 +505,38 @@ def floats_extra(
502
505
  ##
503
506
 
504
507
 
508
+ @composite
509
+ def freqs(
510
+ draw: DrawFn, /, *, unit: MaybeSearchStrategy[DateTimeRoundUnit | None] = None
511
+ ) -> Freq:
512
+ unit_ = draw2(draw, unit, _freq_units())
513
+ match unit_:
514
+ case "day":
515
+ return Freq(unit=unit_)
516
+ case "hour":
517
+ return Freq(unit=unit_, increment=draw(_freq_increments(24)))
518
+ case "minute" | "second":
519
+ return Freq(unit=unit_, increment=draw(_freq_increments(60)))
520
+ case "millisecond" | "microsecond" | "nanosecond":
521
+ return Freq(unit=unit_, increment=draw(_freq_increments(1000)))
522
+ case _ as never:
523
+ assert_never(never)
524
+
525
+
526
+ @composite
527
+ def _freq_units(draw: DrawFn, /) -> DateTimeRoundUnit:
528
+ return draw(sampled_from(get_literal_elements(DateTimeRoundUnit)))
529
+
530
+
531
+ @composite
532
+ def _freq_increments(draw: DrawFn, n: int, /) -> int:
533
+ divisors = [i for i in range(1, n) if n % i == 0]
534
+ return draw(sampled_from(divisors))
535
+
536
+
537
+ ##
538
+
539
+
505
540
  @composite
506
541
  def git_repos(draw: DrawFn, /) -> Path:
507
542
  path = draw(temp_paths())
@@ -1264,6 +1299,7 @@ __all__ = [
1264
1299
  "float64s",
1265
1300
  "float_arrays",
1266
1301
  "floats_extra",
1302
+ "freqs",
1267
1303
  "git_repos",
1268
1304
  "hashables",
1269
1305
  "int32s",
@@ -21,6 +21,7 @@ from whenever import (
21
21
  )
22
22
 
23
23
  from utilities.iterables import always_iterable
24
+ from utilities.whenever import Freq
24
25
 
25
26
  if TYPE_CHECKING:
26
27
  from collections.abc import Callable
@@ -52,6 +53,7 @@ class ExtendedTSConverter(TSConverter):
52
53
  (Date, Date.parse_common_iso),
53
54
  (DateDelta, DateDelta.parse_common_iso),
54
55
  (DateTimeDelta, DateTimeDelta.parse_common_iso),
56
+ (Freq, Freq.parse),
55
57
  (IPv4Address, IPv4Address),
56
58
  (IPv6Address, IPv6Address),
57
59
  (PlainDateTime, PlainDateTime.parse_common_iso),
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Callable, Iterable
3
+ from collections.abc import Callable, Iterable, Mapping
4
4
  from dataclasses import dataclass, replace
5
5
  from functools import cache
6
6
  from logging import LogRecord
@@ -8,9 +8,12 @@ from statistics import fmean
8
8
  from typing import (
9
9
  TYPE_CHECKING,
10
10
  Any,
11
+ ClassVar,
12
+ Literal,
11
13
  Self,
12
14
  SupportsFloat,
13
15
  assert_never,
16
+ cast,
14
17
  overload,
15
18
  override,
16
19
  )
@@ -28,7 +31,7 @@ from utilities.math import sign
28
31
  from utilities.platform import get_strftime
29
32
  from utilities.re import ExtractGroupsError, extract_groups
30
33
  from utilities.sentinel import Sentinel, sentinel
31
- from utilities.types import MaybeStr
34
+ from utilities.types import DateTimeRoundUnit, MaybeStr
32
35
  from utilities.tzlocal import LOCAL_TIME_ZONE, LOCAL_TIME_ZONE_NAME
33
36
  from utilities.zoneinfo import UTC, get_time_zone_name
34
37
 
@@ -166,6 +169,127 @@ def format_compact(datetime: ZonedDateTime, /) -> str:
166
169
  ##
167
170
 
168
171
 
172
+ class Freq:
173
+ """A rounding frequency."""
174
+
175
+ unit: DateTimeRoundUnit
176
+ increment: int
177
+ _mapping: ClassVar[Mapping[DateTimeRoundUnit, _DateTimeRoundUnitAbbrev]] = {
178
+ "day": "D",
179
+ "hour": "H",
180
+ "minute": "M",
181
+ "second": "S",
182
+ "millisecond": "ms",
183
+ "microsecond": "us",
184
+ "nanosecond": "ns",
185
+ }
186
+
187
+ def __init__(
188
+ self, *, unit: DateTimeRoundUnit = "second", increment: int = 1
189
+ ) -> None:
190
+ super().__init__()
191
+ if (unit == "day") and (increment != 1):
192
+ raise _FreqDayIncrementError(increment=increment)
193
+ if (unit == "hour") and not ((0 < increment < 24) and (24 % increment == 0)):
194
+ raise _FreqIncrementError(unit=unit, increment=increment, divisor=24)
195
+ if (unit in {"minute", "second"}) and not (
196
+ (0 < increment < 60) and (60 % increment == 0)
197
+ ):
198
+ raise _FreqIncrementError(unit=unit, increment=increment, divisor=60)
199
+ if (unit in {"millisecond", "microsecond", "nanosecond"}) and not (
200
+ (0 < increment < 1000) and (1000 % increment == 0)
201
+ ):
202
+ raise _FreqIncrementError(unit=unit, increment=increment, divisor=1000)
203
+ self.unit = unit
204
+ self.increment = increment
205
+
206
+ @override
207
+ def __eq__(self, other: object, /) -> bool:
208
+ if not isinstance(other, Freq):
209
+ return NotImplemented
210
+ return (self.unit == other.unit) and (self.increment == other.increment)
211
+
212
+ @override
213
+ def __hash__(self) -> int:
214
+ return hash((self.unit, self.increment))
215
+
216
+ @override
217
+ def __repr__(self) -> str:
218
+ return f"{type(self).__name__}(unit={self.unit!r}, increment={self.increment})"
219
+
220
+ @classmethod
221
+ def parse(cls, text: str, /) -> Self:
222
+ try:
223
+ increment, abbrev = extract_groups(r"^(\d*)(D|H|M|S|ms|us|ns)$", text)
224
+ except ExtractGroupsError:
225
+ raise _FreqParseError(text=text) from None
226
+ return cls(
227
+ unit=cls._expand(cast("_DateTimeRoundUnitAbbrev", abbrev)),
228
+ increment=int(increment) if len(increment) >= 1 else 1,
229
+ )
230
+
231
+ def serialize(self) -> str:
232
+ if self.increment == 1:
233
+ return self._abbreviation
234
+ return f"{self.increment}{self._abbreviation}"
235
+
236
+ @classmethod
237
+ def _abbreviate(cls, unit: DateTimeRoundUnit, /) -> _DateTimeRoundUnitAbbrev:
238
+ return cls._mapping[unit]
239
+
240
+ @property
241
+ def _abbreviation(self) -> _DateTimeRoundUnitAbbrev:
242
+ return self._mapping[self.unit]
243
+
244
+ @classmethod
245
+ def _expand(cls, unit: _DateTimeRoundUnitAbbrev, /) -> DateTimeRoundUnit:
246
+ values: set[DateTimeRoundUnit] = {
247
+ k for k, v in cls._mapping.items() if v == unit
248
+ }
249
+ (value,) = values
250
+ return value
251
+
252
+
253
+ type FreqLike = MaybeStr[Freq]
254
+ type _DateTimeRoundUnitAbbrev = Literal["D", "H", "M", "S", "ms", "us", "ns"]
255
+
256
+
257
+ @dataclass(kw_only=True, slots=True)
258
+ class FreqError(Exception): ...
259
+
260
+
261
+ @dataclass(kw_only=True, slots=True)
262
+ class _FreqDayIncrementError(FreqError):
263
+ increment: int
264
+
265
+ @override
266
+ def __str__(self) -> str:
267
+ return f"Increment must be 1 for the 'day' unit; got {self.increment}"
268
+
269
+
270
+ @dataclass(kw_only=True, slots=True)
271
+ class _FreqIncrementError(FreqError):
272
+ unit: DateTimeRoundUnit
273
+ increment: int
274
+ divisor: int
275
+
276
+ @override
277
+ def __str__(self) -> str:
278
+ return f"Increment must be a proper divisor of {self.divisor} for the {self.unit!r} unit; got {self.increment}"
279
+
280
+
281
+ @dataclass(kw_only=True, slots=True)
282
+ class _FreqParseError(FreqError):
283
+ text: str
284
+
285
+ @override
286
+ def __str__(self) -> str:
287
+ return f"Unable to parse frequency; got {self.text!r}"
288
+
289
+
290
+ ##
291
+
292
+
169
293
  def from_timestamp(i: float, /, *, time_zone: TimeZoneLike = UTC) -> ZonedDateTime:
170
294
  """Get a zoned datetime from a timestamp."""
171
295
  return ZonedDateTime.from_timestamp(i, tz=get_time_zone_name(time_zone))
@@ -736,6 +860,9 @@ __all__ = [
736
860
  "ZONED_DATE_TIME_MAX",
737
861
  "ZONED_DATE_TIME_MIN",
738
862
  "DateOrMonth",
863
+ "Freq",
864
+ "FreqError",
865
+ "FreqLike",
739
866
  "MeanDateTimeError",
740
867
  "MinMaxDateError",
741
868
  "Month",