dycw-utilities 0.131.18__tar.gz → 0.131.19__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 (225) hide show
  1. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/PKG-INFO +1 -1
  2. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/pyproject.toml +2 -2
  3. dycw_utilities-0.131.19/src/tests/conftest.py +104 -0
  4. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_altair.py +23 -17
  5. dycw_utilities-0.131.19/src/tests/test_datetime.py +161 -0
  6. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_functions.py +55 -27
  7. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_http.py +2 -2
  8. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_hypothesis.py +11 -277
  9. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_iterables.py +10 -0
  10. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_orjson.py +2 -3
  11. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_polars.py +32 -16
  12. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pytest.py +113 -93
  13. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_slack_sdk.py +3 -4
  14. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_sqlalchemy_polars.py +3 -4
  15. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_types.py +1 -12
  16. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_typing.py +237 -228
  17. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_whenever2.py +142 -14
  18. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_zoneinfo.py +21 -4
  19. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/__init__.py +1 -1
  20. dycw_utilities-0.131.19/src/utilities/datetime.py +222 -0
  21. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/fastapi.py +5 -7
  22. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/functions.py +78 -45
  23. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/hypothesis.py +20 -269
  24. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/polars.py +6 -3
  25. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pytest.py +83 -63
  26. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/types.py +3 -24
  27. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/typing.py +2 -15
  28. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/whenever2.py +147 -3
  29. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/zoneinfo.py +4 -0
  30. dycw_utilities-0.131.18/src/tests/conftest.py +0 -446
  31. dycw_utilities-0.131.18/src/tests/test_datetime.py +0 -1049
  32. dycw_utilities-0.131.18/src/tests/test_whenever.py +0 -180
  33. dycw_utilities-0.131.18/src/utilities/datetime.py +0 -1125
  34. dycw_utilities-0.131.18/src/utilities/whenever.py +0 -230
  35. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/.gitignore +0 -0
  36. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/LICENSE +0 -0
  37. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/README.md +0 -0
  38. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/__init__.py +0 -0
  39. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/__init__.py +0 -0
  40. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_missing/__init__.py +0 -0
  41. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_missing/module.py +0 -0
  42. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/__init__.py +0 -0
  43. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/outer_1.py +0 -0
  44. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/outer_2.py +0 -0
  45. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  46. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  47. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  48. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  49. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_without/__init__.py +0 -0
  50. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_without/module_1.py +0 -0
  51. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/package_without/module_2.py +0 -0
  52. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/standalone.py +0 -0
  53. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/modules/with_imports.py +0 -0
  54. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  55. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  56. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  57. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  58. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  59. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  60. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  61. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  62. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_aiolimiter.py +0 -0
  63. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_asyncio.py +0 -0
  64. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_asyncio_classes/__init__.py +0 -0
  65. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_asyncio_classes/loopers.py +0 -0
  66. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_asyncio_classes/redis.py +0 -0
  67. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_atomicwrites.py +0 -0
  68. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_atools.py +0 -0
  69. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_cachetools.py +0 -0
  70. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_click.py +0 -0
  71. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_concurrent.py +0 -0
  72. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_contextlib.py +0 -0
  73. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_contextvars.py +0 -0
  74. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_cryptography.py +0 -0
  75. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_cvxpy.py +0 -0
  76. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_dataclasses.py +0 -0
  77. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_enum.py +0 -0
  78. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_errors.py +0 -0
  79. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_eventkit.py +0 -0
  80. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_fastapi.py +0 -0
  81. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_fpdf2.py +0 -0
  82. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_functools.py +0 -0
  83. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_getpass.py +0 -0
  84. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_git.py +0 -0
  85. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_hashlib.py +0 -0
  86. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_importlib.py +0 -0
  87. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_inflect.py +0 -0
  88. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_ipython.py +0 -0
  89. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_jupyter.py +0 -0
  90. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_libcst.py +0 -0
  91. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_lightweight_charts.py +0 -0
  92. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_logging.py +0 -0
  93. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_luigi.py +0 -0
  94. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_math.py +0 -0
  95. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_memory_profiler.py +0 -0
  96. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_modules.py +0 -0
  97. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_more_itertools.py +0 -0
  98. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_numpy.py +0 -0
  99. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_objects/__init__.py +0 -0
  100. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_objects/objects.py +0 -0
  101. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_operator.py +0 -0
  102. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_optuna.py +0 -0
  103. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_os.py +0 -0
  104. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_parse.py +0 -0
  105. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pathlib.py +0 -0
  106. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_period.py +0 -0
  107. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pickle.py +0 -0
  108. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_platform.py +0 -0
  109. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_polars_ols.py +0 -0
  110. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pottery.py +0 -0
  111. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pqdm.py +0 -0
  112. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_psutil.py +0 -0
  113. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pydantic.py +0 -0
  114. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pyinstrument.py +0 -0
  115. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pyrsistent.py +0 -0
  116. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_pytest_regressions.py +0 -0
  117. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_python_dotenv.py +0 -0
  118. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_random.py +0 -0
  119. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_re.py +0 -0
  120. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_redis.py +0 -0
  121. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_reprlib.py +0 -0
  122. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_scipy.py +0 -0
  123. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_sentinel.py +0 -0
  124. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_shelve.py +0 -0
  125. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_socket.py +0 -0
  126. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_sqlalchemy.py +0 -0
  127. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_statsmodel.py +0 -0
  128. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_streamlit.py +0 -0
  129. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_string.py +0 -0
  130. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_tempfile.py +0 -0
  131. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_text.py +0 -0
  132. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_threading.py +0 -0
  133. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_timer.py +0 -0
  134. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_traceback.py +0 -0
  135. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_typed_settings.py +0 -0
  136. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_typing_funcs/__init__.py +0 -0
  137. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_typing_funcs/no_future.py +0 -0
  138. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_typing_funcs/with_future.py +0 -0
  139. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_tzdata.py +0 -0
  140. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_tzlocal.py +0 -0
  141. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_uuid.py +0 -0
  142. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_version.py +0 -0
  143. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_warnings.py +0 -0
  144. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/tests/test_zipfile.py +0 -0
  145. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/aiolimiter.py +0 -0
  146. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/altair.py +0 -0
  147. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/asyncio.py +0 -0
  148. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/atomicwrites.py +0 -0
  149. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/atools.py +0 -0
  150. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/cachetools.py +0 -0
  151. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/click.py +0 -0
  152. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/concurrent.py +0 -0
  153. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/contextlib.py +0 -0
  154. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/contextvars.py +0 -0
  155. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/cryptography.py +0 -0
  156. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/cvxpy.py +0 -0
  157. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/dataclasses.py +0 -0
  158. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/enum.py +0 -0
  159. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/errors.py +0 -0
  160. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/eventkit.py +0 -0
  161. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/fpdf2.py +0 -0
  162. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/functools.py +0 -0
  163. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/getpass.py +0 -0
  164. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/git.py +0 -0
  165. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/hashlib.py +0 -0
  166. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/http.py +0 -0
  167. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/importlib.py +0 -0
  168. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/inflect.py +0 -0
  169. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/ipython.py +0 -0
  170. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/iterables.py +0 -0
  171. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/jupyter.py +0 -0
  172. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/libcst.py +0 -0
  173. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/lightweight_charts.py +0 -0
  174. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/logging.py +0 -0
  175. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/luigi.py +0 -0
  176. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/math.py +0 -0
  177. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/memory_profiler.py +0 -0
  178. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/modules.py +0 -0
  179. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/more_itertools.py +0 -0
  180. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/numpy.py +0 -0
  181. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/operator.py +0 -0
  182. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/optuna.py +0 -0
  183. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/orjson.py +0 -0
  184. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/os.py +0 -0
  185. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/parse.py +0 -0
  186. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pathlib.py +0 -0
  187. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/period.py +0 -0
  188. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pickle.py +0 -0
  189. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/platform.py +0 -0
  190. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/polars_ols.py +0 -0
  191. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pottery.py +0 -0
  192. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pqdm.py +0 -0
  193. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/psutil.py +0 -0
  194. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/py.typed +0 -0
  195. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pydantic.py +0 -0
  196. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pyinstrument.py +0 -0
  197. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pyrsistent.py +0 -0
  198. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/pytest_regressions.py +0 -0
  199. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/python_dotenv.py +0 -0
  200. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/random.py +0 -0
  201. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/re.py +0 -0
  202. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/redis.py +0 -0
  203. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/reprlib.py +0 -0
  204. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/scipy.py +0 -0
  205. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/sentinel.py +0 -0
  206. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/shelve.py +0 -0
  207. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/slack_sdk.py +0 -0
  208. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/socket.py +0 -0
  209. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/sqlalchemy.py +0 -0
  210. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/sqlalchemy_polars.py +0 -0
  211. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/statsmodels.py +0 -0
  212. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/streamlit.py +0 -0
  213. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/string.py +0 -0
  214. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/tempfile.py +0 -0
  215. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/text.py +0 -0
  216. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/threading.py +0 -0
  217. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/timer.py +0 -0
  218. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/traceback.py +0 -0
  219. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/typed_settings.py +0 -0
  220. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/tzdata.py +0 -0
  221. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/tzlocal.py +0 -0
  222. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/uuid.py +0 -0
  223. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/version.py +0 -0
  224. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/warnings.py +0 -0
  225. {dycw_utilities-0.131.18 → dycw_utilities-0.131.19}/src/utilities/zipfile.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.131.18
