dycw-utilities 0.131.7__tar.gz → 0.131.9__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.7 → dycw_utilities-0.131.9}/PKG-INFO +1 -1
  2. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/pyproject.toml +2 -2
  3. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_aiolimiter.py +2 -1
  4. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_asyncio.py +14 -10
  5. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_re.py +12 -0
  6. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_timer.py +27 -35
  7. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/__init__.py +1 -1
  8. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/asyncio.py +6 -2
  9. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/re.py +51 -20
  10. dycw_utilities-0.131.9/src/utilities/timer.py +97 -0
  11. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/types.py +6 -0
  12. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/whenever2.py +1 -1
  13. dycw_utilities-0.131.7/src/utilities/timer.py +0 -131
  14. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/.gitignore +0 -0
  15. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/LICENSE +0 -0
  16. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/README.md +0 -0
  17. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/__init__.py +0 -0
  18. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/conftest.py +0 -0
  19. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/__init__.py +0 -0
  20. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_missing/__init__.py +0 -0
  21. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_missing/module.py +0 -0
  22. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/__init__.py +0 -0
  23. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/outer_1.py +0 -0
  24. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/outer_2.py +0 -0
  25. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  26. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  27. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  28. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  29. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_without/__init__.py +0 -0
  30. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_without/module_1.py +0 -0
  31. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/package_without/module_2.py +0 -0
  32. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/standalone.py +0 -0
  33. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/modules/with_imports.py +0 -0
  34. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  35. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  36. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  37. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  38. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  39. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  40. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  41. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  42. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_altair.py +0 -0
  43. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_asyncio_classes/__init__.py +0 -0
  44. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_asyncio_classes/loopers.py +0 -0
  45. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_asyncio_classes/redis.py +0 -0
  46. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_atomicwrites.py +0 -0
  47. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_atools.py +0 -0
  48. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_cachetools.py +0 -0
  49. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_click.py +0 -0
  50. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_concurrent.py +0 -0
  51. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_contextlib.py +0 -0
  52. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_contextvars.py +0 -0
  53. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_cryptography.py +0 -0
  54. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_cvxpy.py +0 -0
  55. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_dataclasses.py +0 -0
  56. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_datetime.py +0 -0
  57. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_enum.py +0 -0
  58. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_errors.py +0 -0
  59. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_eventkit.py +0 -0
  60. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_fastapi.py +0 -0
  61. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_fpdf2.py +0 -0
  62. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_functions.py +0 -0
  63. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_functools.py +0 -0
  64. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_getpass.py +0 -0
  65. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_git.py +0 -0
  66. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_hashlib.py +0 -0
  67. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_http.py +0 -0
  68. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_hypothesis.py +0 -0
  69. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_importlib.py +0 -0
  70. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_inflect.py +0 -0
  71. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_ipython.py +0 -0
  72. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_iterables.py +0 -0
  73. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_jupyter.py +0 -0
  74. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_libcst.py +0 -0
  75. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_lightweight_charts.py +0 -0
  76. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_logging.py +0 -0
  77. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_luigi.py +0 -0
  78. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_math.py +0 -0
  79. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_memory_profiler.py +0 -0
  80. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_modules.py +0 -0
  81. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_more_itertools.py +0 -0
  82. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_numpy.py +0 -0
  83. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_operator.py +0 -0
  84. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_optuna.py +0 -0
  85. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_orjson.py +0 -0
  86. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_os.py +0 -0
  87. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_parse.py +0 -0
  88. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pathlib.py +0 -0
  89. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_period.py +0 -0
  90. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pickle.py +0 -0
  91. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_platform.py +0 -0
  92. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_polars.py +0 -0
  93. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_polars_ols.py +0 -0
  94. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pottery.py +0 -0
  95. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pqdm.py +0 -0
  96. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_psutil.py +0 -0
  97. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pydantic.py +0 -0
  98. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pyinstrument.py +0 -0
  99. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pyrsistent.py +0 -0
  100. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pytest.py +0 -0
  101. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_pytest_regressions.py +0 -0
  102. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_python_dotenv.py +0 -0
  103. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_random.py +0 -0
  104. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_redis.py +0 -0
  105. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_reprlib.py +0 -0
  106. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_scipy.py +0 -0
  107. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_sentinel.py +0 -0
  108. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_shelve.py +0 -0
  109. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_slack_sdk.py +0 -0
  110. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_socket.py +0 -0
  111. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_sqlalchemy.py +0 -0
  112. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_statsmodel.py +0 -0
  114. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_string.py +0 -0
  116. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_tempfile.py +0 -0
  117. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_tenacity.py +0 -0
  118. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_text.py +0 -0
  119. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_threading.py +0 -0
  120. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_traceback.py +0 -0
  121. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_types.py +0 -0
  122. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_typing.py +0 -0
  123. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_typing_funcs/__init__.py +0 -0
  124. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_typing_funcs/no_future.py +0 -0
  125. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_typing_funcs/with_future.py +0 -0
  126. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_tzdata.py +0 -0
  127. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_tzlocal.py +0 -0
  128. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_uuid.py +0 -0
  129. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_version.py +0 -0
  130. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_warnings.py +0 -0
  131. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_whenever.py +0 -0
  132. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_whenever2.py +0 -0
  133. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_zipfile.py +0 -0
  134. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/tests/test_zoneinfo.py +0 -0
  135. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/aiolimiter.py +0 -0
  136. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/altair.py +0 -0
  137. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/click.py +0 -0
  141. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/concurrent.py +0 -0
  142. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/contextlib.py +0 -0
  143. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/contextvars.py +0 -0
  144. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/cryptography.py +0 -0
  145. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/cvxpy.py +0 -0
  146. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/dataclasses.py +0 -0
  147. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/datetime.py +0 -0
  148. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/enum.py +0 -0
  149. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/errors.py +0 -0
  150. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/eventkit.py +0 -0
  151. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/fastapi.py +0 -0
  152. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/fpdf2.py +0 -0
  153. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/functions.py +0 -0
  154. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/functools.py +0 -0
  155. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/getpass.py +0 -0
  156. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/git.py +0 -0
  157. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/hashlib.py +0 -0
  158. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/http.py +0 -0
  159. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/hypothesis.py +0 -0
  160. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/importlib.py +0 -0
  161. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/inflect.py +0 -0
  162. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/ipython.py +0 -0
  163. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/iterables.py +0 -0
  164. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/jupyter.py +0 -0
  165. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/libcst.py +0 -0
  166. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/lightweight_charts.py +0 -0
  167. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/logging.py +0 -0
  168. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/luigi.py +0 -0
  169. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/math.py +0 -0
  170. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/memory_profiler.py +0 -0
  171. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/modules.py +0 -0
  172. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/more_itertools.py +0 -0
  173. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/numpy.py +0 -0
  174. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/operator.py +0 -0
  175. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/optuna.py +0 -0
  176. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/orjson.py +0 -0
  177. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/os.py +0 -0
  178. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/parse.py +0 -0
  179. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pathlib.py +0 -0
  180. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/period.py +0 -0
  181. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pickle.py +0 -0
  182. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/platform.py +0 -0
  183. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/polars.py +0 -0
  184. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/polars_ols.py +0 -0
  185. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pottery.py +0 -0
  186. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pqdm.py +0 -0
  187. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/psutil.py +0 -0
  188. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/py.typed +0 -0
  189. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pydantic.py +0 -0
  190. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pyinstrument.py +0 -0
  191. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pyrsistent.py +0 -0
  192. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pytest.py +0 -0
  193. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/pytest_regressions.py +0 -0
  194. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/python_dotenv.py +0 -0
  195. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/random.py +0 -0
  196. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/redis.py +0 -0
  197. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/reprlib.py +0 -0
  198. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/scipy.py +0 -0
  199. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/sentinel.py +0 -0
  200. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/shelve.py +0 -0
  201. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/slack_sdk.py +0 -0
  202. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/socket.py +0 -0
  203. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/sqlalchemy.py +0 -0
  204. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/sqlalchemy_polars.py +0 -0
  205. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/statsmodels.py +0 -0
  206. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/streamlit.py +0 -0
  207. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/string.py +0 -0
  208. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/tempfile.py +0 -0
  209. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/tenacity.py +0 -0
  210. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/text.py +0 -0
  211. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/threading.py +0 -0
  212. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/traceback.py +0 -0
  213. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/typing.py +0 -0
  214. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/tzdata.py +0 -0
  215. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/tzlocal.py +0 -0
  216. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/uuid.py +0 -0
  217. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/version.py +0 -0
  218. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/warnings.py +0 -0
  219. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/whenever.py +0 -0
  220. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/src/utilities/zipfile.py +0 -0
  221. {dycw_utilities-0.131.7 → dycw_utilities-0.131.9}/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.7
