dycw-utilities 0.131.6__tar.gz → 0.131.8__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 (221) hide show
  1. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/PKG-INFO +1 -1
  2. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/pyproject.toml +2 -2
  3. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_aiolimiter.py +2 -1
  4. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_asyncio.py +14 -10
  5. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_hypothesis.py +35 -1
  6. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_timer.py +27 -35
  7. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/__init__.py +1 -1
  8. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/asyncio.py +6 -2
  9. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/hypothesis.py +89 -15
  10. dycw_utilities-0.131.8/src/utilities/timer.py +97 -0
  11. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/whenever2.py +1 -1
  12. dycw_utilities-0.131.6/src/utilities/timer.py +0 -131
  13. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/.gitignore +0 -0
  14. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/LICENSE +0 -0
  15. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/README.md +0 -0
  16. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/__init__.py +0 -0
  17. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/conftest.py +0 -0
  18. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/__init__.py +0 -0
  19. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_missing/__init__.py +0 -0
  20. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_missing/module.py +0 -0
  21. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/__init__.py +0 -0
  22. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/outer_1.py +0 -0
  23. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/outer_2.py +0 -0
  24. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  25. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  26. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  27. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  28. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_without/__init__.py +0 -0
  29. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_without/module_1.py +0 -0
  30. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/package_without/module_2.py +0 -0
  31. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/standalone.py +0 -0
  32. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/modules/with_imports.py +0 -0
  33. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  34. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  35. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  36. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  37. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  38. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  39. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  40. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  41. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_altair.py +0 -0
  42. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_asyncio_classes/__init__.py +0 -0
  43. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_asyncio_classes/loopers.py +0 -0
  44. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_asyncio_classes/redis.py +0 -0
  45. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_atomicwrites.py +0 -0
  46. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_atools.py +0 -0
  47. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_cachetools.py +0 -0
  48. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_click.py +0 -0
  49. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_concurrent.py +0 -0
  50. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_contextlib.py +0 -0
  51. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_contextvars.py +0 -0
  52. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_cryptography.py +0 -0
  53. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_cvxpy.py +0 -0
  54. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_dataclasses.py +0 -0
  55. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_datetime.py +0 -0
  56. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_enum.py +0 -0
  57. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_errors.py +0 -0
  58. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_eventkit.py +0 -0
  59. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_fastapi.py +0 -0
  60. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_fpdf2.py +0 -0
  61. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_functions.py +0 -0
  62. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_functools.py +0 -0
  63. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_getpass.py +0 -0
  64. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_git.py +0 -0
  65. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_hashlib.py +0 -0
  66. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_http.py +0 -0
  67. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_importlib.py +0 -0
  68. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_inflect.py +0 -0
  69. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_ipython.py +0 -0
  70. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_iterables.py +0 -0
  71. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_jupyter.py +0 -0
  72. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_libcst.py +0 -0
  73. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_lightweight_charts.py +0 -0
  74. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_logging.py +0 -0
  75. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_luigi.py +0 -0
  76. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_math.py +0 -0
  77. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_memory_profiler.py +0 -0
  78. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_modules.py +0 -0
  79. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_more_itertools.py +0 -0
  80. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_numpy.py +0 -0
  81. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_operator.py +0 -0
  82. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_optuna.py +0 -0
  83. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_orjson.py +0 -0
  84. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_os.py +0 -0
  85. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_parse.py +0 -0
  86. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pathlib.py +0 -0
  87. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_period.py +0 -0
  88. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pickle.py +0 -0
  89. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_platform.py +0 -0
  90. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_polars.py +0 -0
  91. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_polars_ols.py +0 -0
  92. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pottery.py +0 -0
  93. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pqdm.py +0 -0
  94. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_psutil.py +0 -0
  95. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pydantic.py +0 -0
  96. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pyinstrument.py +0 -0
  97. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pyrsistent.py +0 -0
  98. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pytest.py +0 -0
  99. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_pytest_regressions.py +0 -0
  100. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_python_dotenv.py +0 -0
  101. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_random.py +0 -0
  102. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_re.py +0 -0
  103. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_redis.py +0 -0
  104. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_reprlib.py +0 -0
  105. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_scipy.py +0 -0
  106. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_sentinel.py +0 -0
  107. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_shelve.py +0 -0
  108. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_slack_sdk.py +0 -0
  109. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_socket.py +0 -0
  110. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_sqlalchemy.py +0 -0
  111. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_sqlalchemy_polars.py +0 -0
  112. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_statsmodel.py +0 -0
  113. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_streamlit.py +0 -0
  114. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_string.py +0 -0
  115. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_tempfile.py +0 -0
  116. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_tenacity.py +0 -0
  117. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_text.py +0 -0
  118. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_threading.py +0 -0
  119. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_traceback.py +0 -0
  120. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_types.py +0 -0
  121. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_typing.py +0 -0
  122. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_typing_funcs/__init__.py +0 -0
  123. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_typing_funcs/no_future.py +0 -0
  124. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_typing_funcs/with_future.py +0 -0
  125. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_tzdata.py +0 -0
  126. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_tzlocal.py +0 -0
  127. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_uuid.py +0 -0
  128. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_version.py +0 -0
  129. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_warnings.py +0 -0
  130. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_whenever.py +0 -0
  131. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_whenever2.py +0 -0
  132. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_zipfile.py +0 -0
  133. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/tests/test_zoneinfo.py +0 -0
  134. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/aiolimiter.py +0 -0
  135. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/altair.py +0 -0
  136. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/atomicwrites.py +0 -0
  137. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/atools.py +0 -0
  138. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/cachetools.py +0 -0
  139. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/click.py +0 -0
  140. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/concurrent.py +0 -0
  141. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/contextlib.py +0 -0
  142. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/contextvars.py +0 -0
  143. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/cryptography.py +0 -0
  144. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/cvxpy.py +0 -0
  145. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/dataclasses.py +0 -0
  146. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/datetime.py +0 -0
  147. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/enum.py +0 -0
  148. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/errors.py +0 -0
  149. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/eventkit.py +0 -0
  150. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/fastapi.py +0 -0
  151. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/fpdf2.py +0 -0
  152. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/functions.py +0 -0
  153. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/functools.py +0 -0
  154. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/getpass.py +0 -0
  155. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/git.py +0 -0
  156. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/hashlib.py +0 -0
  157. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/http.py +0 -0
  158. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/importlib.py +0 -0
  159. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/inflect.py +0 -0
  160. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/ipython.py +0 -0
  161. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/iterables.py +0 -0
  162. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/jupyter.py +0 -0
  163. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/libcst.py +0 -0
  164. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/lightweight_charts.py +0 -0
  165. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/logging.py +0 -0
  166. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/luigi.py +0 -0
  167. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/math.py +0 -0
  168. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/memory_profiler.py +0 -0
  169. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/modules.py +0 -0
  170. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/more_itertools.py +0 -0
  171. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/numpy.py +0 -0
  172. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/operator.py +0 -0
  173. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/optuna.py +0 -0
  174. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/orjson.py +0 -0
  175. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/os.py +0 -0
  176. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/parse.py +0 -0
  177. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pathlib.py +0 -0
  178. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/period.py +0 -0
  179. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pickle.py +0 -0
  180. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/platform.py +0 -0
  181. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/polars.py +0 -0
  182. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/polars_ols.py +0 -0
  183. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pottery.py +0 -0
  184. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pqdm.py +0 -0
  185. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/psutil.py +0 -0
  186. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/py.typed +0 -0
  187. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pydantic.py +0 -0
  188. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pyinstrument.py +0 -0
  189. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pyrsistent.py +0 -0
  190. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pytest.py +0 -0
  191. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/pytest_regressions.py +0 -0
  192. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/python_dotenv.py +0 -0
  193. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/random.py +0 -0
  194. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/re.py +0 -0
  195. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/redis.py +0 -0
  196. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/reprlib.py +0 -0
  197. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/scipy.py +0 -0
  198. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/sentinel.py +0 -0
  199. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/shelve.py +0 -0
  200. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/slack_sdk.py +0 -0
  201. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/socket.py +0 -0
  202. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/sqlalchemy.py +0 -0
  203. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/sqlalchemy_polars.py +0 -0
  204. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/statsmodels.py +0 -0
  205. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/streamlit.py +0 -0
  206. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/string.py +0 -0
  207. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/tempfile.py +0 -0
  208. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/tenacity.py +0 -0
  209. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/text.py +0 -0
  210. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/threading.py +0 -0
  211. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/traceback.py +0 -0
  212. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/types.py +0 -0
  213. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/typing.py +0 -0
  214. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/tzdata.py +0 -0
  215. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/tzlocal.py +0 -0
  216. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/uuid.py +0 -0
  217. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/version.py +0 -0
  218. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/warnings.py +0 -0
  219. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/whenever.py +0 -0
  220. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/zipfile.py +0 -0
  221. {dycw_utilities-0.131.6 → dycw_utilities-0.131.8}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.131.6
