dycw-utilities 0.133.0__tar.gz → 0.133.2__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.2}/PKG-INFO +1 -1
  2. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/pyproject.toml +4 -2
  3. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_arq.py +24 -13
  4. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_click.py +3 -0
  5. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_hypothesis.py +13 -1
  6. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typed_settings.py +3 -0
  7. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_whenever.py +74 -0
  8. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/__init__.py +1 -1
  9. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/arq.py +66 -4
  10. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/click.py +26 -1
  11. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/hypothesis.py +36 -0
  12. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/typed_settings.py +2 -0
  13. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/whenever.py +129 -2
  14. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/.gitignore +0 -0
  15. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/LICENSE +0 -0
  16. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/README.md +0 -0
  17. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/__init__.py +0 -0
  18. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/conftest.py +0 -0
  19. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/__init__.py +0 -0
  20. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_missing/__init__.py +0 -0
  21. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_missing/module.py +0 -0
  22. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/__init__.py +0 -0
  23. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/outer_1.py +0 -0
  24. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/outer_2.py +0 -0
  25. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  26. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  27. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  28. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  29. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_without/__init__.py +0 -0
  30. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_without/module_1.py +0 -0
  31. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_without/module_2.py +0 -0
  32. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/standalone.py +0 -0
  33. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/with_imports.py +0 -0
  34. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  35. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  36. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  37. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  38. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  39. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  40. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  41. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  42. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_aiolimiter.py +0 -0
  43. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_altair.py +0 -0
  44. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio.py +0 -0
  45. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio_classes/__init__.py +0 -0
  46. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio_classes/loopers.py +0 -0
  47. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio_classes/redis.py +0 -0
  48. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_atomicwrites.py +0 -0
  49. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_atools.py +0 -0
  50. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_cachetools.py +0 -0
  51. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_concurrent.py +0 -0
  52. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_contextlib.py +0 -0
  53. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_contextvars.py +0 -0
  54. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_cryptography.py +0 -0
  55. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_cvxpy.py +0 -0
  56. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_dataclasses.py +0 -0
  57. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_enum.py +0 -0
  58. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_errors.py +0 -0
  59. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_eventkit.py +0 -0
  60. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_fastapi.py +0 -0
  61. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_fpdf2.py +0 -0
  62. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_functions.py +0 -0
  63. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_functools.py +0 -0
  64. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_getpass.py +0 -0
  65. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_git.py +0 -0
  66. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_hashlib.py +0 -0
  67. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_http.py +0 -0
  68. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_importlib.py +0 -0
  69. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_inflect.py +0 -0
  70. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_ipython.py +0 -0
  71. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_iterables.py +0 -0
  72. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_jupyter.py +0 -0
  73. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_libcst.py +0 -0
  74. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_lightweight_charts.py +0 -0
  75. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_logging.py +0 -0
  76. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_luigi.py +0 -0
  77. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_math.py +0 -0
  78. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_memory_profiler.py +0 -0
  79. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_modules.py +0 -0
  80. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_more_itertools.py +0 -0
  81. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_numpy.py +0 -0
  82. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_objects/__init__.py +0 -0
  83. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_objects/objects.py +0 -0
  84. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_operator.py +0 -0
  85. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_optuna.py +0 -0
  86. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_orjson.py +0 -0
  87. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_os.py +0 -0
  88. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_parse.py +0 -0
  89. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pathlib.py +0 -0
  90. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_period.py +0 -0
  91. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pickle.py +0 -0
  92. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_platform.py +0 -0
  93. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_polars.py +0 -0
  94. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_polars_ols.py +0 -0
  95. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pottery.py +0 -0
  96. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pqdm.py +0 -0
  97. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_psutil.py +0 -0
  98. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pydantic.py +0 -0
  99. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pyinstrument.py +0 -0
  100. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pytest.py +0 -0
  101. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pytest_regressions.py +0 -0
  102. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_python_dotenv.py +0 -0
  103. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_random.py +0 -0
  104. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_re.py +0 -0
  105. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_redis.py +0 -0
  106. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_reprlib.py +0 -0
  107. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_scipy.py +0 -0
  108. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_sentinel.py +0 -0
  109. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_shelve.py +0 -0
  110. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_slack_sdk.py +0 -0
  111. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_socket.py +0 -0
  112. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_sqlalchemy.py +0 -0
  113. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_sqlalchemy_polars.py +0 -0
  114. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_statsmodels.py +0 -0
  115. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_streamlit.py +0 -0
  116. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_string.py +0 -0
  117. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_tempfile.py +0 -0
  118. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_text.py +0 -0
  119. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_threading.py +0 -0
  120. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_timer.py +0 -0
  121. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_traceback.py +0 -0
  122. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_types.py +0 -0
  123. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing.py +0 -0
  124. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing_funcs/__init__.py +0 -0
  125. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing_funcs/no_future.py +0 -0
  126. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing_funcs/with_future.py +0 -0
  127. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_tzdata.py +0 -0
  128. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_tzlocal.py +0 -0
  129. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_uuid.py +0 -0
  130. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_version.py +0 -0
  131. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_warnings.py +0 -0
  132. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_zipfile.py +0 -0
  133. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_zoneinfo.py +0 -0
  134. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/aiolimiter.py +0 -0
  135. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/altair.py +0 -0
  136. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/asyncio.py +0 -0
  137. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/concurrent.py +0 -0
  141. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/contextlib.py +0 -0
  142. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/contextvars.py +0 -0
  143. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/cryptography.py +0 -0
  144. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/cvxpy.py +0 -0
  145. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/dataclasses.py +0 -0
  146. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/enum.py +0 -0
  147. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/errors.py +0 -0
  148. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/eventkit.py +0 -0
  149. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/fastapi.py +0 -0
  150. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/fpdf2.py +0 -0
  151. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/functions.py +0 -0
  152. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/functools.py +0 -0
  153. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/getpass.py +0 -0
  154. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/git.py +0 -0
  155. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/hashlib.py +0 -0
  156. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/http.py +0 -0
  157. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/importlib.py +0 -0
  158. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/inflect.py +0 -0
  159. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/ipython.py +0 -0
  160. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/iterables.py +0 -0
  161. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/jupyter.py +0 -0
  162. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/libcst.py +0 -0
  163. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/lightweight_charts.py +0 -0
  164. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/logging.py +0 -0
  165. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/luigi.py +0 -0
  166. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/math.py +0 -0
  167. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/memory_profiler.py +0 -0
  168. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/modules.py +0 -0
  169. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/more_itertools.py +0 -0
  170. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/numpy.py +0 -0
  171. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/operator.py +0 -0
  172. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/optuna.py +0 -0
  173. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/orjson.py +0 -0
  174. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/os.py +0 -0
  175. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/parse.py +0 -0
  176. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pathlib.py +0 -0
  177. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/period.py +0 -0
  178. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pickle.py +0 -0
  179. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/platform.py +0 -0
  180. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/polars.py +0 -0
  181. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/polars_ols.py +0 -0
  182. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pottery.py +0 -0
  183. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pqdm.py +0 -0
  184. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/psutil.py +0 -0
  185. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/py.typed +0 -0
  186. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pydantic.py +0 -0
  187. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pyinstrument.py +0 -0
  188. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pytest.py +0 -0
  189. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pytest_regressions.py +0 -0
  190. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/python_dotenv.py +0 -0
  191. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/random.py +0 -0
  192. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/re.py +0 -0
  193. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/redis.py +0 -0
  194. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/reprlib.py +0 -0
  195. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/scipy.py +0 -0
  196. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/sentinel.py +0 -0
  197. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/shelve.py +0 -0
  198. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/slack_sdk.py +0 -0
  199. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/socket.py +0 -0
  200. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/sqlalchemy.py +0 -0
  201. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/sqlalchemy_polars.py +0 -0
  202. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/statsmodels.py +0 -0
  203. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/streamlit.py +0 -0
  204. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/string.py +0 -0
  205. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/tempfile.py +0 -0
  206. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/text.py +0 -0
  207. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/threading.py +0 -0
  208. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/timer.py +0 -0
  209. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/traceback.py +0 -0
  210. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/types.py +0 -0
  211. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/typing.py +0 -0
  212. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/tzdata.py +0 -0
  213. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/tzlocal.py +0 -0
  214. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/uuid.py +0 -0
  215. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/version.py +0 -0
  216. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/warnings.py +0 -0
  217. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/zipfile.py +0 -0
  218. {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/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.2
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.2"
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.2"
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
@@ -3,45 +3,56 @@ from __future__ import annotations
3
3
  from asyncio import sleep
4
4
  from typing import TYPE_CHECKING, Any, cast
5
5
 
6
+ from arq.connections import ArqRedis
6
7
  from hypothesis import given
7
8
  from hypothesis.strategies import integers
8
9
 
9
- from utilities.arq import Worker, cron_raw
10
+ from tests.conftest import SKIPIF_CI_AND_NOT_LINUX
11
+ from utilities.arq import Worker, cron_raw, job_enqueuer
10
12
  from utilities.iterables import one
11
13
 
12
14
  if TYPE_CHECKING:
13
15
  from collections.abc import Sequence
14
16
 
15
- from arq.cron import CronJob
16
17
  from arq.typing import WorkerCoroutine
17
18
 
18
19
  from utilities.types import CallableCoroutine1
19
20
 
20
21
 
21
- class TestWorker:
22
+ class TestCronRaw:
22
23
  @given(x=integers(), y=integers())
23
24
  async def test_main(self, *, x: int, y: int) -> None:
24
25
  async def func(x: int, y: int, /) -> int:
25
26
  await sleep(0.01)
26
27
  return x + y
27
28
 
28
- class Example(Worker):
29
- functions_raw: Sequence[CallableCoroutine1[Any]] = [func]
30
-
31
- func_use = cast("WorkerCoroutine", one(Example.functions))
32
- result = await func_use({}, x, y)
29
+ job = cron_raw(func, args=(x, y))
30
+ result = await job.coroutine({})
33
31
  assert result == (x + y)
34
32
 
33
+
34
+ class TestJobEnqueuer:
35
+ @given(x=integers(), y=integers())
36
+ @SKIPIF_CI_AND_NOT_LINUX
37
+ async def test_main(self, *, x: int, y: int) -> None:
38
+ async def func(x: int, y: int, /) -> int:
39
+ await sleep(0.01)
40
+ return x + y
41
+
42
+ redis = ArqRedis(db=15)
43
+ _ = await job_enqueuer.settings(queue_name="test")(redis, func, x, y)
44
+
45
+
46
+ class TestWorker:
35
47
  @given(x=integers(), y=integers())
36
- async def test_cron(self, *, x: int, y: int) -> None:
48
+ async def test_main(self, *, x: int, y: int) -> None:
37
49
  async def func(x: int, y: int, /) -> int:
38
50
  await sleep(0.01)
39
51
  return x + y
40
52
 
41
53
  class Example(Worker):
42
- cron_jobs: Sequence[CronJob] | None = [cron_raw(func, args=(x, y))]
54
+ functions_raw: Sequence[CallableCoroutine1[Any]] = [func]
43
55
 
44
- assert Example.cron_jobs is not None
45
- cron_job = one(Example.cron_jobs)
46
- result = await cron_job.coroutine({})
56
+ func_use = cast("WorkerCoroutine", one(Example.functions))
57
+ result = await func_use({}, x, y)
47
58
  assert result == (x + y)
@@ -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.2"
@@ -3,18 +3,21 @@ from __future__ import annotations
3
3
  from dataclasses import dataclass
4
4
  from functools import wraps
5
5
  from itertools import chain
6
- from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast, override
6
+ from typing import TYPE_CHECKING, Any, ParamSpec, Self, TypeVar, cast, override
7
7
 
8
8
  from arq.constants import default_queue_name, expires_extra_ms
9
9
  from arq.cron import cron
10
10
 
11
+ from utilities.dataclasses import replace_non_sentinel
12
+ from utilities.sentinel import Sentinel, sentinel
13
+
11
14
  if TYPE_CHECKING:
12
15
  from collections.abc import Callable, Iterable, Sequence
13
- from datetime import timezone
16
+ from datetime import datetime, timedelta, timezone
14
17
 
15
18
  from arq.connections import ArqRedis, RedisSettings
16
19
  from arq.cron import CronJob
17
- from arq.jobs import Deserializer, Serializer
20
+ from arq.jobs import Deserializer, Job, Serializer
18
21
  from arq.typing import (
19
22
  OptionType,
20
23
  SecondsTimedelta,
@@ -26,6 +29,7 @@ if TYPE_CHECKING:
26
29
 
27
30
  from utilities.types import CallableCoroutine1, Coroutine1, StrMapping
28
31
 
32
+
29
33
  _P = ParamSpec("_P")
30
34
  _T = TypeVar("_T")
31
35
 
@@ -95,6 +99,64 @@ def _lift_cron(
95
99
  ##
96
100
 
97
101
 
102
+ @dataclass(kw_only=True, slots=True)
103
+ class _JobEnqueuer:
104
+ """Enqueuer of jobs."""
105
+
106
+ job_id: str | None = None
107
+ queue_name: str | None = None
108
+ defer_until: datetime | None = None
109
+ defer_by: int | float | timedelta | None = None
110
+ expires: int | float | timedelta | None = None
111
+ job_try: int | None = None
112
+
113
+ async def __call__(
114
+ self,
115
+ redis: ArqRedis,
116
+ function: Callable[_P, Coroutine1[_T]],
117
+ *args: _P.args,
118
+ **kwargs: _P.kwargs,
119
+ ) -> Job | None:
120
+ return await redis.enqueue_job( # skipif-ci-and-not-linux
121
+ function.__name__,
122
+ *args,
123
+ _job_id=self.job_id,
124
+ _queue_name=self.queue_name,
125
+ _defer_until=self.defer_until,
126
+ _defer_by=self.defer_by,
127
+ _expires=self.expires,
128
+ _job_try=self.job_try,
129
+ **kwargs,
130
+ )
131
+
132
+ def settings(
133
+ self,
134
+ *,
135
+ job_id: str | None | Sentinel = sentinel,
136
+ queue_name: str | None | Sentinel = sentinel,
137
+ defer_until: datetime | None | Sentinel = sentinel,
138
+ defer_by: float | timedelta | None | Sentinel = sentinel,
139
+ expires: float | timedelta | None | Sentinel = sentinel,
140
+ job_try: int | None | Sentinel = sentinel,
141
+ ) -> Self:
142
+ """Replace elements of the enqueuer."""
143
+ return replace_non_sentinel( # skipif-ci-and-not-linux
144
+ self,
145
+ job_id=job_id,
146
+ queue_name=queue_name,
147
+ defer_until=defer_until,
148
+ defer_by=defer_by,
149
+ expires=expires,
150
+ job_try=job_try,
151
+ )
152
+
153
+
154
+ job_enqueuer = _JobEnqueuer()
155
+
156
+
157
+ ##
158
+
159
+
98
160
  class _WorkerMeta(type):
99
161
  @override
100
162
  def __new__(
@@ -158,4 +220,4 @@ class Worker(metaclass=_WorkerMeta):
158
220
  log_results: bool = True
159
221
 
160
222
 
161
- __all__ = ["Worker", "cron"]
223
+ __all__ = ["Worker", "cron", "job_enqueuer"]
@@ -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),