3
+ Version: 0.131.9
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.7"
97
+ version = "0.131.9"
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.7"
124
+ current_version = "0.131.9"
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:
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
3
4
  from re import DOTALL
4
5
  from typing import TYPE_CHECKING
5
6
 
@@ -13,6 +14,7 @@ from utilities.re import (
13
14
  _ExtractGroupsMultipleMatchesError,
14
15
  _ExtractGroupsNoCaptureGroupsError,
15
16
  _ExtractGroupsNoMatchesError,
17
+ ensure_pattern,
16
18
  extract_group,
17
19
  extract_groups,
18
20
  )
@@ -21,6 +23,16 @@ if TYPE_CHECKING:
21
23
  from collections.abc import Sequence
22
24
 
23
25
 
26
+ class TestEnsurePattern:
27
+ def test_pattern(self) -> None:
28
+ pattern = re.compile(r"\d")
29
+ assert ensure_pattern(pattern) == pattern
30
+
31
+ def test_str(self) -> None:
32
+ pattern = r"\d"
33
+ assert ensure_pattern(pattern) == re.compile(pattern)
34
+
35
+
24
36
  class TestExtractGroup:
25
37
  def test_main(self) -> None:
26
38
  assert extract_group(r"(\d)", "A0A") == "0"
@@ -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.7"
3
+ __version__ = "0.131.9"
@@ -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
  ##