3
+ Version: 0.131.19
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.18"
97
+ version = "0.131.19"
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.18"
124
+ current_version = "0.131.19"
125
125
 
126
126
  [[tool.bumpversion.files]]
127
127
  filename = "src/utilities/__init__.py"
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+
3
+ from asyncio import sleep
4
+ from contextlib import AbstractContextManager, contextmanager, suppress
5
+ from logging import LogRecord, setLogRecordFactory
6
+ from os import environ
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from hypothesis import HealthCheck
10
+ from pytest import fixture, mark, param
11
+ from sqlalchemy import text
12
+ from whenever import PlainDateTime
13
+
14
+ from utilities.platform import IS_NOT_LINUX, IS_WINDOWS
15
+ from utilities.re import ExtractGroupError, extract_group
16
+ from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
17
+ from utilities.whenever2 import MINUTE, get_now
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Iterator, Sequence
21
+ from pathlib import Path
22
+
23
+ from _pytest.fixtures import SubRequest
24
+
25
+
26
+ FLAKY = mark.flaky(reruns=5, reruns_delay=1)
27
+ IS_CI = "CI" in environ
28
+ SKIPIF_CI = mark.skipif(IS_CI, reason="Skipped for CI")
29
+ IS_CI_AND_WINDOWS = IS_CI and IS_WINDOWS
30
+ SKIPIF_CI_AND_WINDOWS = mark.skipif(IS_CI_AND_WINDOWS, reason="Skipped for CI/Windows")
31
+ SKIPIF_CI_AND_NOT_LINUX = mark.skipif(
32
+ IS_CI and IS_NOT_LINUX, reason="Skipped for CI/non-Linux"
33
+ )
34
+
35
+
36
+ # hypothesis
37
+
38
+
39
+ try:
40
+ from utilities.hypothesis import setup_hypothesis_profiles
41
+ except ModuleNotFoundError:
42
+ pass
43
+ else:
44
+ setup_hypothesis_profiles(suppress_health_check={HealthCheck.differing_executors})
45
+
46
+
47
+ # fixture - logging
48
+
49
+
50
+ @fixture
51
+ def set_log_factory() -> AbstractContextManager[None]:
52
+ @contextmanager
53
+ def cm() -> Iterator[None]:
54
+ try:
55
+ yield
56
+ finally:
57
+ setLogRecordFactory(LogRecord)
58
+
59
+ return cm()
60
+
61
+
62
+ # fixtures - sqlalchemy
63
+
64
+
65
+ @fixture(params=[param("sqlite"), param("postgresql", marks=SKIPIF_CI)])
66
+ async def test_engine(*, request: SubRequest, tmp_path: Path) -> Any:
67
+ from utilities.sqlalchemy import create_async_engine
68
+
69
+ dialect = request.param
70
+ match dialect:
71
+ case "sqlite":
72
+ db_path = tmp_path / "db.sqlite"
73
+ return create_async_engine("sqlite+aiosqlite", database=str(db_path))
74
+ case "postgresql":
75
+ engine = create_async_engine(
76
+ "postgresql+asyncpg", host="localhost", port=5432, database="testing"
77
+ )
78
+ query = text("SELECT tablename FROM pg_tables")
79
+ async with engine.begin() as conn:
80
+ tables: Sequence[str] = (await conn.execute(query)).scalars().all()
81
+ for table in tables:
82
+ if _is_to_drop(table):
83
+ async with engine.begin() as conn:
84
+ with suppress(Exception):
85
+ _ = await conn.execute(
86
+ text(f'DROP TABLE IF EXISTS "{table}" CASCADE')
87
+ )
88
+ await sleep(0.01)
89
+ return engine
90
+ case _:
91
+ msg = f"Unsupported dialect: {dialect}"
92
+ raise NotImplementedError(msg)
93
+
94
+
95
+ def _is_to_drop(table: str, /) -> bool:
96
+ try:
97
+ datetime_str = extract_group(r"^(\d{8}T\d{6})_", table)
98
+ except ExtractGroupError:
99
+ return True
100
+ datetime = PlainDateTime.parse_common_iso(datetime_str).assume_tz(
101
+ LOCAL_TIME_ZONE_NAME
102
+ )
103
+ now = get_now()
104
+ return (now - datetime) >= MINUTE
@@ -2,15 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
4
  from math import inf