3
+ Version: 0.131.8
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -94,7 +94,7 @@ dependencies = [
94
94
  name = "dycw-utilities"
95
95
  readme = "README.md"
96
96
  requires-python = ">= 3.12"
97
- version = "0.131.6"
97
+ version = "0.131.8"
98
98
 
99
99
  [project.optional-dependencies]
100
100
  logging = [
@@ -121,7 +121,7 @@ test = [
121
121
  # bump-my-version
122
122
  [tool.bumpversion]
123
123
  allow_dirty = true
124
- current_version = "0.131.6"
124
+ current_version = "0.131.8"
125
125
 
126
126
  [[tool.bumpversion.files]]
127
127
  filename = "src/utilities/__init__.py"
@@ -6,6 +6,7 @@ from typing import ClassVar
6
6
  from utilities.aiolimiter import get_async_limiter
7
7
  from utilities.text import unique_str
8
8
  from utilities.timer import Timer
9
+ from utilities.whenever2 import SECOND
9
10
 
10
11
 
11
12
  class TestGetAsyncLimiter:
@@ -22,7 +23,7 @@ class TestGetAsyncLimiter:
22
23
  for _ in range(2):
23
24
  async with get_async_limiter(name, rate=0.5):
24
25
  await increment()
25
- assert timer >= 0.48
26
+ assert timer >= 0.48 * SECOND
26
27
 
27
28
  shared: ClassVar[str] = unique_str()
28
29
 
@@ -177,14 +177,14 @@ class TestEnhancedTaskGroup:
177
177
  async with EnhancedTaskGroup() as tg:
178
178
  for _ in range(10):
179
179
  _ = tg.create_task(sleep(0.01))
180
- assert timer <= 0.05
180
+ assert timer.timedelta.in_seconds() <= 0.05
181
181
 
182
182
  async def test_max_tasks_enabled(self) -> None:
183
183
  with Timer() as timer:
184
184
  async with EnhancedTaskGroup(max_tasks=2) as tg:
185
185
  for _ in range(10):
186
186
  _ = tg.create_task(sleep(0.01))
187
- assert timer >= 0.05
187
+ assert timer.timedelta.in_seconds() >= 0.05
188
188
 
189
189
  async def test_timeout_pass(self) -> None:
190
190
  async with EnhancedTaskGroup(timeout=0.2) as tg:
@@ -610,7 +610,7 @@ class TestLooper:
610
610
  looper = Example()
611
611
  with Timer() as timer:
612
612
  await looper.restart()
613
- assert timer.timedelta >= self._restart_min_elapsed
613
+ assert timer.timedelta.py_timedelta() >= self._restart_min_elapsed
614
614
  pattern = rf": encountered {get_class_name(CountingLooperError)}\(\) whilst restarting \(initialize\); sleeping for .*\.\.\.$"
615
615
  _ = one(m for m in caplog.messages if search(pattern, m))
616
616
 
@@ -627,7 +627,7 @@ class TestLooper:
627
627
  looper = Example()
628
628
  with Timer() as timer:
629
629
  await looper.restart()
630
- assert timer.timedelta >= self._restart_min_elapsed
630
+ assert timer.timedelta.py_timedelta() >= self._restart_min_elapsed
631
631
  pattern = rf": encountered {get_class_name(CountingLooperError)}\(\) whilst restarting \(tear down\); sleeping for .*\.\.\.$"
632
632
  _ = one(m for m in caplog.messages if search(pattern, m))
633
633
 
@@ -650,7 +650,7 @@ class TestLooper:
650
650
  looper = Example()
651
651
  with Timer() as timer:
652
652
  await looper.restart()
653
- assert timer.timedelta >= self._restart_min_elapsed
653
+ assert timer.timedelta.py_timedelta() >= self._restart_min_elapsed
654
654
  pattern = rf": encountered {get_class_name(CountingLooperError)}\(\) \(tear down\) and then {get_class_name(CountingLooperError)}\(\) \(initialization\) whilst restarting; sleeping for .*\.\.\.$"
655
655
  _ = one(m for m in caplog.messages if search(pattern, m))
656
656
 
@@ -658,7 +658,7 @@ class TestLooper:
658
658
  looper = CountingLooper()
659
659
  with Timer() as timer:
660
660
  await looper.restart()
661
- assert timer.timedelta <= self._restart_max_elapsed
661
+ assert timer.timedelta.py_timedelta() <= self._restart_max_elapsed
662
662
  pattern = r": finished restarting$"
663
663
  _ = one(m for m in caplog.messages if search(pattern, m))
664
664
 
@@ -911,12 +911,14 @@ class TestSleepDur:
911
911
  async def test_main(self, *, duration: Duration) -> None:
912
912
  with Timer() as timer:
913
913
  await sleep_dur(duration=duration)
914
- assert timer <= datetime_duration_to_timedelta(2 * duration)
914
+ assert timer.timedelta.py_timedelta() <= datetime_duration_to_timedelta(
915
+ 2 * duration
916
+ )
915
917
 
916
918
  async def test_none(self) -> None:
917
919
  with Timer() as timer:
918
920
  await sleep_dur()
919
- assert timer <= 0.01
921
+ assert timer.timedelta.in_seconds() <= 0.01
920
922
 
921
923
 
922
924
  class TestSleepMaxDur:
@@ -925,12 +927,14 @@ class TestSleepMaxDur:
925
927
  async def test_main(self, *, duration: Duration) -> None:
926
928
  with Timer() as timer:
927
929
  await sleep_max_dur(duration=duration)
928
- assert timer <= datetime_duration_to_timedelta(2 * duration)
930
+ assert timer.timedelta.py_timedelta() <= datetime_duration_to_timedelta(
931
+ 2 * duration
932
+ )
929
933
 
930
934
  async def test_none(self) -> None:
931
935
  with Timer() as timer:
932
936
  await sleep_max_dur()
933
- assert timer <= 0.01
937
+ assert timer.timedelta.in_seconds() <= 0.01
934
938
 
935
939
 
936
940
  class TestSleepUntil:
@@ -28,7 +28,7 @@ from luigi import Task
28
28
  from numpy import inf, int64, isfinite, isinf, isnan, ravel, rint
29
29
  from pathvalidate import validate_filepath
30
30
  from pytest import mark, raises
31
- from whenever import Date, DateDelta, PlainDateTime, ZonedDateTime
31
+ from whenever import Date, DateDelta, PlainDateTime, Time, TimeDelta, ZonedDateTime
32
32
 
33
33
  from tests.conftest import SKIPIF_CI_AND_WINDOWS
34
34
  from utilities.datetime import (
@@ -92,7 +92,9 @@ from utilities.hypothesis import (
92
92
  text_clean,
93
93
  text_digits,
94
94
  text_printable,
95
+ time_deltas_whenever,
95
96
  timedeltas_2w,
97
+ times_whenever,
96
98
  triples,
97
99
  uint32s,
98
100
  uint64s,
@@ -1153,6 +1155,38 @@ class TestTimeDeltas2W:
1153
1155
  assert min_value <= timedelta <= max_value
1154
1156
 
1155
1157
 
1158
+ class TestTimeDeltas:
1159
+ @given(data=data())
1160
+ def test_main(self, *, data: DataObject) -> None:
1161
+ min_value = data.draw(time_deltas_whenever() | none())
1162
+ max_value = data.draw(time_deltas_whenever() | none())
1163
+ with assume_does_not_raise(InvalidArgument):
1164
+ delta = data.draw(
1165
+ time_deltas_whenever(min_value=min_value, max_value=max_value)
1166
+ )
1167
+ assert isinstance(delta, TimeDelta)
1168
+ assert TimeDelta.parse_common_iso(delta.format_common_iso()) == delta
1169
+ if min_value is not None:
1170
+ assert delta >= min_value
1171
+ if max_value is not None:
1172
+ assert delta <= max_value
1173
+
1174
+
1175
+ class TestTimes:
1176
+ @given(data=data())
1177
+ def test_main(self, *, data: DataObject) -> None:
1178
+ min_value = data.draw(times_whenever() | none())
1179
+ max_value = data.draw(times_whenever() | none())
1180
+ with assume_does_not_raise(InvalidArgument):
1181
+ time = data.draw(times_whenever(min_value=min_value, max_value=max_value))
1182
+ assert isinstance(time, Time)
1183
+ assert Time.parse_common_iso(time.format_common_iso()) == time
1184
+ if min_value is not None:
1185
+ assert time >= min_value
1186
+ if max_value is not None:
1187
+ assert time <= max_value
1188
+
1189
+
1156
1190
  class TestTriples:
1157
1191
  @given(data=data(), unique=booleans(), sorted_=booleans())
1158
1192
  def test_main(self, *, data: DataObject, unique: bool, sorted_: bool) -> None:
@@ -1,15 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime as dt
4
3
  from operator import add, eq, ge, gt, le, lt, mul, ne, sub, truediv
5
4
  from re import search
6
5
  from time import sleep
7
6
  from typing import TYPE_CHECKING, Any
8
7
 
9
8
  from pytest import mark, param, raises
9
+ from whenever import TimeDelta
10
10
 
11
- from utilities.datetime import SECOND, ZERO_TIME
11
+ from utilities.asyncio import sleep_dur
12
12
  from utilities.timer import Timer
13
+ from utilities.whenever2 import SECOND, ZERO_TIME
13
14
 
14
15
  if TYPE_CHECKING:
15
16
  from collections.abc import Callable
@@ -17,32 +18,27 @@ if TYPE_CHECKING:
17
18
 
18
19
  class TestTimer:
19
20
  @mark.parametrize(
20
- ("op", "other", "cls"),
21
+ ("op", "other"),
21
22
  [
22
- param(add, 0, dt.timedelta),
23
- param(add, 0.0, dt.timedelta),
24
- param(add, ZERO_TIME, dt.timedelta),
25
- param(sub, 0, dt.timedelta),
26
- param(sub, 0.0, dt.timedelta),
27
- param(sub, ZERO_TIME, dt.timedelta),
28
- param(mul, 1, dt.timedelta),
29
- param(mul, 1.0, dt.timedelta),
30
- param(truediv, 1, dt.timedelta),
31
- param(truediv, 1.0, dt.timedelta),
32
- param(truediv, SECOND, float),
23
+ param(add, ZERO_TIME),
24
+ param(sub, ZERO_TIME),
25
+ param(mul, 1),
26
+ param(mul, 1.0),
27
+ param(truediv, 1),
28
+ param(truediv, 1.0),
33
29
  ],
34
30
  ids=str,
35
31
  )
36
32
  def test_arithmetic_against_numbers_or_timedeltas(
37
- self, *, op: Callable[[Any, Any], Any], other: Any, cls: type[Any]
33
+ self, *, op: Callable[[Any, Any], Any], other: Any
38
34
  ) -> None:
39
35
  with Timer() as timer:
40
36
  pass
41
- assert isinstance(op(timer, other), cls)
37
+ assert isinstance(op(timer, other), TimeDelta)
42
38
 
43
39
  @mark.parametrize(
44
40
  ("op", "cls"),
45
- [param(add, dt.timedelta), param(sub, dt.timedelta), param(truediv, float)],
41
+ [param(add, TimeDelta), param(sub, TimeDelta), param(truediv, float)],
46
42
  ids=str,
47
43
  )
48
44
  def test_arithmetic_against_another_timer(
@@ -83,15 +79,12 @@ class TestTimer:
83
79
  ],
84
80
  ids=str,
85
81
  )
86
- @mark.parametrize(
87
- "dur", [param(1), param(1.0), param(dt.timedelta(seconds=1))], ids=str
88
- )
89
82
  def test_comparison(
90
- self, *, op: Callable[[Any, Any], bool], dur: Any, expected: bool
83
+ self, *, op: Callable[[Any, Any], bool], expected: bool
91
84
  ) -> None:
92
85
  with Timer() as timer:
93
86
  pass
94
- assert op(timer, dur) is expected
87
+ assert op(timer, SECOND) is expected
95
88
 
96
89
  @mark.parametrize(
97
90
  "op",
@@ -120,28 +113,27 @@ class TestTimer:
120
113
  with raises(TypeError):
121
114
  _ = op(timer, "")
122
115
 
123
- def test_context_manager(self) -> None:
124
- duration = 0.01
116
+ async def test_context_manager(self) -> None:
117
+ delta = 0.01 * SECOND
125
118
  with Timer() as timer:
126
- assert isinstance(timer, Timer)
127
- sleep(2 * duration)
128
- assert timer >= duration
119
+ await sleep_dur(duration=2 * delta)
120
+ assert timer >= delta
129
121
 
130
122
  @mark.parametrize("func", [param(repr), param(str)], ids=str)
131
123
  def test_repr_and_str(self, *, func: Callable[[Timer], str]) -> None:
132
124
  with Timer() as timer:
133
125
  sleep(0.01)
134
126
  as_str = func(timer)
135
- assert search(r"^\d+:\d{2}:\d{2}\.\d{6}$", as_str)
127
+ assert search(r"^PT0\.\d+S$", as_str)
136
128
 
137
- def test_running(self) -> None:
138
- duration = 0.01
129
+ async def test_running(self) -> None:
130
+ delta = 0.01 * SECOND
139
131
  timer = Timer()
140
- sleep(2 * duration)
141
- assert timer >= duration
142
- sleep(2 * duration)
143
- assert timer >= 2 * duration
132
+ await sleep_dur(duration=2 * delta)
133
+ assert timer >= delta
134
+ await sleep_dur(duration=2 * delta)
135
+ assert timer >= 2 * delta
144
136
 
145
137
  def test_timedelta(self) -> None:
146
138
  timer = Timer()
147
- assert isinstance(timer.timedelta, dt.timedelta)
139
+ assert isinstance(timer.timedelta, TimeDelta)
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.131.6"
3
+ __version__ = "0.131.8"
@@ -43,6 +43,7 @@ from typing import (
43
43
  )
44
44
 
45
45
  from typing_extensions import deprecated
46
+ from whenever import TimeDelta
46
47
 
47
48
  from utilities.dataclasses import replace_non_sentinel
48
49
  from utilities.datetime import (
@@ -961,11 +962,14 @@ def put_items_nowait(items: Iterable[_T], queue: Queue[_T], /) -> None:
961
962
  ##
962
963
 
963
964
 
964
- async def sleep_dur(*, duration: Duration | None = None) -> None:
965
+ async def sleep_dur(*, duration: Duration | TimeDelta | None = None) -> None:
965
966
  """Sleep which accepts durations."""
966
967
  if duration is None:
967
968
  return
968
- await sleep(datetime_duration_to_float(duration))
969
+ if isinstance(duration, TimeDelta):
970
+ await sleep(duration.in_seconds())
971
+ else:
972
+ await sleep(datetime_duration_to_float(duration))
969
973
 
970
974
 
971
975
  ##
@@ -24,6 +24,7 @@ from typing import (
24
24
  override,
25
25
  )
26
26
 
27
+ import hypothesis.strategies
27
28
  from hypothesis import HealthCheck, Phase, Verbosity, assume, settings
28
29
  from hypothesis.errors import InvalidArgument
29
30
  from hypothesis.strategies import (
@@ -47,7 +48,7 @@ from hypothesis.strategies import (
47
48
  uuids,
48
49
  )
49
50
  from hypothesis.utils.conventions import not_set
50
- from whenever import Date, DateDelta
51
+ from whenever import Date, DateDelta, PlainDateTime, Time, TimeDelta
51
52
 
52
53
  from utilities.datetime import (
53
54
  DATETIME_MAX_NAIVE,
@@ -89,6 +90,20 @@ from utilities.platform import IS_WINDOWS
89
90
  from utilities.sentinel import Sentinel, sentinel
90
91
  from utilities.tempfile import TEMP_DIR, TemporaryDirectory
91
92
  from utilities.version import Version
93
+ from utilities.whenever2 import (
94
+ DATE_DELTA_MAX,
95
+ DATE_DELTA_MIN,
96
+ DATE_DELTA_PARSABLE_MAX,
97
+ DATE_DELTA_PARSABLE_MIN,
98
+ DATE_MAX,
99
+ DATE_MIN,
100
+ PLAIN_DATE_TIME_MAX,
101
+ PLAIN_DATE_TIME_MIN,
102
+ TIME_DELTA_MAX,
103
+ TIME_DELTA_MIN,
104
+ TIME_MAX,
105
+ TIME_MIN,
106
+ )
92
107
  from utilities.zoneinfo import UTC, ensure_time_zone
93
108
 
94
109
  if TYPE_CHECKING:
@@ -97,7 +112,7 @@ if TYPE_CHECKING:
97
112
 
98
113
  from hypothesis.database import ExampleDatabase
99
114
  from numpy.random import RandomState
100
- from whenever import PlainDateTime, ZonedDateTime
115
+ from whenever import ZonedDateTime
101
116
 
102
117
  from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
103
118
  from utilities.types import Duration, Number, RoundMode, TimeZoneLike
@@ -170,13 +185,6 @@ def date_deltas_whenever(
170
185
  parsable: MaybeSearchStrategy[bool] = False,
171
186
  ) -> DateDelta:
172
187
  """Strategy for generating date deltas."""
173
- from utilities.whenever2 import (
174
- DATE_DELTA_MAX,
175
- DATE_DELTA_MIN,
176
- DATE_DELTA_PARSABLE_MAX,
177
- DATE_DELTA_PARSABLE_MIN,
178
- )
179
-
180
188
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
181
189
  match min_value_:
182
190
  case None:
@@ -307,8 +315,6 @@ def dates_whenever(
307
315
  max_value: MaybeSearchStrategy[Date | None] = None,
308
316
  ) -> Date:
309
317
  """Strategy for generating dates."""
310
- from utilities.whenever2 import DATE_MAX, DATE_MIN
311
-
312
318
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
313
319
  match min_value_:
314
320
  case None:
@@ -1069,10 +1075,6 @@ def plain_datetimes_whenever(
1069
1075
  max_value: MaybeSearchStrategy[PlainDateTime | None] = None,
1070
1076
  ) -> PlainDateTime:
1071
1077
  """Strategy for generating plain datetimes."""
1072
- from whenever import PlainDateTime
1073
-
1074
- from utilities.whenever2 import PLAIN_DATE_TIME_MAX, PLAIN_DATE_TIME_MIN
1075
-
1076
1078
  min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1077
1079
  match min_value_:
1078
1080
  case None:
@@ -1405,6 +1407,41 @@ def text_printable(
1405
1407
  ##
1406
1408
 
1407
1409
 
1410
+ @composite
1411
+ def time_deltas_whenever(
1412
+ draw: DrawFn,
1413
+ /,
1414
+ *,
1415
+ min_value: MaybeSearchStrategy[TimeDelta | None] = None,
1416
+ max_value: MaybeSearchStrategy[TimeDelta | None] = None,
1417
+ ) -> TimeDelta:
1418
+ """Strategy for generating time deltas."""
1419
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1420
+ match min_value_:
1421
+ case None:
1422
+ min_value_ = TIME_DELTA_MIN
1423
+ case TimeDelta():
1424
+ ...
1425
+ case _ as never:
1426
+ assert_never(never)
1427
+ match max_value_:
1428
+ case None:
1429
+ max_value_ = TIME_DELTA_MAX
1430
+ case TimeDelta():
1431
+ ...
1432
+ case _ as never:
1433
+ assert_never(never)
1434
+ py_time = draw(
1435
+ hypothesis.strategies.timedeltas(
1436
+ min_value=min_value_.py_timedelta(), max_value=max_value_.py_timedelta()
1437
+ )
1438
+ )
1439
+ return TimeDelta.from_py_timedelta(py_time)
1440
+
1441
+
1442
+ ##
1443
+
1444
+
1408
1445
  @composite
1409
1446
  def timedeltas_2w(
1410
1447
  draw: DrawFn,
@@ -1431,6 +1468,41 @@ def timedeltas_2w(
1431
1468
  ##
1432
1469
 
1433
1470
 
1471
+ @composite
1472
+ def times_whenever(
1473
+ draw: DrawFn,
1474
+ /,
1475
+ *,
1476
+ min_value: MaybeSearchStrategy[Time | None] = None,
1477
+ max_value: MaybeSearchStrategy[Time | None] = None,
1478
+ ) -> Time:
1479
+ """Strategy for generating times."""
1480
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1481
+ match min_value_:
1482
+ case None:
1483
+ min_value_ = TIME_MIN
1484
+ case Time():
1485
+ ...
1486
+ case _ as never:
1487
+ assert_never(never)
1488
+ match max_value_:
1489
+ case None:
1490
+ max_value_ = TIME_MAX
1491
+ case Time():
1492
+ ...
1493
+ case _ as never:
1494
+ assert_never(never)
1495
+ py_time = draw(
1496
+ hypothesis.strategies.times(
1497
+ min_value=min_value_.py_time(), max_value=max_value_.py_time()
1498
+ )
1499
+ )
1500
+ return Time.from_py_time(py_time)
1501
+
1502
+
1503
+ ##
1504
+
1505
+
1434
1506
  def triples(
1435
1507
  strategy: SearchStrategy[_T],
1436
1508
  /,
@@ -1651,7 +1723,9 @@ __all__ = [
1651
1723
  "text_clean",
1652
1724
  "text_digits",
1653
1725
  "text_printable",
1726
+ "time_deltas_whenever",
1654
1727
  "timedeltas_2w",
1728
+ "times_whenever",
1655
1729
  "triples",
1656
1730
  "uint32s",
1657
1731
  "uint64s",
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ from operator import add, eq, ge, gt, le, lt, mul, ne, sub, truediv
4
+ from typing import TYPE_CHECKING, Any, Self, override
5
+
6
+ from utilities.whenever2 import get_now_local
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Callable
10
+
11
+ from whenever import TimeDelta, ZonedDateTime
12
+
13
+
14
+ class Timer:
15
+ """Context manager for timing blocks of code."""
16
+
17
+ def __init__(self) -> None:
18
+ super().__init__()
19
+ self._start: ZonedDateTime = get_now_local()
20
+ self._end: ZonedDateTime | None = None
21
+
22
+ # arithmetic
23
+
24
+ def __add__(self, other: Any) -> TimeDelta:
25
+ return self._apply_op(add, other)
26
+
27
+ def __float__(self) -> float:
28
+ return self.timedelta.in_seconds()
29
+
30
+ def __sub__(self, other: Any) -> TimeDelta:
31
+ return self._apply_op(sub, other)
32
+
33
+ def __mul__(self, other: Any) -> TimeDelta:
34
+ return self._apply_op(mul, other)
35
+
36
+ def __truediv__(self, other: Any) -> TimeDelta:
37
+ return self._apply_op(truediv, other)
38
+
39
+ # context manager
40
+
41
+ def __enter__(self) -> Self:
42
+ self._start = get_now_local()
43
+ return self
44
+
45
+ def __exit__(self, *_: object) -> bool:
46
+ self._end = get_now_local()
47
+ return False
48
+
49
+ # repr
50
+
51
+ @override
52
+ def __repr__(self) -> str:
53
+ return self.timedelta.format_common_iso()
54
+
55
+ @override
56
+ def __str__(self) -> str:
57
+ return self.timedelta.format_common_iso()
58
+
59
+ # comparison
60
+
61
+ @override
62
+ def __eq__(self, other: object) -> bool:
63
+ return self._apply_op(eq, other)
64
+
65
+ def __ge__(self, other: Any) -> bool:
66
+ return self._apply_op(ge, other)
67
+
68
+ def __gt__(self, other: Any) -> bool:
69
+ return self._apply_op(gt, other)
70
+
71
+ def __le__(self, other: Any) -> bool:
72
+ return self._apply_op(le, other)
73
+
74
+ def __lt__(self, other: Any) -> bool:
75
+ return self._apply_op(lt, other)
76
+
77
+ @override
78
+ def __ne__(self, other: object) -> bool:
79
+ return self._apply_op(ne, other)
80
+
81
+ # properties
82
+
83
+ @property
84
+ def timedelta(self) -> TimeDelta:
85
+ """The elapsed time, as a `timedelta` object."""
86
+ end_use = get_now_local() if (end := self._end) is None else end
87
+ return end_use - self._start
88
+
89
+ # private
90
+
91
+ def _apply_op(self, op: Callable[[Any, Any], Any], other: Any, /) -> Any:
92
+ if isinstance(other, Timer):
93
+ return op(self.timedelta, other.timedelta)
94
+ return op(self.timedelta, other)
95
+
96
+
97
+ __all__ = ["Timer"]
@@ -53,7 +53,7 @@ DATE_DELTA_PARSABLE_MAX = DateDelta(days=999999)
53
53
  ## common constants
54
54
 
55
55
 
56
- ZERO_TIME = Time()
56
+ ZERO_TIME = TimeDelta()
57
57
  MICROSECOND = TimeDelta(microseconds=1)
58
58
  MILLISECOND = TimeDelta(milliseconds=1)
59
59
  SECOND = TimeDelta(seconds=1)