@@ -2,37 +2,59 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  from dataclasses import dataclass
5
- from typing import override
5
+ from re import Pattern
6
+ from typing import TYPE_CHECKING, assert_never, override
6
7
 
7
8
  from utilities.iterables import OneEmptyError, OneNonUniqueError, one
8
9
 
10
+ if TYPE_CHECKING:
11
+ from utilities.types import PatternLike
9
12
 
10
- def extract_group(pattern: str, text: str, /, *, flags: int = 0) -> str:
13
+
14
+ def ensure_pattern(pattern: PatternLike, /, *, flags: int = 0) -> Pattern[str]:
15
+ """Ensure a pattern is returned."""
16
+ match pattern:
17
+ case Pattern():
18
+ return pattern
19
+ case str():
20
+ return re.compile(pattern, flags=flags)
21
+ case _ as never:
22
+ assert_never(never)
23
+
24
+
25
+ ##
26
+
27
+
28
+ def extract_group(pattern: PatternLike, text: str, /, *, flags: int = 0) -> str:
11
29
  """Extract a group.
12
30
 
13
31
  The regex must have 1 capture group, and this must match exactly once.
14
32
  """
15
- compiled = re.compile(pattern, flags=flags)
16
- match compiled.groups:
33
+ pattern_use = ensure_pattern(pattern, flags=flags)
34
+ match pattern_use.groups:
17
35
  case 0:
18
- raise _ExtractGroupNoCaptureGroupsError(pattern=pattern, text=text)
36
+ raise _ExtractGroupNoCaptureGroupsError(pattern=pattern_use, text=text)
19
37
  case 1:
20
- matches: list[str] = compiled.findall(text)
38
+ matches: list[str] = pattern_use.findall(text)
21
39
  try:
22
40
  return one(matches)
23
41
  except OneEmptyError:
24
- raise _ExtractGroupNoMatchesError(pattern=pattern, text=text) from None
42
+ raise _ExtractGroupNoMatchesError(
43
+ pattern=pattern_use, text=text
44
+ ) from None
25
45
  except OneNonUniqueError:
26
46
  raise _ExtractGroupMultipleMatchesError(
27
- pattern=pattern, text=text, matches=matches
47
+ pattern=pattern_use, text=text, matches=matches
28
48
  ) from None
29
49
  case _:
30
- raise _ExtractGroupMultipleCaptureGroupsError(pattern=pattern, text=text)
50
+ raise _ExtractGroupMultipleCaptureGroupsError(
51
+ pattern=pattern_use, text=text
52
+ )
31
53
 