5
- from typing import TYPE_CHECKING, Literal
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  import polars as pl
8
8
  from hypothesis import HealthCheck, given, settings
9
9
  from hypothesis.strategies import (
10
- DataObject,
11
10
  booleans,
12
- data,
13
- dates,
14
11
  floats,
15
12
  integers,
16
13
  just,
@@ -19,7 +16,7 @@ from hypothesis.strategies import (
19
16
  sampled_from,
20
17
  tuples,
21
18
  )
22
- from polars import DataFrame, Date, Float64, datetime_range, int_range
19
+ from polars import DataFrame, Float64, datetime_range, int_range
23
20
  from pytest import fixture
24
21
 
25
22
  from utilities.altair import (
@@ -30,7 +27,7 @@ from utilities.altair import (
30
27
  vconcat_charts,
31
28
  )
32
29
  from utilities.functions import ensure_class
33
- from utilities.hypothesis import text_ascii, zoned_datetimes
30
+ from utilities.hypothesis import dates_whenever, text_ascii, zoned_datetimes_whenever
34
31
  from utilities.polars import DatetimeUTC, zoned_datetime
35
32
  from utilities.tzdata import HongKong, Tokyo
36
33
  from utilities.whenever2 import get_now
@@ -40,6 +37,9 @@ if TYPE_CHECKING:
40
37
  from pathlib import Path
41
38
  from zoneinfo import ZoneInfo
42
39
 
40
+ import whenever
41
+ from whenever import ZonedDateTime
42
+
43
43
 
44
44
  @fixture
45
45
  def time_series() -> DataFrame:
@@ -105,19 +105,25 @@ class TestPlotDataFrames:
105
105
  expected = dt.datetime(2000, 1, 1, 12, tzinfo=time_zone).replace(tzinfo=None)
106
106
  assert datetime == expected
107
107
 
108
- @given(data=data(), case=sampled_from(["date", "datetime"]))
109
- def test_tooltip_format(
110
- self, *, data: DataObject, case: Literal["date", "datetime"]
108
+ @given(data=lists(tuples(dates_whenever(), floats(-10, 10))))
109
+ def test_tooltip_format_date(
110
+ self, *, data: list[tuple[whenever.Date, float]]
111
+ ) -> None:
112
+ df = DataFrame(
113
+ data=[(d.py_date(), v) for d, v in data],
114
+ schema={"index": pl.Date, "value": Float64},
115
+ orient="row",
116
+ )
117
+ _ = plot_dataframes(df, x="index", y="value")
118
+
119
+ @given(data=lists(tuples(zoned_datetimes_whenever(), floats(-10, 10))))
120
+ def test_tooltip_format_date_time(
121
+ self, *, data: list[tuple[ZonedDateTime, float]]
111
122
  ) -> None:
112
- match case:
113
- case "date":
114
- values = data.draw(lists(tuples(dates(), floats(-10, 10))))
115
- dtype = Date()
116
- case "datetime":
117
- values = data.draw(lists(tuples(zoned_datetimes(), floats(-10, 10))))
118
- dtype = DatetimeUTC
119
123
  df = DataFrame(
120
- data=values, schema={"index": dtype, "value": Float64}, orient="row"
124
+ data=[(d.py_datetime(), v) for d, v in data],
125
+ schema={"index": DatetimeUTC, "value": Float64},
126
+ orient="row",
121
127
  )
122
128
  _ = plot_dataframes(df, x="index", y="value")
123
129
 
@@ -0,0 +1,161 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime as dt
4
+ from re import search
5
+ from typing import TYPE_CHECKING
6
+
7
+ from hypothesis import assume, given
8
+ from hypothesis.strategies import DataObject, data, dates, integers, sampled_from
9
+ from pytest import mark, param, raises
10
+
11
+ from utilities.datetime import (
12
+ EnsureMonthError,
13
+ Month,
14
+ MonthError,
15
+ ParseMonthError,
16
+ _ParseTwoDigitYearInvalidIntegerError,
17
+ _ParseTwoDigitYearInvalidStringError,
18
+ date_to_month,
19
+ ensure_month,
20
+ maybe_sub_pct_y,
21
+ parse_month,
22
+ parse_two_digit_year,
23
+ serialize_month,
24
+ )
25
+ from utilities.hypothesis import months, text_clean
26
+ from utilities.zoneinfo import UTC
27
+
28
+ if TYPE_CHECKING:
29
+ from collections.abc import Callable
30
+
31
+
32
+ class TestDateToMonth:
33
+ @given(date=dates())
34
+ def test_main(self, *, date: dt.date) -> None:
35
+ result = date_to_month(date).to_date(day=date.day)
36
+ assert result == date
37
+
38
+
39
+ class TestMaybeSubPctY:
40
+ @given(text=text_clean())
41
+ def test_main(self, *, text: str) -> None:
42
+ result = maybe_sub_pct_y(text)
43
+ _ = assume(not search("%Y", result))
44
+ assert not search("%Y", result)
45
+
46
+
47
+ class TestMonth:
48
+ @mark.parametrize(
49
+ ("month", "n", "expected"),
50
+ [
51
+ param(Month(2000, 1), -2, Month(1999, 11)),
52
+ param(Month(2000, 1), -1, Month(1999, 12)),
53
+ param(Month(2000, 1), 0, Month(2000, 1)),
54
+ param(Month(2000, 1), 1, Month(2000, 2)),
55
+ param(Month(2000, 1), 2, Month(2000, 3)),
56
+ param(Month(2000, 1), 11, Month(2000, 12)),
57
+ param(Month(2000, 1), 12, Month(2001, 1)),
58
+ ],
59
+ )
60
+ def test_add(self, *, month: Month, n: int, expected: Month) -> None:
61
+ result = month + n
62
+ assert result == expected
63
+
64
+ @mark.parametrize(
65
+ ("x", "y", "expected"),
66
+ [
67
+ param(Month(2000, 1), Month(1999, 11), 2),
68
+ param(Month(2000, 1), Month(1999, 12), 1),
69
+ param(Month(2000, 1), Month(2000, 1), 0),
70
+ param(Month(2000, 1), Month(2000, 2), -1),
71
+ param(Month(2000, 1), Month(2000, 3), -2),
72
+ param(Month(2000, 1), Month(2000, 12), -11),
73
+ param(Month(2000, 1), Month(2001, 1), -12),
74
+ ],
75
+ )
76
+ def test_diff(self, *, x: Month, y: Month, expected: int) -> None:
77
+ result = x - y
78
+ assert result == expected
79
+
80
+ @given(month=months())
81
+ def test_hashable(self, *, month: Month) -> None:
82
+ _ = hash(month)
83
+
84
+ @mark.parametrize("func", [param(repr), param(str)])
85
+ def test_repr(self, *, func: Callable[..., str]) -> None:
86
+ result = func(Month(2000, 12))
87
+ expected = "2000-12"
88
+ assert result == expected
89
+
90
+ @mark.parametrize(
91
+ ("month", "n", "expected"),
92
+ [
93
+ param(Month(2000, 1), -2, Month(2000, 3)),
94
+ param(Month(2000, 1), -1, Month(2000, 2)),
95
+ param(Month(2000, 1), 0, Month(2000, 1)),
96
+ param(Month(2000, 1), 1, Month(1999, 12)),
97
+ param(Month(2000, 1), 2, Month(1999, 11)),
98
+ param(Month(2000, 1), 12, Month(1999, 1)),
99
+ param(Month(2000, 1), 13, Month(1998, 12)),
100
+ ],
101
+ )
102
+ def test_subtract(self, *, month: Month, n: int, expected: Month) -> None:
103
+ result = month - n
104
+ assert result == expected
105
+
106
+ @given(date=dates())
107
+ def test_to_and_from_date(self, *, date: dt.date) -> None:
108
+ month = Month.from_date(date)
109
+ result = month.to_date(day=date.day)
110
+ assert result == date
111
+
112
+ def test_error(self) -> None:
113
+ with raises(MonthError, match=r"Invalid year and month: \d+, \d+"):
114
+ _ = Month(2000, 13)
115
+
116
+
117
+ class TestSerializeAndParseMonth:
118
+ @given(month=months())
119
+ def test_main(self, *, month: Month) -> None:
120
+ serialized = serialize_month(month)
121
+ result = parse_month(serialized)
122
+ assert result == month
123
+
124
+ def test_error_parse(self) -> None:
125
+ with raises(ParseMonthError, match="Unable to parse month; got 'invalid'"):
126
+ _ = parse_month("invalid")
127
+
128
+ @given(data=data(), month=months())
129
+ def test_ensure(self, *, data: DataObject, month: Month) -> None:
130
+ str_or_value = data.draw(sampled_from([month, serialize_month(month)]))
131
+ result = ensure_month(str_or_value)
132
+ assert result == month
133
+
134
+ def test_error_ensure(self) -> None:
135
+ with raises(EnsureMonthError, match="Unable to ensure month; got 'invalid'"):
136
+ _ = ensure_month("invalid")
137
+
138
+
139
+ class TestParseTwoDigitYear:
140
+ @given(data=data(), year=integers(0, 99))
141
+ def test_main(self, *, data: DataObject, year: int) -> None:
142
+ input_ = data.draw(sampled_from([year, str(year)]))
143
+ result = parse_two_digit_year(input_)
144
+ expected = (
145
+ dt.datetime.strptime(format(year, "02d"), "%y").replace(tzinfo=UTC).year
146
+ )
147
+ assert result == expected
148
+
149
+ @given(year=integers(max_value=-1) | integers(min_value=100))
150
+ def test_error_int(self, *, year: int) -> None:
151
+ with raises(
152
+ _ParseTwoDigitYearInvalidIntegerError, match="Unable to parse year; got .*"
153
+ ):
154
+ _ = parse_two_digit_year(year)
155
+
156
+ @given(year=(integers(max_value=-1) | integers(min_value=100)).map(str))
157
+ def test_error_str(self, *, year: str) -> None:
158
+ with raises(
159
+ _ParseTwoDigitYearInvalidStringError, match="Unable to parse year; got .*"
160
+ ):
161
+ _ = parse_two_digit_year(year)
@@ -24,14 +24,12 @@ from hypothesis.strategies import (
24
24
  )
25
25
  from pytest import raises
26
26
 
27
- from utilities.datetime import ZERO_TIME, get_now, get_today
28
27
  from utilities.errors import ImpossibleCaseError
29
28
  from utilities.functions import (
30
29
  EnsureBoolError,
31
30
  EnsureBytesError,
32
31
  EnsureClassError,
33
32
  EnsureDateError,
34
- EnsureDateTimeError,
35
33
  EnsureFloatError,
36
34
  EnsureHashableError,
37
35
  EnsureIntError,
@@ -39,11 +37,13 @@ from utilities.functions import (
39
37
  EnsureNotNoneError,
40
38
  EnsureNumberError,
41
39
  EnsurePathError,
40
+ EnsurePlainDateTimeError,
42
41
  EnsureSizedError,
43
42
  EnsureSizedNotStrError,
44
43
  EnsureStrError,
45
44
  EnsureTimeDeltaError,
46
45
  EnsureTimeError,
46
+ EnsureZonedDateTimeError,
47
47
  MaxNullableError,
48
48
  MinNullableError,
49
49
  apply_decorators,
@@ -51,7 +51,6 @@ from utilities.functions import (
51
51
  ensure_bytes,
52
52
  ensure_class,
53
53
  ensure_date,
54
- ensure_datetime,
55
54
  ensure_float,
56
55
  ensure_hashable,
57
56
  ensure_int,
@@ -59,11 +58,13 @@ from utilities.functions import (
59
58
  ensure_not_none,
60
59
  ensure_number,
61
60
  ensure_path,
61
+ ensure_plain_date_time,
62
62
  ensure_sized,
63
63
  ensure_sized_not_str,
64
64
  ensure_str,
65
65
  ensure_time,
66
- ensure_timedelta,
66
+ ensure_time_delta,
67
+ ensure_zoned_date_time,
67
68
  first,
68
69
  get_class,
69
70
  get_class_name,
@@ -94,11 +95,14 @@ from utilities.functions import (
94
95
  yield_object_properties,
95
96
  )
96
97
  from utilities.sentinel import sentinel
98
+ from utilities.whenever2 import NOW_UTC, ZERO_TIME, get_now, get_today
97
99
 
98
100
  if TYPE_CHECKING:
99
101
  import datetime as dt
100
102
  from collections.abc import Callable, Iterable
101
103
 
104
+ from whenever import PlainDateTime, TimeDelta, ZonedDateTime
105
+
102
106
  from utilities.types import Number
103
107
 
104
108
 
@@ -212,24 +216,6 @@ class TestEnsureDate:
212
216
  _ = ensure_date(sentinel, nullable=nullable)
213
217
 
214
218
 
215
- class TestEnsureDateTime:
216
- @given(case=sampled_from([(get_now(), False), (get_now(), True), (None, True)]))
217
- def test_main(self, *, case: tuple[dt.datetime | None, bool]) -> None:
218
- obj, nullable = case
219
- _ = ensure_datetime(obj, nullable=nullable)
220
-
221
- @given(
222
- case=sampled_from([
223
- (False, "Object '.*' of type '.*' must be a datetime"),
224
- (True, "Object '.*' of type '.*' must be a datetime or None"),
225
- ])
226
- )
227
- def test_error(self, *, case: tuple[bool, str]) -> None:
228
- nullable, match = case
229
- with raises(EnsureDateTimeError, match=match):
230
- _ = ensure_datetime(sentinel, nullable=nullable)
231
-
232
-
233
219
  class TestEnsureFloat:
234
220
  @given(case=sampled_from([(0.0, False), (0.0, True), (None, True)]))
235
221
  def test_main(self, *, case: tuple[float | None, bool]) -> None:
@@ -355,6 +341,30 @@ class TestEnsurePath:
355
341
  _ = ensure_path(sentinel, nullable=nullable)
356
342
 
357
343
 
344
+ class TestEnsurePlainDateTime:
345
+ @given(
346
+ case=sampled_from([
347
+ (NOW_UTC.to_plain(), False),
348
+ (NOW_UTC.to_plain(), True),
349
+ (None, True),
350
+ ])
351
+ )
352
+ def test_main(self, *, case: tuple[PlainDateTime | None, bool]) -> None:
353
+ obj, nullable = case
354
+ _ = ensure_plain_date_time(obj, nullable=nullable)
355
+
356
+ @given(
357
+ case=sampled_from([
358
+ (False, "Object '.*' of type '.*' must be a plain date-time"),
359
+ (True, "Object '.*' of type '.*' must be a plain date-time or None"),
360
+ ])
361
+ )
362
+ def test_error(self, *, case: tuple[bool, str]) -> None:
363
+ nullable, match = case
364
+ with raises(EnsurePlainDateTimeError, match=match):
365
+ _ = ensure_plain_date_time(sentinel, nullable=nullable)
366
+
367
+
358
368
  class TestEnsureSized:
359
369
  @given(obj=sampled_from([[], (), ""]))
360
370
  def test_main(self, *, obj: Any) -> None:
@@ -423,20 +433,38 @@ class TestEnsureTime:
423
433
 
424
434
  class TestEnsureTimeDelta:
425
435
  @given(case=sampled_from([(ZERO_TIME, False), (ZERO_TIME, True), (None, True)]))
426
- def test_main(self, *, case: tuple[dt.timedelta | None, bool]) -> None:
436
+ def test_main(self, *, case: tuple[TimeDelta | None, bool]) -> None:
427
437
  obj, nullable = case
428
- _ = ensure_timedelta(obj, nullable=nullable)
438
+ _ = ensure_time_delta(obj, nullable=nullable)
429
439
 
430
440
  @given(
431
441
  case=sampled_from([
432
- (False, "Object '.*' of type '.*' must be a timedelta"),
433
- (True, "Object '.*' of type '.*' must be a timedelta or None"),
442
+ (False, "Object '.*' of type '.*' must be a time-delta"),
443
+ (True, "Object '.*' of type '.*' must be a time-delta or None"),
434
444
  ])
435
445
  )
436
446
  def test_error(self, *, case: tuple[bool, str]) -> None:
437
447
  nullable, match = case
438
448
  with raises(EnsureTimeDeltaError, match=match):
439
- _ = ensure_timedelta(sentinel, nullable=nullable)
449
+ _ = ensure_time_delta(sentinel, nullable=nullable)
450
+
451
+
452
+ class TestEnsureZonedDateTime:
453
+ @given(case=sampled_from([(NOW_UTC, False), (NOW_UTC, True), (None, True)]))
454
+ def test_main(self, *, case: tuple[ZonedDateTime | None, bool]) -> None:
455
+ obj, nullable = case
456
+ _ = ensure_zoned_date_time(obj, nullable=nullable)
457
+
458
+ @given(
459
+ case=sampled_from([
460
+ (False, "Object '.*' of type '.*' must be a zoned date-time"),
461
+ (True, "Object '.*' of type '.*' must be a zoned date-time or None"),
462
+ ])
463
+ )
464
+ def test_error(self, *, case: tuple[bool, str]) -> None:
465
+ nullable, match = case
466
+ with raises(EnsureZonedDateTimeError, match=match):
467
+ _ = ensure_zoned_date_time(sentinel, nullable=nullable)
440
468
 
441
469
 
442
470
  class TestFirst:
@@ -2,13 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from ipaddress import IPv4Address
4
4
 
5
- from utilities.datetime import MINUTE
6
5
  from utilities.http import get_public_ip, yield_connection
7
6
  from utilities.pytest import throttle
7
+ from utilities.whenever2 import MINUTE
8
8
 
9
9
 
10
10
  class TestGetPublicIP:
11
- @throttle(duration=5 * MINUTE)
11
+ @throttle(delta=5 * MINUTE)
12
12
  def test_main(self) -> None:
13
13
  ip = get_public_ip(timeout=10.0)
14
14
  assert isinstance(ip, IPv4Address)