dycw-utilities 0.131.1__tar.gz → 0.131.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/PKG-INFO +1 -1
  2. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/pyproject.toml +3 -2
  3. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_hypothesis.py +80 -0
  4. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_whenever.py +1 -20
  5. dycw_utilities-0.131.2/src/tests/test_whenever2.py +194 -0
  6. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/__init__.py +1 -1
  7. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/hypothesis.py +162 -3
  8. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/logging.py +1 -1
  9. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/typing.py +1 -1
  10. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/whenever.py +1 -64
  11. dycw_utilities-0.131.2/src/utilities/whenever2.py +139 -0
  12. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/.gitignore +0 -0
  13. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/LICENSE +0 -0
  14. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/README.md +0 -0
  15. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/__init__.py +0 -0
  16. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/conftest.py +0 -0
  17. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/__init__.py +0 -0
  18. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_missing/__init__.py +0 -0
  19. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_missing/module.py +0 -0
  20. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/__init__.py +0 -0
  21. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/outer_1.py +0 -0
  22. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/outer_2.py +0 -0
  23. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  24. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  25. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  26. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  27. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_without/__init__.py +0 -0
  28. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_without/module_1.py +0 -0
  29. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/package_without/module_2.py +0 -0
  30. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/standalone.py +0 -0
  31. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/modules/with_imports.py +0 -0
  32. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  33. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  34. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  35. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  36. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  37. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  38. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  39. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  40. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_aiolimiter.py +0 -0
  41. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_altair.py +0 -0
  42. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_asyncio.py +0 -0
  43. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_asyncio_classes/__init__.py +0 -0
  44. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_asyncio_classes/loopers.py +0 -0
  45. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_asyncio_classes/redis.py +0 -0
  46. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_atomicwrites.py +0 -0
  47. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_atools.py +0 -0
  48. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_cachetools.py +0 -0
  49. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_click.py +0 -0
  50. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_concurrent.py +0 -0
  51. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_contextlib.py +0 -0
  52. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_contextvars.py +0 -0
  53. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_cryptography.py +0 -0
  54. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_cvxpy.py +0 -0
  55. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_dataclasses.py +0 -0
  56. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_datetime.py +0 -0
  57. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_enum.py +0 -0
  58. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_errors.py +0 -0
  59. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_eventkit.py +0 -0
  60. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_fastapi.py +0 -0
  61. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_fpdf2.py +0 -0
  62. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_functions.py +0 -0
  63. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_functools.py +0 -0
  64. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_getpass.py +0 -0
  65. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_git.py +0 -0
  66. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_hashlib.py +0 -0
  67. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_http.py +0 -0
  68. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_importlib.py +0 -0
  69. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_inflect.py +0 -0
  70. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_ipython.py +0 -0
  71. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_iterables.py +0 -0
  72. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_jupyter.py +0 -0
  73. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_libcst.py +0 -0
  74. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_lightweight_charts.py +0 -0
  75. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_logging.py +0 -0
  76. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_luigi.py +0 -0
  77. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_math.py +0 -0
  78. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_memory_profiler.py +0 -0
  79. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_modules.py +0 -0
  80. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_more_itertools.py +0 -0
  81. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_numpy.py +0 -0
  82. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_operator.py +0 -0
  83. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_optuna.py +0 -0
  84. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_orjson.py +0 -0
  85. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_os.py +0 -0
  86. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_parse.py +0 -0
  87. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pathlib.py +0 -0
  88. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_period.py +0 -0
  89. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pickle.py +0 -0
  90. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_platform.py +0 -0
  91. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_polars.py +0 -0
  92. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_polars_ols.py +0 -0
  93. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pottery.py +0 -0
  94. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pqdm.py +0 -0
  95. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_psutil.py +0 -0
  96. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pydantic.py +0 -0
  97. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pyinstrument.py +0 -0
  98. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pyrsistent.py +0 -0
  99. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pytest.py +0 -0
  100. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_pytest_regressions.py +0 -0
  101. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_python_dotenv.py +0 -0
  102. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_random.py +0 -0
  103. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_re.py +0 -0
  104. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_redis.py +0 -0
  105. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_reprlib.py +0 -0
  106. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_scipy.py +0 -0
  107. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_sentinel.py +0 -0
  108. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_shelve.py +0 -0
  109. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_slack_sdk.py +0 -0
  110. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_socket.py +0 -0
  111. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_sqlalchemy.py +0 -0
  112. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_statsmodel.py +0 -0
  114. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_string.py +0 -0
  116. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_tempfile.py +0 -0
  117. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_tenacity.py +0 -0
  118. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_text.py +0 -0
  119. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_threading.py +0 -0
  120. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_timer.py +0 -0
  121. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_traceback.py +0 -0
  122. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_types.py +0 -0
  123. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_typing.py +0 -0
  124. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_typing_funcs/__init__.py +0 -0
  125. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_typing_funcs/no_future.py +0 -0
  126. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_typing_funcs/with_future.py +0 -0
  127. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_tzdata.py +0 -0
  128. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_tzlocal.py +0 -0
  129. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_uuid.py +0 -0
  130. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_version.py +0 -0
  131. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_warnings.py +0 -0
  132. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_zipfile.py +0 -0
  133. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/tests/test_zoneinfo.py +0 -0
  134. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/aiolimiter.py +0 -0
  135. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/altair.py +0 -0
  136. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/asyncio.py +0 -0
  137. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/click.py +0 -0
  141. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/concurrent.py +0 -0
  142. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/contextlib.py +0 -0
  143. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/contextvars.py +0 -0
  144. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/cryptography.py +0 -0
  145. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/cvxpy.py +0 -0
  146. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/dataclasses.py +0 -0
  147. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/datetime.py +0 -0
  148. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/enum.py +0 -0
  149. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/errors.py +0 -0
  150. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/eventkit.py +0 -0
  151. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/fastapi.py +0 -0
  152. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/fpdf2.py +0 -0
  153. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/functions.py +0 -0
  154. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/functools.py +0 -0
  155. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/getpass.py +0 -0
  156. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/git.py +0 -0
  157. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/hashlib.py +0 -0
  158. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/http.py +0 -0
  159. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/importlib.py +0 -0
  160. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/inflect.py +0 -0
  161. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/ipython.py +0 -0
  162. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/iterables.py +0 -0
  163. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/jupyter.py +0 -0
  164. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/libcst.py +0 -0
  165. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/lightweight_charts.py +0 -0
  166. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/luigi.py +0 -0
  167. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/math.py +0 -0
  168. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/memory_profiler.py +0 -0
  169. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/modules.py +0 -0
  170. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/more_itertools.py +0 -0
  171. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/numpy.py +0 -0
  172. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/operator.py +0 -0
  173. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/optuna.py +0 -0
  174. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/orjson.py +0 -0
  175. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/os.py +0 -0
  176. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/parse.py +0 -0
  177. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pathlib.py +0 -0
  178. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/period.py +0 -0
  179. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pickle.py +0 -0
  180. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/platform.py +0 -0
  181. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/polars.py +0 -0
  182. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/polars_ols.py +0 -0
  183. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pottery.py +0 -0
  184. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pqdm.py +0 -0
  185. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/psutil.py +0 -0
  186. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/py.typed +0 -0
  187. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pydantic.py +0 -0
  188. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pyinstrument.py +0 -0
  189. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pyrsistent.py +0 -0
  190. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pytest.py +0 -0
  191. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/pytest_regressions.py +0 -0
  192. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/python_dotenv.py +0 -0
  193. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/random.py +0 -0
  194. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/re.py +0 -0
  195. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/redis.py +0 -0
  196. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/reprlib.py +0 -0
  197. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/scipy.py +0 -0
  198. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/sentinel.py +0 -0
  199. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/shelve.py +0 -0
  200. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/slack_sdk.py +0 -0
  201. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/socket.py +0 -0
  202. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/sqlalchemy.py +0 -0
  203. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/sqlalchemy_polars.py +0 -0
  204. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/statsmodels.py +0 -0
  205. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/streamlit.py +0 -0
  206. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/string.py +0 -0
  207. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/tempfile.py +0 -0
  208. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/tenacity.py +0 -0
  209. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/text.py +0 -0
  210. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/threading.py +0 -0
  211. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/timer.py +0 -0
  212. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/traceback.py +0 -0
  213. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/types.py +0 -0
  214. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/tzdata.py +0 -0
  215. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/tzlocal.py +0 -0
  216. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/uuid.py +0 -0
  217. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/version.py +0 -0
  218. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/warnings.py +0 -0
  219. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/zipfile.py +0 -0
  220. {dycw_utilities-0.131.1 → dycw_utilities-0.131.2}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.131.1