32
54
 
33
55
  @dataclass(kw_only=True, slots=True)
34
56
  class ExtractGroupError(Exception):
35
- pattern: str
57
+ pattern: Pattern[str]
36
58
  text: str
37
59
 
38
60
 
@@ -68,32 +90,35 @@ class _ExtractGroupNoMatchesError(ExtractGroupError):
68
90
  return f"Pattern {self.pattern} must match against {self.text}"
69
91
 
70
92
 
71
- def extract_groups(pattern: str, text: str, /, *, flags: int = 0) -> list[str]:
93
+ ##
94
+
95
+
96
+ def extract_groups(pattern: PatternLike, text: str, /, *, flags: int = 0) -> list[str]:
72
97
  """Extract multiple groups.
73
98
 
74
99
  The regex may have any number of capture groups, and they must collectively
75
100
  match exactly once.
76
101
  """
77
- compiled = re.compile(pattern, flags=flags)
78
- if (n_groups := compiled.groups) == 0:
79
- raise _ExtractGroupsNoCaptureGroupsError(pattern=pattern, text=text)
80
- matches: list[str] = compiled.findall(text)
102
+ pattern_use = ensure_pattern(pattern, flags=flags)
103
+ if (n_groups := pattern_use.groups) == 0:
104
+ raise _ExtractGroupsNoCaptureGroupsError(pattern=pattern_use, text=text)
105
+ matches: list[str] = pattern_use.findall(text)
81
106
  match len(matches), n_groups:
82
107
  case 0, _:
83
- raise _ExtractGroupsNoMatchesError(pattern=pattern, text=text)
108
+ raise _ExtractGroupsNoMatchesError(pattern=pattern_use, text=text)
84
109
  case 1, 1:
85
110
  return matches
86
111
  case 1, _:
87
112
  return list(one(matches))
88
113
  case _:
89
114
  raise _ExtractGroupsMultipleMatchesError(
90
- pattern=pattern, text=text, matches=matches
115
+ pattern=pattern_use, text=text, matches=matches
91
116
  )
92
117
 
93
118
 
94
119
  @dataclass(kw_only=True, slots=True)
95
120
  class ExtractGroupsError(Exception):
96
- pattern: str
121
+ pattern: Pattern[str]
97
122
  text: str
98
123
 
99
124
 
@@ -108,7 +133,7 @@ class _ExtractGroupsMultipleMatchesError(ExtractGroupsError):
108
133
 
109
134
  @dataclass(kw_only=True, slots=True)
110
135
  class _ExtractGroupsNoCaptureGroupsError(ExtractGroupsError):
111
- pattern: str
136
+ pattern: Pattern[str]
112
137
  text: str
113
138
 
114
139
  @override
@@ -123,4 +148,10 @@ class _ExtractGroupsNoMatchesError(ExtractGroupsError):
123
148
  return f"Pattern {self.pattern} must match against {self.text}"
124
149
 
125
150
 
126
- __all__ = ["ExtractGroupError", "ExtractGroupsError", "extract_group", "extract_groups"]
151
+ __all__ = [
152
+ "ExtractGroupError",
153
+ "ExtractGroupsError",
154
+ "ensure_pattern",
155
+ "extract_group",
156
+ "extract_groups",
157
+ ]
@@ -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"]
@@ -7,6 +7,7 @@ from enum import Enum
7
7
  from logging import Logger
8
8
  from pathlib import Path
9
9
  from random import Random
10
+ from re import Pattern
10
11
  from types import TracebackType
11
12
  from typing import (
12
13
  Any,
@@ -249,6 +250,10 @@ type PathLike = MaybeStr[Path]
249
250
  type Seed = int | float | str | bytes | bytearray | Random
250
251
 
251
252
 
253
+ # re
254
+ type PatternLike = MaybeStr[Pattern[str]]
255
+
256
+
252
257
  # traceback
253
258
  type ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
254
259
  type OptExcInfo = ExcInfo | tuple[None, None, None]
@@ -294,6 +299,7 @@ __all__ = [
294
299
  "Parallelism",
295
300
  "ParseObjectExtra",
296
301
  "PathLike",
302
+ "PatternLike",
297
303
  "RoundMode",
298
304
  "Seed",
299
305
  "SerializeObjectExtra",
@@ -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)