3
+ Version: 0.131.2
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.1"
97
+ version = "0.131.2"
98
98
 
99
99
  [project.optional-dependencies]
100
100
  logging = [
@@ -341,7 +341,7 @@ zzz-test-zoneinfo = [
341
341
  # bump-my-version
342
342
  [tool.bumpversion]
343
343
  allow_dirty = true
344
- current_version = "0.131.1"
344
+ current_version = "0.131.2"
345
345
 
346
346
  [[tool.bumpversion.files]]
347
347
  filename = "src/utilities/__init__.py"
@@ -354,6 +354,7 @@ search = "__version__ = \"{current_version}\""
354
354
  [tool.coverage.coverage_conditional_plugin.rules]
355
355
  skipif-ci = '"CI" in os_environ'
356
356
  skipif-ci-and-not-linux = '("CI" in os_environ) and (sys_platform != "linux")'
357
+ skipif-ci-and-not-windows = '("CI" in os_environ) and (sys_platform != "windows")'
357
358
  skipif-ci-and-windows = '("CI" in os_environ) and (sys_platform != "windows")'
358
359
  skipif-linux = 'sys_platform == "linux"'
359
360
  skipif-mac = 'sys_platform == "darwin"'
@@ -28,6 +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
32
 
32
33
  from tests.conftest import SKIPIF_CI_AND_WINDOWS
33
34
  from utilities.datetime import (
@@ -50,8 +51,10 @@ from utilities.hypothesis import (
50
51
  _Draw2InputResolvedToSentinelError,
51
52
  assume_does_not_raise,
52
53
  bool_arrays,
54
+ date_deltas_whenever,
53
55
  date_durations,
54
56
  dates_two_digit_year,
57
+ dates_whenever,
55
58
  datetime_durations,
56
59
  draw2,
57
60
  float32s,
@@ -73,6 +76,7 @@ from utilities.hypothesis import (
73
76
  pairs,
74
77
  paths,
75
78
  plain_datetimes,
79
+ plain_datetimes_whenever,
76
80
  random_states,
77
81
  sentinels,
78
82
  sets_fixed_length,
@@ -94,6 +98,7 @@ from utilities.hypothesis import (
94
98
  uint64s,
95
99
  versions,
96
100
  zoned_datetimes,
101
+ zoned_datetimes_whenever,
97
102
  )
98
103
  from utilities.math import (
99
104
  MAX_FLOAT32,
@@ -178,6 +183,31 @@ class TestBoolArrays:
178
183
  assert array.shape == shape
179
184
 
180
185
 
186
+ class TestDateDeltas:
187
+ @given(data=data())
188
+ def test_main(self, *, data: DataObject) -> None:
189
+ min_value = data.draw(date_deltas_whenever() | none())
190
+ max_value = data.draw(date_deltas_whenever() | none())
191
+ with assume_does_not_raise(InvalidArgument):
192
+ delta = data.draw(
193
+ date_deltas_whenever(min_value=min_value, max_value=max_value)
194
+ )
195
+ assert isinstance(delta, DateDelta)
196
+ years, months, days = delta.in_years_months_days()
197
+ assert years == 0
198
+ assert months == 0
199
+ if min_value is not None:
200
+ min_years, min_months, min_days = min_value.in_years_months_days()
201
+ assert min_years == 0
202
+ assert min_months == 0
203
+ assert days >= min_days
204
+ if max_value is not None:
205
+ max_years, max_months, max_days = max_value.in_years_months_days()
206
+ assert max_years == 0
207
+ assert max_months == 0
208
+ assert days <= max_days
209
+
210
+
181
211
  class TestDateDurations:
182
212
  @given(
183
213
  data=data(),
@@ -257,6 +287,20 @@ class TestDatesTwoDigitYear:
257
287
  assert date.year == parsed
258
288
 
259
289
 
290
+ class TestDatesWhenever:
291
+ @given(data=data())
292
+ def test_main(self, *, data: DataObject) -> None:
293
+ min_value = data.draw(dates_whenever() | none())
294
+ max_value = data.draw(dates_whenever() | none())
295
+ with assume_does_not_raise(InvalidArgument):
296
+ date = data.draw(dates_whenever(min_value=min_value, max_value=max_value))
297
+ assert isinstance(date, Date)
298
+ if min_value is not None:
299
+ assert date >= min_value
300
+ if max_value is not None:
301
+ assert date <= max_value
302
+
303
+
260
304
  class TestDateTimeDurations:
261
305
  @given(
262
306
  data=data(),
@@ -840,6 +884,22 @@ class TestPlainDateTimes:
840
884
  _ = data.draw(plain_datetimes(round_="standard"))
841
885
 
842
886
 
887
+ class TestPlainDateTimesWhenever:
888
+ @given(data=data())
889
+ def test_main(self, *, data: DataObject) -> None:
890
+ min_value = data.draw(plain_datetimes_whenever() | none())
891
+ max_value = data.draw(plain_datetimes_whenever() | none())
892
+ with assume_does_not_raise(InvalidArgument):
893
+ datetime = data.draw(
894
+ plain_datetimes_whenever(min_value=min_value, max_value=max_value)
895
+ )
896
+ assert isinstance(datetime, PlainDateTime)
897
+ if min_value is not None:
898
+ assert datetime >= min_value
899
+ if max_value is not None:
900
+ assert datetime <= max_value
901
+
902
+
843
903
  class TestRandomStates:
844
904
  @given(data=data())
845
905
  def test_main(self, *, data: DataObject) -> None:
@@ -1198,3 +1258,23 @@ class TestZonedDateTimes:
1198
1258
  ZonedDateTimesError, match="Rounding requires a timedelta; got None"
1199
1259
  ):
1200
1260
  _ = data.draw(zoned_datetimes(round_="standard"))
1261
+
1262
+
1263
+ class TestZonedDateTimesWhenever:
1264
+ @given(data=data(), time_zone=timezones())
1265
+ @settings(suppress_health_check={HealthCheck.filter_too_much})
1266
+ def test_main(self, *, data: DataObject, time_zone: ZoneInfo) -> None:
1267
+ min_value = data.draw(zoned_datetimes_whenever() | none())
1268
+ max_value = data.draw(zoned_datetimes_whenever() | none())
1269
+ with assume_does_not_raise(InvalidArgument):
1270
+ datetime = data.draw(
1271
+ zoned_datetimes_whenever(
1272
+ min_value=min_value, max_value=max_value, time_zone=time_zone
1273
+ )
1274
+ )
1275
+ assert isinstance(datetime, ZonedDateTime)
1276
+ assert datetime.tz == time_zone.key
1277
+ if min_value is not None:
1278
+ assert datetime >= min_value
1279
+ if max_value is not None:
1280
+ assert datetime <= max_value
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
4
  from datetime import timezone
5
- from logging import DEBUG
6
5
  from re import escape
7
6
  from typing import TYPE_CHECKING
8
7
  from zoneinfo import ZoneInfo
@@ -21,7 +20,7 @@ from hypothesis.strategies import (
21
20
  timezones,
22
21
  )
23
22
  from pytest import raises
24
- from whenever import DateTimeDelta, ZonedDateTime
23
+ from whenever import DateTimeDelta
25
24
 
26
25
  from tests.conftest import SKIPIF_CI_AND_WINDOWS
27
26
  from utilities.datetime import (
@@ -61,7 +60,6 @@ from utilities.whenever import (
61
60
  SerializePlainDateTimeError,
62
61
  SerializeTimeDeltaError,
63
62
  SerializeZonedDateTimeError,
64
- WheneverLogRecord,
65
63
  _CheckValidZonedDateTimeUnequalError,
66
64
  _EnsureTimedeltaNanosecondError,
67
65
  _EnsureTimedeltaParseError,
@@ -495,20 +493,3 @@ class TestToDateTimeDelta:
495
493
  _ToDateTimeDeltaError, match="Unable to create DateTimeDelta; got .*"
496
494
  ):
497
495
  _ = _to_datetime_delta(timedelta)
498
-
499
-
500
- class TestWheneverLogRecord:
501
- def test_init(self) -> None:
502
- _ = WheneverLogRecord("name", DEBUG, "pathname", 0, None, None, None)
503
-
504
- def test_get_length(self) -> None:
505
- assert isinstance(WheneverLogRecord._get_length(), int)
506
-
507
- def test_get_now(self) -> None:
508
- assert isinstance(WheneverLogRecord._get_now(), ZonedDateTime)
509
-
510
- def test_get_time_zone(self) -> None:
511
- assert isinstance(WheneverLogRecord._get_time_zone(), ZoneInfo)
512
-
513
- def test_get_time_zone_key(self) -> None:
514
- assert isinstance(WheneverLogRecord._get_time_zone_key(), str)
@@ -0,0 +1,194 @@
1
+ from __future__ import annotations
2
+
3
+ from logging import DEBUG
4
+ from zoneinfo import ZoneInfo
5
+
6
+ from hypothesis import given
7
+ from hypothesis.strategies import just, timezones
8
+ from pytest import mark, param, raises
9
+ from whenever import DateDelta, DateTimeDelta, TimeDelta, ZonedDateTime
10
+
11
+ from tests.conftest import IS_CI
12
+ from utilities.hypothesis import zoned_datetimes_whenever
13
+ from utilities.tzdata import HongKong, Tokyo
14
+ from utilities.whenever2 import (
15
+ DATE_DELTA_MAX,
16
+ DATE_DELTA_MIN,
17
+ DATE_MAX,
18
+ DATE_MIN,
19
+ DATE_TIME_DELTA_MAX,
20
+ DATE_TIME_DELTA_MIN,
21
+ NOW_UTC,
22
+ PLAIN_DATE_TIME_MAX,
23
+ PLAIN_DATE_TIME_MIN,
24
+ TIME_DELTA_MAX,
25
+ TIME_DELTA_MIN,
26
+ ZONED_DATE_TIME_MAX,
27
+ ZONED_DATE_TIME_MIN,
28
+ WheneverLogRecord,
29
+ from_timestamp,
30
+ from_timestamp_millis,
31
+ from_timestamp_nanos,
32
+ get_now,
33
+ get_now_local,
34
+ )
35
+ from utilities.zoneinfo import UTC
36
+
37
+
38
+ class TestFromTimeStamp:
39
+ @given(datetime=zoned_datetimes_whenever(time_zone=UTC if IS_CI else timezones()))
40
+ def test_main(self, *, datetime: ZonedDateTime) -> None:
41
+ datetime = datetime.round("second")
42
+ timestamp = datetime.to_tz(UTC.key).timestamp()
43
+ result = from_timestamp(timestamp, time_zone=ZoneInfo(datetime.tz))
44
+ assert result == datetime
45
+
46
+ @given(datetime=zoned_datetimes_whenever(time_zone=UTC if IS_CI else timezones()))
47
+ def test_millis(self, *, datetime: ZonedDateTime) -> None:
48
+ datetime = datetime.round("millisecond")
49
+ timestamp = datetime.to_tz(UTC.key).timestamp_millis()
50
+ result = from_timestamp_millis(timestamp, time_zone=ZoneInfo(datetime.tz))
51
+ assert result == datetime
52
+
53
+ @given(datetime=zoned_datetimes_whenever(time_zone=UTC if IS_CI else timezones()))
54
+ def test_nanos(self, *, datetime: ZonedDateTime) -> None:
55
+ timestamp = datetime.to_tz(UTC.key).timestamp_nanos()
56
+ result = from_timestamp_nanos(timestamp, time_zone=ZoneInfo(datetime.tz))
57
+ assert result == datetime
58
+
59
+
60
+ class TestGetNow:
61
+ @given(time_zone=just(UTC) if IS_CI else timezones())
62
+ def test_function(self, *, time_zone: ZoneInfo) -> None:
63
+ now = get_now(time_zone=time_zone)
64
+ assert isinstance(now, ZonedDateTime)
65
+ assert now.tz == time_zone.key
66
+
67
+ def test_constant(self) -> None:
68
+ assert isinstance(NOW_UTC, ZonedDateTime)
69
+ assert NOW_UTC.tz == "UTC"
70
+
71
+
72
+ class TestGetNowLocal:
73
+ def test_function(self) -> None:
74
+ now = get_now_local()
75
+ assert isinstance(now, ZonedDateTime)
76
+ ETC = ZoneInfo("Etc/UTC") # noqa: N806
77
+ time_zones = {ETC, HongKong, Tokyo, UTC}
78
+ assert any(now.tz == time_zone.key for time_zone in time_zones)
79
+
80
+
81
+ class TestMinMax:
82
+ def test_date_min(self) -> None:
83
+ with raises(ValueError, match="Resulting date out of range"):
84
+ _ = DATE_MIN - DateDelta(days=1)
85
+
86
+ def test_date_max(self) -> None:
87
+ with raises(ValueError, match="Resulting date out of range"):
88
+ _ = DATE_MAX + DateDelta(days=1)
89
+
90
+ def test_date_delta_min(self) -> None:
91
+ with raises(ValueError, match="Addition result out of bounds"):
92
+ _ = DATE_DELTA_MIN - DateDelta(days=1)
93
+
94
+ def test_date_delta_max(self) -> None:
95
+ with raises(ValueError, match="Addition result out of bounds"):
96
+ _ = DATE_DELTA_MAX + DateDelta(days=1)
97
+
98
+ @mark.parametrize(
99
+ "delta",
100
+ [
101
+ param(DateTimeDelta(days=1)),
102
+ param(DateTimeDelta(seconds=1)),
103
+ param(DateTimeDelta(milliseconds=1)),
104
+ param(DateTimeDelta(microseconds=1)),
105
+ param(DateTimeDelta(nanoseconds=1)),
106
+ ],
107
+ )
108
+ def test_date_time_delta_min(self, *, delta: DateTimeDelta) -> None:
109
+ with raises(ValueError, match="Addition result out of bounds"):
110
+ _ = DATE_TIME_DELTA_MIN - delta
111
+
112
+ @mark.parametrize(
113
+ ("delta", "is_ok"),
114
+ [
115
+ param(DateTimeDelta(days=1), False),
116
+ param(DateTimeDelta(seconds=1), False),
117
+ param(DateTimeDelta(milliseconds=999), True),
118
+ param(DateTimeDelta(milliseconds=1000), False),
119
+ param(DateTimeDelta(microseconds=999_999), True),
120
+ param(DateTimeDelta(microseconds=1_000_000), False),
121
+ param(DateTimeDelta(nanoseconds=999_999_999), True),
122
+ param(DateTimeDelta(nanoseconds=1_000_000_000), False),
123
+ ],
124
+ )
125
+ def test_date_time_delta_max(self, *, delta: DateTimeDelta, is_ok: bool) -> None:
126
+ if is_ok:
127
+ _ = DATE_TIME_DELTA_MAX + delta
128
+ else:
129
+ with raises(ValueError, match="Addition result out of bounds"):
130
+ _ = DATE_TIME_DELTA_MAX + delta
131
+
132
+ def test_plain_date_time_min(self) -> None:
133
+ with raises(ValueError, match=r"Result of subtract\(\) out of range"):
134
+ _ = PLAIN_DATE_TIME_MIN.subtract(nanoseconds=1, ignore_dst=True)
135
+
136
+ def test_plain_date_time_max(self) -> None:
137
+ _ = PLAIN_DATE_TIME_MAX.add(nanoseconds=999, ignore_dst=True)
138
+ with raises(ValueError, match=r"Result of add\(\) out of range"):
139
+ _ = PLAIN_DATE_TIME_MAX.add(microseconds=1, ignore_dst=True)
140
+
141
+ @mark.parametrize(
142
+ "delta",
143
+ [
144
+ param(TimeDelta(seconds=1)),
145
+ param(TimeDelta(milliseconds=1)),
146
+ param(TimeDelta(microseconds=1)),
147
+ param(TimeDelta(nanoseconds=1)),
148
+ ],
149
+ )
150
+ def test_time_delta_min(self, *, delta: TimeDelta) -> None:
151
+ with raises(ValueError, match="Addition result out of range"):
152
+ _ = TIME_DELTA_MIN - delta
153
+
154
+ @mark.parametrize(
155
+ ("delta", "is_ok"),
156
+ [
157
+ param(TimeDelta(seconds=1), False),
158
+ param(TimeDelta(milliseconds=999), True),
159
+ param(TimeDelta(milliseconds=1000), False),
160
+ param(TimeDelta(microseconds=999_999), True),
161
+ param(TimeDelta(microseconds=1_000_000), False),
162
+ param(TimeDelta(nanoseconds=999_999_999), True),
163
+ param(TimeDelta(nanoseconds=1_000_000_000), False),
164
+ ],
165
+ )
166
+ def test_time_delta_max(self, *, delta: TimeDelta, is_ok: bool) -> None:
167
+ if is_ok:
168
+ _ = TIME_DELTA_MAX + delta
169
+ else:
170
+ with raises(ValueError, match="Addition result out of range"):
171
+ _ = TIME_DELTA_MAX + delta
172
+
173
+ def test_zoned_date_time_min(self) -> None:
174
+ with raises(ValueError, match="Instant is out of range"):
175
+ _ = ZONED_DATE_TIME_MIN.subtract(nanoseconds=1)
176
+
177
+ def test_zoned_date_time_max(self) -> None:
178
+ _ = ZONED_DATE_TIME_MAX.add(nanoseconds=999)
179
+ with raises(ValueError, match="Instant is out of range"):
180
+ _ = ZONED_DATE_TIME_MAX.add(microseconds=1)
181
+
182
+
183
+ class TestWheneverLogRecord:
184
+ def test_init(self) -> None:
185
+ _ = WheneverLogRecord("name", DEBUG, "pathname", 0, None, None, None)
186
+
187
+ def test_get_length(self) -> None:
188
+ assert isinstance(WheneverLogRecord._get_length(), int)
189
+
190
+ def test_get_time_zone(self) -> None:
191
+ assert isinstance(WheneverLogRecord._get_time_zone(), ZoneInfo)
192
+
193
+ def test_get_time_zone_key(self) -> None:
194
+ assert isinstance(WheneverLogRecord._get_time_zone_key(), str)
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.131.1"
3
+ __version__ = "0.131.2"
@@ -47,6 +47,7 @@ from hypothesis.strategies import (
47
47
  uuids,
48
48
  )
49
49
  from hypothesis.utils.conventions import not_set
50
+ from whenever import Date, DateDelta
50
51
 
51
52
  from utilities.datetime import (
52
53
  DATETIME_MAX_NAIVE,
@@ -88,7 +89,7 @@ from utilities.platform import IS_WINDOWS
88
89
  from utilities.sentinel import Sentinel, sentinel
89
90
  from utilities.tempfile import TEMP_DIR, TemporaryDirectory
90
91
  from utilities.version import Version
91
- from utilities.zoneinfo import UTC
92
+ from utilities.zoneinfo import UTC, ensure_time_zone
92
93
 
93
94
  if TYPE_CHECKING:
94
95
  from collections.abc import Collection, Hashable, Iterable, Iterator, Sequence
@@ -96,9 +97,10 @@ if TYPE_CHECKING:
96
97
 
97
98
  from hypothesis.database import ExampleDatabase
98
99
  from numpy.random import RandomState
100
+ from whenever import PlainDateTime, ZonedDateTime
99
101
 
100
102
  from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
101
- from utilities.types import Duration, Number, RoundMode
103
+ from utilities.types import Duration, Number, RoundMode, TimeZoneLike
102
104
 
103
105
 
104
106
  _T = TypeVar("_T")
@@ -158,6 +160,45 @@ def bool_arrays(
158
160
  ##
159
161
 
160
162
 
163
+ @composite
164
+ def date_deltas_whenever(
165
+ draw: DrawFn,
166
+ /,
167
+ *,
168
+ min_value: MaybeSearchStrategy[DateDelta | None] = None,
169
+ max_value: MaybeSearchStrategy[DateDelta | None] = None,
170
+ ) -> DateDelta:
171
+ """Strategy for generating date deltas."""
172
+ from utilities.whenever2 import DATE_DELTA_MAX, DATE_DELTA_MIN
173
+
174
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
175
+ match min_value_:
176
+ case None:
177
+ min_value_ = DATE_DELTA_MIN
178
+ case DateDelta():
179
+ ...
180
+ case _ as never:
181
+ assert_never(never)
182
+ match max_value_:
183
+ case None:
184
+ max_value_ = DATE_DELTA_MAX
185
+ case DateDelta():
186
+ ...
187
+ case _ as never:
188
+ assert_never(never)
189
+ min_years, min_months, min_days = min_value_.in_years_months_days()
190
+ assert min_years == 0
191
+ assert min_months == 0
192
+ max_years, max_months, max_days = max_value_.in_years_months_days()
193
+ assert max_years == 0
194
+ assert max_months == 0
195
+ days = draw(integers(min_value=min_days, max_value=max_days))
196
+ return DateDelta(days=days)
197
+
198
+
199
+ ##
200
+
201
+
161
202
  @composite
162
203
  def date_durations(
163
204
  draw: DrawFn,
@@ -238,6 +279,41 @@ def dates_two_digit_year(
238
279
  ##
239
280
 
240
281
 
282
+ @composite
283
+ def dates_whenever(
284
+ draw: DrawFn,
285
+ /,
286
+ *,
287
+ min_value: MaybeSearchStrategy[Date | None] = None,
288
+ max_value: MaybeSearchStrategy[Date | None] = None,
289
+ ) -> Date:
290
+ """Strategy for generating dates."""
291
+ from utilities.whenever2 import DATE_MAX, DATE_MIN
292
+
293
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
294
+ match min_value_:
295
+ case None:
296
+ min_value_ = DATE_MIN
297
+ case Date():
298
+ ...
299
+ case _ as never:
300
+ assert_never(never)
301
+ match max_value_:
302
+ case None:
303
+ max_value_ = DATE_MAX
304
+ case Date():
305
+ ...
306
+ case _ as never:
307
+ assert_never(never)
308
+ py_date = draw(
309
+ dates(min_value=min_value_.py_date(), max_value=max_value_.py_date())
310
+ )
311
+ return Date.from_py_date(py_date)
312
+
313
+
314
+ ##
315
+
316
+
241
317
  @composite
242
318
  def datetime_durations(
243
319
  draw: DrawFn,
@@ -920,7 +996,7 @@ def _pairs_map(elements: list[_T], /) -> tuple[_T, _T]:
920
996
 
921
997
  def paths() -> SearchStrategy[Path]:
922
998
  """Strategy for generating `Path`s."""
923
- reserved = {"NUL"}
999
+ reserved = {"AUX", "NUL"}
924
1000
  strategy = text_ascii(min_size=1, max_size=10).filter(lambda x: x not in reserved)
925
1001
  return lists(strategy, max_size=10).map(lambda parts: Path(*parts))
926
1002
 
@@ -965,6 +1041,45 @@ class PlainDateTimesError(Exception):
965
1041
  ##
966
1042
 
967
1043
 
1044
+ @composite
1045
+ def plain_datetimes_whenever(
1046
+ draw: DrawFn,
1047
+ /,
1048
+ *,
1049
+ min_value: MaybeSearchStrategy[PlainDateTime | None] = None,
1050
+ max_value: MaybeSearchStrategy[PlainDateTime | None] = None,
1051
+ ) -> PlainDateTime:
1052
+ """Strategy for generating plain datetimes."""
1053
+ from whenever import PlainDateTime
1054
+
1055
+ from utilities.whenever2 import PLAIN_DATE_TIME_MAX, PLAIN_DATE_TIME_MIN
1056
+
1057
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1058
+ match min_value_:
1059
+ case None:
1060
+ min_value_ = PLAIN_DATE_TIME_MIN
1061
+ case PlainDateTime():
1062
+ ...
1063
+ case _ as never:
1064
+ assert_never(never)
1065
+ match max_value_:
1066
+ case None:
1067
+ max_value_ = PLAIN_DATE_TIME_MAX
1068
+ case PlainDateTime():
1069
+ ...
1070
+ case _ as never:
1071
+ assert_never(never)
1072
+ py_datetime = draw(
1073
+ datetimes(
1074
+ min_value=min_value_.py_datetime(), max_value=max_value_.py_datetime()
1075
+ )
1076
+ )
1077
+ return PlainDateTime.from_py_datetime(py_datetime)
1078
+
1079
+
1080
+ ##
1081
+
1082
+
968
1083
  @composite
969
1084
  def random_states(
970
1085
  draw: DrawFn, /, *, seed: MaybeSearchStrategy[int | None] = None
@@ -1427,6 +1542,46 @@ class ZonedDateTimesError(Exception):
1427
1542
  return "Rounding requires a timedelta; got None"
1428
1543
 
1429
1544
 
1545
+ ##
1546
+
1547
+
1548
+ @composite
1549
+ def zoned_datetimes_whenever(
1550
+ draw: DrawFn,
1551
+ /,
1552
+ *,
1553
+ min_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
1554
+ max_value: MaybeSearchStrategy[PlainDateTime | ZonedDateTime | None] = None,
1555
+ time_zone: MaybeSearchStrategy[TimeZoneLike] = UTC,
1556
+ ) -> ZonedDateTime:
1557
+ """Strategy for generating zoned datetimes."""
1558
+ from whenever import PlainDateTime, ZonedDateTime
1559
+
1560
+ min_value_, max_value_ = [draw2(draw, v) for v in [min_value, max_value]]
1561
+ time_zone_ = ensure_time_zone(draw2(draw, time_zone))
1562
+ match min_value_:
1563
+ case None | PlainDateTime():
1564
+ ...
1565
+ case ZonedDateTime():
1566
+ with assume_does_not_raise(ValueError):
1567
+ min_value_ = min_value_.to_tz(time_zone_.key).to_plain()
1568
+ case _ as never:
1569
+ assert_never(never)
1570
+ match max_value_:
1571
+ case None | PlainDateTime():
1572
+ ...
1573
+ case ZonedDateTime():
1574
+ with assume_does_not_raise(ValueError):
1575
+ max_value_ = max_value_.to_tz(time_zone_.key).to_plain()
1576
+ case _ as never:
1577
+ assert_never(never)
1578
+ plain_datetime = draw(
1579
+ plain_datetimes_whenever(min_value=min_value_, max_value=max_value_)
1580
+ )
1581
+ with assume_does_not_raise(ValueError):
1582
+ return plain_datetime.assume_tz(time_zone_.key, disambiguate="raise")
1583
+
1584
+
1430
1585
  __all__ = [
1431
1586
  "Draw2Error",
1432
1587
  "MaybeSearchStrategy",
@@ -1435,8 +1590,10 @@ __all__ = [
1435
1590
  "ZonedDateTimesError",
1436
1591
  "assume_does_not_raise",
1437
1592
  "bool_arrays",
1593
+ "date_deltas_whenever",
1438
1594
  "date_durations",
1439
1595
  "dates_two_digit_year",
1596
+ "dates_whenever",
1440
1597
  "datetime_durations",
1441
1598
  "draw2",
1442
1599
  "float32s",
@@ -1460,6 +1617,7 @@ __all__ = [
1460
1617
  "paths",
1461
1618
  "plain_datetimes",
1462
1619
  "plain_datetimes",
1620
+ "plain_datetimes_whenever",
1463
1621
  "random_states",
1464
1622
  "sentinels",
1465
1623
  "sets_fixed_length",
@@ -1480,4 +1638,5 @@ __all__ = [
1480
1638
  "uint64s",
1481
1639
  "versions",
1482
1640
  "zoned_datetimes",
1641
+ "zoned_datetimes_whenever",
1483
1642
  ]
@@ -190,7 +190,7 @@ def get_formatter(
190
190
  ) -> Formatter:
191
191
  """Get the formatter; colored if available."""
192
192
  if whenever:
193
- from utilities.whenever import WheneverLogRecord
193
+ from utilities.whenever2 import WheneverLogRecord
194
194
 
195
195
  setLogRecordFactory(WheneverLogRecord)
196
196
  format_ = format_.replace("{asctime}", "{zoned_datetime}")
@@ -234,7 +234,7 @@ def is_instance_gen(obj: Any, type_: Any, /) -> bool:
234
234
  """Check if an instance relationship holds, except bool<int."""
235
235
  # parent
236
236
  if isinstance(type_, tuple):
237
- return any(is_instance_gen(obj, t) for t in type_)
237
+ return any(is_instance_gen(obj, t) for t in type_) # skipif-ci-and-not-windows
238
238
  if is_literal_type(type_):
239
239
  return obj in get_args(type_)
240
240
  if is_union_type(type_):