dycw-utilities 0.162.11__tar.gz → 0.163.0__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 (211) hide show
  1. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/PKG-INFO +1 -1
  2. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/pyproject.toml +7 -5
  3. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/conftest.py +7 -3
  4. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_contextlib.py +1 -1
  5. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_hypothesis.py +2 -2
  6. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_logging.py +41 -71
  7. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pyinstrument.py +1 -1
  8. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_sqlalchemy.py +2 -2
  9. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_traceback.py +5 -3
  10. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_whenever.py +52 -23
  11. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/__init__.py +1 -1
  12. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/fpdf2.py +2 -2
  13. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/hypothesis.py +7 -2
  14. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/logging.py +9 -9
  15. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pyinstrument.py +4 -2
  16. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/traceback.py +4 -4
  17. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/whenever.py +28 -25
  18. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/.gitignore +0 -0
  19. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/LICENSE +0 -0
  20. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/README.md +0 -0
  21. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/__init__.py +0 -0
  22. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/__init__.py +0 -0
  23. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_missing/__init__.py +0 -0
  24. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_missing/module.py +0 -0
  25. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/__init__.py +0 -0
  26. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/outer_1.py +0 -0
  27. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/outer_2.py +0 -0
  28. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  29. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  30. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  31. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  32. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_without/__init__.py +0 -0
  33. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_without/module_1.py +0 -0
  34. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/package_without/module_2.py +0 -0
  35. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/standalone.py +0 -0
  36. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/modules/with_imports.py +0 -0
  37. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  38. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  39. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  40. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  41. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  42. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  43. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  44. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  45. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_aeventkit.py +0 -0
  46. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_altair.py +0 -0
  47. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_asyncio.py +0 -0
  48. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_atomicwrites.py +0 -0
  49. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_atools.py +0 -0
  50. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_cachetools.py +0 -0
  51. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_click.py +0 -0
  52. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_concurrent.py +0 -0
  53. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_contextvars.py +0 -0
  54. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_cryptography.py +0 -0
  55. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_cvxpy.py +0 -0
  56. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_dataclasses.py +0 -0
  57. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_enum.py +0 -0
  58. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_errors.py +0 -0
  59. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_fastapi.py +0 -0
  60. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_fpdf2.py +0 -0
  61. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_functions.py +0 -0
  62. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_functools.py +0 -0
  63. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_getpass.py +0 -0
  64. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_gzip.py +0 -0
  65. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_hashlib.py +0 -0
  66. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_http.py +0 -0
  67. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_importlib.py +0 -0
  68. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_inflect.py +0 -0
  69. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_ipython.py +0 -0
  70. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_iterables.py +0 -0
  71. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_json.py +0 -0
  72. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_jupyter.py +0 -0
  73. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_libcst.py +0 -0
  74. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_lightweight_charts.py +0 -0
  75. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_math.py +0 -0
  76. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_memory_profiler.py +0 -0
  77. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_modules.py +0 -0
  78. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_more_itertools.py +0 -0
  79. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_numpy.py +0 -0
  80. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_objects/__init__.py +0 -0
  81. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_objects/objects.py +0 -0
  82. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_operator.py +0 -0
  83. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_optuna.py +0 -0
  84. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_orjson.py +0 -0
  85. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_os.py +0 -0
  86. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_parse.py +0 -0
  87. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pathlib.py +0 -0
  88. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pickle.py +0 -0
  89. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_platform.py +0 -0
  90. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_polars.py +0 -0
  91. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_polars_ols.py +0 -0
  92. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_postgres.py +0 -0
  93. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pottery.py +0 -0
  94. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pqdm.py +0 -0
  95. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_psutil.py +0 -0
  96. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pytest.py +0 -0
  97. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pytest_randomly.py +0 -0
  98. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_pytest_regressions.py +0 -0
  99. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_random.py +0 -0
  100. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_re.py +0 -0
  101. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_redis.py +0 -0
  102. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_reprlib.py +0 -0
  103. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_scipy.py +0 -0
  104. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_sentinel.py +0 -0
  105. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_shelve.py +0 -0
  106. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_slack_sdk.py +0 -0
  107. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_socket.py +0 -0
  108. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_sqlalchemy_polars.py +0 -0
  109. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_statsmodels.py +0 -0
  110. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_string.py +0 -0
  111. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_tempfile.py +0 -0
  112. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_testbook.py +0 -0
  113. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_text.py +0 -0
  114. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_threading.py +0 -0
  115. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_timer.py +0 -0
  116. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_typed_settings.py +0 -0
  117. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_types.py +0 -0
  118. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_typing.py +0 -0
  119. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_typing_funcs/__init__.py +0 -0
  120. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_typing_funcs/no_future.py +0 -0
  121. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_typing_funcs/with_future.py +0 -0
  122. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_tzdata.py +0 -0
  123. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_tzlocal.py +0 -0
  124. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_uuid.py +0 -0
  125. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_version.py +0 -0
  126. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_warnings.py +0 -0
  127. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_zipfile.py +0 -0
  128. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/tests/test_zoneinfo.py +0 -0
  129. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/aeventkit.py +0 -0
  130. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/altair.py +0 -0
  131. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/asyncio.py +0 -0
  132. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/atomicwrites.py +0 -0
  133. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/atools.py +0 -0
  134. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/cachetools.py +0 -0
  135. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/click.py +0 -0
  136. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/concurrent.py +0 -0
  137. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/contextlib.py +0 -0
  138. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/contextvars.py +0 -0
  139. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/cryptography.py +0 -0
  140. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/cvxpy.py +0 -0
  141. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/dataclasses.py +0 -0
  142. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/enum.py +0 -0
  143. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/errors.py +0 -0
  144. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/fastapi.py +0 -0
  145. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/functions.py +0 -0
  146. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/functools.py +0 -0
  147. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/getpass.py +0 -0
  148. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/gzip.py +0 -0
  149. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/hashlib.py +0 -0
  150. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/http.py +0 -0
  151. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/importlib.py +0 -0
  152. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/inflect.py +0 -0
  153. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/ipython.py +0 -0
  154. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/iterables.py +0 -0
  155. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/json.py +0 -0
  156. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/jupyter.py +0 -0
  157. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/libcst.py +0 -0
  158. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/lightweight_charts.py +0 -0
  159. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/math.py +0 -0
  160. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/memory_profiler.py +0 -0
  161. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/modules.py +0 -0
  162. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/more_itertools.py +0 -0
  163. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/numpy.py +0 -0
  164. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/operator.py +0 -0
  165. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/optuna.py +0 -0
  166. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/orjson.py +0 -0
  167. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/os.py +0 -0
  168. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/parse.py +0 -0
  169. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pathlib.py +0 -0
  170. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pickle.py +0 -0
  171. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/platform.py +0 -0
  172. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/polars.py +0 -0
  173. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/polars_ols.py +0 -0
  174. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/postgres.py +0 -0
  175. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pottery.py +0 -0
  176. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pqdm.py +0 -0
  177. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/psutil.py +0 -0
  178. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/py.typed +0 -0
  179. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pytest.py +0 -0
  180. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pytest_plugins/__init__.py +0 -0
  181. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  182. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  183. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/pytest_regressions.py +0 -0
  184. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/random.py +0 -0
  185. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/re.py +0 -0
  186. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/redis.py +0 -0
  187. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/reprlib.py +0 -0
  188. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/scipy.py +0 -0
  189. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/sentinel.py +0 -0
  190. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/shelve.py +0 -0
  191. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/slack_sdk.py +0 -0
  192. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/socket.py +0 -0
  193. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/sqlalchemy.py +0 -0
  194. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/sqlalchemy_polars.py +0 -0
  195. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/statsmodels.py +0 -0
  196. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/string.py +0 -0
  197. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/tempfile.py +0 -0
  198. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/testbook.py +0 -0
  199. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/text.py +0 -0
  200. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/threading.py +0 -0
  201. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/timer.py +0 -0
  202. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/typed_settings.py +0 -0
  203. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/types.py +0 -0
  204. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/typing.py +0 -0
  205. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/tzdata.py +0 -0
  206. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/tzlocal.py +0 -0
  207. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/uuid.py +0 -0
  208. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/version.py +0 -0
  209. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/warnings.py +0 -0
  210. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/zipfile.py +0 -0
  211. {dycw_utilities-0.162.11 → dycw_utilities-0.163.0}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.162.11
3
+ Version: 0.163.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -72,8 +72,8 @@ hypothesis = [
72
72
  ]
73
73
  hypothesis-test = [
74
74
  "libcst",
75
- "pathvalidate",
76
75
  "numpy",
76
+ "pathvalidate",
77
77
  "pytest-rerunfailures",
78
78
  ]
79
79
  inflect = [
@@ -135,7 +135,6 @@ pottery = [
135
135
  ]
136
136
  pottery-test = [
137
137
  "orjson",
138
- "pytest-rerunfailures",
139
138
  ]
140
139
  pqdm = [
141
140
  "pqdm >=0.2.0, <0.3",
@@ -162,8 +161,8 @@ pytest-regressions-test = [
162
161
  ]
163
162
  pytest-test = [
164
163
  "orjson",
165
- "pytest-rng",
166
164
  "pytest-rerunfailures",
165
+ "pytest-rng",
167
166
  ]
168
167
  redis = [
169
168
  "redis >=6.4.0, <6.5",
@@ -217,6 +216,9 @@ typed-settings = [
217
216
  tzdata = [
218
217
  "tzdata >=2025.2, <2025.3",
219
218
  ]
219
+ whenever-test = [
220
+ "pathvalidate",
221
+ ]
220
222
 
221
223
  # project
222
224
  [project]
@@ -230,7 +232,7 @@ dependencies = [
230
232
  name = "dycw-utilities"
231
233
  readme = "README.md"
232
234
  requires-python = ">= 3.12"
233
- version = "0.162.11"
235
+ version = "0.163.0"
234
236
 
235
237
  [project.entry-points.pytest11]
236
238
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -263,7 +265,7 @@ test = [
263
265
  # bump-my-version
264
266
  [tool.bumpversion]
265
267
  allow_dirty = true
266
- current_version = "0.162.11"
268
+ current_version = "0.163.0"
267
269
 
268
270
  [[tool.bumpversion.files]]
269
271
  filename = "src/utilities/__init__.py"
@@ -7,11 +7,12 @@ from typing import TYPE_CHECKING
7
7
 
8
8
  from hypothesis import HealthCheck
9
9
  from pytest import fixture, mark, param, skip
10
+ from whenever import PlainDateTime
10
11
 
11
12
  from utilities.contextlib import enhanced_context_manager
12
13
  from utilities.platform import IS_LINUX, IS_MAC, IS_NOT_LINUX, IS_WINDOWS
13
14
  from utilities.re import ExtractGroupError, extract_group
14
- from utilities.whenever import MINUTE, get_now, parse_plain_local
15
+ from utilities.whenever import MINUTE, get_now_local_plain
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  from collections.abc import AsyncIterator, Iterator, Sequence
@@ -170,11 +171,14 @@ async def test_async_postgres_engine() -> AsyncEngine:
170
171
 
171
172
 
172
173
  def _is_to_drop(table: str, /) -> bool:
174
+ now = get_now_local_plain()
173
175
  try:
174
- datetime_str = extract_group(r"^(\d{8}T\d{6})_", table)
176
+ text = extract_group(r"^(\d{8}T\d{2,})_", table)
175
177
  except ExtractGroupError:
176
178
  return True
177
- return (get_now() - parse_plain_local(datetime_str)) >= MINUTE
179
+ date_time = PlainDateTime.parse_common_iso(text)
180
+ age = now.difference(date_time, ignore_dst=True)
181
+ return age >= MINUTE
178
182
 
179
183
 
180
184
  def _select_tables() -> TextClause:
@@ -153,7 +153,7 @@ class TestEnhancedContextManager:
153
153
  def test_multiprocessing_sigterm(
154
154
  self, *, tmp_path: Path, target: Callable[..., None]
155
155
  ) -> None:
156
- sleep = 0.5
156
+ sleep = 1.0
157
157
  marker = tmp_path.joinpath("marker")
158
158
  proc = Process(target=target, args=(marker,), kwargs={"sleep": 4 * sleep})
159
159
  proc.start()
@@ -40,6 +40,7 @@ from whenever import (
40
40
 
41
41
  from utilities.functions import ensure_int
42
42
  from utilities.hypothesis import (
43
+ _LINUX_DISALLOW_TIME_ZONES,
43
44
  Shape,
44
45
  _Draw2DefaultGeneratedSentinelError,
45
46
  _Draw2InputResolvedToSentinelError,
@@ -1192,8 +1193,7 @@ class TestZoneInfos:
1192
1193
  time_zone = data.draw(zone_infos())
1193
1194
  assert isinstance(time_zone, ZoneInfo)
1194
1195
  if IS_LINUX:
1195
- _ = assume(time_zone.key not in {"Etc/UTC", "localtime"})
1196
- assert time_zone.key not in {"Etc/UTC", "localtime"}
1196
+ assert time_zone.key not in _LINUX_DISALLOW_TIME_ZONES
1197
1197
  _ = get_now(time_zone)
1198
1198
 
1199
1199
 
@@ -31,7 +31,7 @@ from utilities.logging import (
31
31
  from utilities.text import unique_str
32
32
  from utilities.types import LogLevel
33
33
  from utilities.typing import get_args
34
- from utilities.whenever import get_now, to_local_plain
34
+ from utilities.whenever import format_compact, get_now_local
35
35
 
36
36
  if TYPE_CHECKING:
37
37
  from collections.abc import Mapping
@@ -101,7 +101,7 @@ class TestComputeRolloverActions:
101
101
  actions.do()
102
102
  files = list(tmp_path.iterdir())
103
103
  assert len(files) == 1
104
- assert any(p for p in files if search(r"^log\.1\__[\dT]+\.txt$", p.name))
104
+ assert any(p for p in files if search(r"^log\.1\__.+?\.txt$", p.name))
105
105
 
106
106
  for _ in range(2):
107
107
  await sleep(1)
@@ -112,9 +112,7 @@ class TestComputeRolloverActions:
112
112
  actions.do()
113
113
  files = list(tmp_path.iterdir())
114
114
  assert len(files) == 1
115
- assert any(
116
- p for p in files if search(r"^log\.1\__[\dT]+__[\dT]+\.txt$", p.name)
117
- )
115
+ assert any(p for p in files if search(r"^log\.1\__.+?__.+?\.txt$", p.name))
118
116
 
119
117
  async def test_multiple_backups(self, *, tmp_path: Path) -> None:
120
118
  tmp_path.joinpath("log.txt").touch()
@@ -125,7 +123,7 @@ class TestComputeRolloverActions:
125
123
  actions.do()
126
124
  files = list(tmp_path.iterdir())
127
125
  assert len(files) == 1
128
- assert any(p for p in files if search(r"^log\.1\__[\dT]+\.txt$", p.name))
126
+ assert any(p for p in files if search(r"^log\.1\__.+\.txt$", p.name))
129
127
 
130
128
  await sleep(1)
131
129
  tmp_path.joinpath("log.txt").touch()
@@ -135,10 +133,8 @@ class TestComputeRolloverActions:
135
133
  actions.do()
136
134
  files = list(tmp_path.iterdir())
137
135
  assert len(files) == 2
138
- assert any(
139
- p for p in files if search(r"^log\.1\__[\dT]+__[\dT]+\.txt$", p.name)
140
- )
141
- assert any(p for p in files if search(r"^log\.2\__[\dT]+\.txt$", p.name))
136
+ assert any(p for p in files if search(r"^log\.1\__.+?__.+?\.txt$", p.name))
137
+ assert any(p for p in files if search(r"^log\.2\__.+?\.txt$", p.name))
142
138
 
143
139
  await sleep(1)
144
140
  tmp_path.joinpath("log.txt").touch()
@@ -148,13 +144,9 @@ class TestComputeRolloverActions:
148
144
  actions.do()
149
145
  files = list(tmp_path.iterdir())
150
146
  assert len(files) == 3
151
- assert any(
152
- p for p in files if search(r"^log\.1\__[\dT]+__[\dT]+\.txt$", p.name)
153
- )
154
- assert any(
155
- p for p in files if search(r"^log\.2\__[\dT]+__[\dT]+\.txt$", p.name)
156
- )
157
- assert all(p for p in files if search(r"^log\.3\__[\dT]+\.txt$", p.name))
147
+ assert any(p for p in files if search(r"^log\.1\__.+?__.+?\.txt$", p.name))
148
+ assert any(p for p in files if search(r"^log\.2\__.+?__.+?\.txt$", p.name))
149
+ assert all(p for p in files if search(r"^log\.3\__.+?\.txt$", p.name))
158
150
 
159
151
  for _ in range(2):
160
152
  await sleep(1)
@@ -165,15 +157,9 @@ class TestComputeRolloverActions:
165
157
  actions.do()
166
158
  files = list(tmp_path.iterdir())
167
159
  assert len(files) == 3
168
- assert any(
169
- p for p in files if search(r"^log\.1\__[\dT]+__[\dT]+\.txt$", p.name)
170
- )
171
- assert any(
172
- p for p in files if search(r"^log\.2\__[\dT]+__[\dT]+\.txt$", p.name)
173
- )
174
- assert all(
175
- p for p in files if search(r"^log\.3\__[\dT]+__[\dT]+\.txt$", p.name)
176
- )
160
+ assert any(p for p in files if search(r"^log\.1\__.+?__.+?\.txt$", p.name))
161
+ assert any(p for p in files if search(r"^log\.2\__.+?__.+?\.txt$", p.name))
162
+ assert all(p for p in files if search(r"^log\.3\__.+?__.+?\.txt$", p.name))
177
163
 
178
164
  async def test_deleting_old_files(self, *, tmp_path: Path) -> None:
179
165
  tmp_path.joinpath("log.txt").touch()
@@ -184,11 +170,11 @@ class TestComputeRolloverActions:
184
170
  actions.do()
185
171
  files = list(tmp_path.iterdir())
186
172
  assert len(files) == 1
187
- assert any(p for p in files if search(r"^log\.1\__[\dT]+\.txt$", p.name))
173
+ assert any(p for p in files if search(r"^log\.1\__.+?\.txt$", p.name))
188
174
 
189
175
  await sleep(1)
190
176
  tmp_path.joinpath("log.txt").touch()
191
- now = to_local_plain(get_now())
177
+ now = format_compact(get_now_local(), path=True)
192
178
  tmp_path.joinpath(f"log.99__{now}__{now}.txt").touch()
193
179
  actions = _compute_rollover_actions(tmp_path, "log", ".txt")
194
180
  assert len(actions.deletions) == 2
@@ -196,9 +182,7 @@ class TestComputeRolloverActions:
196
182
  actions.do()
197
183
  files = list(tmp_path.iterdir())
198
184
  assert len(files) == 1
199
- assert any(
200
- p for p in files if search(r"^log\.1\__[\dT]+__[\dT]+\.txt$", p.name)
201
- )
185
+ assert any(p for p in files if search(r"^log\.1\__.+?__.+?\.txt$", p.name))
202
186
 
203
187
 
204
188
  class TestGetFormatStr:
@@ -278,7 +262,7 @@ class TestRotatingLogFile:
278
262
  def test_from_path_with_index_and_end(
279
263
  self, *, index: int, end: ZonedDateTime
280
264
  ) -> None:
281
- path = Path(f"log.{index}__{to_local_plain(end)}.txt")
265
+ path = Path(f"log.{index}__{format_compact(end, path=True)}.txt")
282
266
  result = _RotatingLogFile.from_path(path, "log", ".txt")
283
267
  assert result is not None
284
268
  assert result.stem == "log"
@@ -294,7 +278,9 @@ class TestRotatingLogFile:
294
278
  self, *, index: int, datetimes: tuple[ZonedDateTime, ZonedDateTime]
295
279
  ) -> None:
296
280
  start, end = datetimes
297
- path = Path(f"log.{index}__{to_local_plain(start)}__{to_local_plain(end)}.txt")
281
+ path = Path(
282
+ f"log.{index}__{format_compact(start, path=True)}__{format_compact(end, path=True)}.txt"
283
+ )
298
284
  result = _RotatingLogFile.from_path(path, "log", ".txt")
299
285
  assert result is not None
300
286
  assert result.stem == "log"
@@ -324,7 +310,9 @@ class TestRotatingLogFile:
324
310
  file = _RotatingLogFile(
325
311
  directory=root, stem="log", suffix=".txt", index=index, end=end
326
312
  )
327
- assert file.path == root.joinpath(f"log.{index}__{to_local_plain(end)}.txt")
313
+ assert file.path == root.joinpath(
314
+ f"log.{index}__{format_compact(end, path=True)}.txt"
315
+ )
328
316
 
329
317
  @given(
330
318
  root=temp_paths(),
@@ -339,7 +327,7 @@ class TestRotatingLogFile:
339
327
  directory=root, stem="log", suffix=".txt", index=index, start=start, end=end
340
328
  )
341
329
  assert file.path == root.joinpath(
342
- f"log.{index}__{to_local_plain(start)}__{to_local_plain(end)}.txt"
330
+ f"log.{index}__{format_compact(start, path=True)}__{format_compact(end, path=True)}.txt"
343
331
  )
344
332
 
345
333
 
@@ -393,46 +381,34 @@ class TestSizeAndTimeRotatingFileHandler:
393
381
  assert any(p for p in files if search(r"^log\.txt$", p.name))
394
382
  if cycle == 2:
395
383
  assert any(
396
- p for p in files if search(r"^log\.1__[\dT]+\.txt$", p.name)
384
+ p for p in files if search(r"^log\.1__.+?\.txt$", p.name)
397
385
  )
398
386
  elif cycle == 3:
399
387
  assert any(
400
- p
401
- for p in files
402
- if search(r"^log\.1__[\dT]+__[\dT]+\.txt$", p.name)
388
+ p for p in files if search(r"^log\.1__.+?__.+?\.txt$", p.name)
403
389
  )
404
390
  assert any(
405
- p for p in files if search(r"^log\.2__[\dT]+\.txt$", p.name)
391
+ p for p in files if search(r"^log\.2__.+?\.txt$", p.name)
406
392
  )
407
393
  elif cycle == 4:
408
394
  assert any(
409
- p
410
- for p in files
411
- if search(r"^log\.1__[\dT]+__[\dT]+\.txt$", p.name)
395
+ p for p in files if search(r"^log\.1__.+?__.+?\.txt$", p.name)
412
396
  )
413
397
  assert any(
414
- p
415
- for p in files
416
- if search(r"^log\.2__[\dT]+__[\dT]+\.txt$", p.name)
398
+ p for p in files if search(r"^log\.2__.+?__.+?\.txt$", p.name)
417
399
  )
418
400
  assert any(
419
- p for p in files if search(r"^log\.3__[\dT]+\.txt$", p.name)
401
+ p for p in files if search(r"^log\.3__.+?\.txt$", p.name)
420
402
  )
421
403
  elif cycle >= 5:
422
404
  assert any(
423
- p
424
- for p in files
425
- if search(r"^log\.1__[\dT]+__[\dT]+\.txt$", p.name)
405
+ p for p in files if search(r"^log\.1__.+?__.+?\.txt$", p.name)
426
406
  )
427
407
  assert any(
428
- p
429
- for p in files
430
- if search(r"^log\.2__[\dT]+__[\dT]+\.txt$", p.name)
408
+ p for p in files if search(r"^log\.2__.+?__.+?\.txt$", p.name)
431
409
  )
432
410
  assert any(
433
- p
434
- for p in files
435
- if search(r"^log\.3__[\dT]+__[\dT]+\.txt$", p.name)
411
+ p for p in files if search(r"^log\.3__.+?__.+?\.txt$", p.name)
436
412
  )
437
413
  await sleep(0.1)
438
414
 
@@ -463,7 +439,7 @@ class TestSizeAndTimeRotatingFileHandler:
463
439
  files = list(tmp_path.iterdir())
464
440
  assert len(files) == 2
465
441
  assert any(p for p in files if search(r"^log\.txt$", p.name))
466
- assert any(p for p in files if search(r"^log\.1__[\dT]+\.txt$", p.name))
442
+ assert any(p for p in files if search(r"^log\.1__.+?\.txt$", p.name))
467
443
 
468
444
  await sleep(1.1)
469
445
  for i in range(4, 6):
@@ -471,10 +447,8 @@ class TestSizeAndTimeRotatingFileHandler:
471
447
  files = list(tmp_path.iterdir())
472
448
  assert len(files) == 3
473
449
  assert any(p for p in files if search(r"^log\.txt$", p.name))
474
- assert any(
475
- p for p in files if search(r"^log\.1__[\dT]+__[\dT]+\.txt$", p.name)
476
- )
477
- assert any(p for p in files if search(r"^log\.2__[\dT]+\.txt$", p.name))
450
+ assert any(p for p in files if search(r"^log\.1__.+?__.+?\.txt$", p.name))
451
+ assert any(p for p in files if search(r"^log\.2__.+?\.txt$", p.name))
478
452
 
479
453
  await sleep(1.1)
480
454
  for i in range(6, 8):
@@ -482,13 +456,9 @@ class TestSizeAndTimeRotatingFileHandler:
482
456
  files = list(tmp_path.iterdir())
483
457
  assert len(files) == 4
484
458
  assert any(p for p in files if search(r"^log\.txt$", p.name))
485
- assert any(
486
- p for p in files if search(r"^log\.1__[\dT]+__[\dT]+\.txt$", p.name)
487
- )
488
- assert any(
489
- p for p in files if search(r"^log\.2__[\dT]+__[\dT]+\.txt$", p.name)
490
- )
491
- assert any(p for p in files if search(r"^log\.3__[\dT]+\.txt$", p.name))
459
+ assert any(p for p in files if search(r"^log\.1__.+?__.+?\.txt$", p.name))
460
+ assert any(p for p in files if search(r"^log\.2__.+?__.+?\.txt$", p.name))
461
+ assert any(p for p in files if search(r"^log\.3__.+?\.txt$", p.name))
492
462
 
493
463
  for _ in range(2):
494
464
  await sleep(1.1)
@@ -498,13 +468,13 @@ class TestSizeAndTimeRotatingFileHandler:
498
468
  assert len(files) == 4
499
469
  assert any(p for p in files if search(r"^log\.txt$", p.name))
500
470
  assert any(
501
- p for p in files if search(r"^log\.1__[\dT]+__[\dT]+\.txt$", p.name)
471
+ p for p in files if search(r"^log\.1__.+?__.+?\.txt$", p.name)
502
472
  )
503
473
  assert any(
504
- p for p in files if search(r"^log\.2__[\dT]+__[\dT]+\.txt$", p.name)
474
+ p for p in files if search(r"^log\.2__.+?__.+?\.txt$", p.name)
505
475
  )
506
476
  assert any(
507
- p for p in files if search(r"^log\.3__[\dT]+__[\dT]+\.txt$", p.name)
477
+ p for p in files if search(r"^log\.3__.+?__.+?\.txt$", p.name)
508
478
  )
509
479
 
510
480
  @mark.parametrize("max_bytes", [param(0), param(1)])
@@ -16,4 +16,4 @@ class TestProfile:
16
16
  sleep(1e-3)
17
17
 
18
18
  (file,) = tmp_path.iterdir()
19
- assert search(r"^profile__[\dT]+\.html$", file.name)
19
+ assert search(r"^profile__.+?\.html$", file.name)
@@ -90,7 +90,7 @@ from utilities.sqlalchemy import (
90
90
  )
91
91
  from utilities.text import strip_and_dedent
92
92
  from utilities.typing import get_args
93
- from utilities.whenever import MILLISECOND, get_now, to_local_plain
93
+ from utilities.whenever import MILLISECOND, format_compact, get_now_local_plain
94
94
 
95
95
  if TYPE_CHECKING:
96
96
  from pathlib import Path
@@ -101,7 +101,7 @@ if TYPE_CHECKING:
101
101
  def _table_names() -> str:
102
102
  """Generate at unique string."""
103
103
  key = str(uuid4()).replace("-", "")
104
- return f"{to_local_plain(get_now())}_{key}"
104
+ return f"{format_compact(get_now_local_plain())}_{key}"
105
105
 
106
106
 
107
107
  class TestCheckConnect:
@@ -19,7 +19,7 @@ from utilities.traceback import (
19
19
  make_except_hook,
20
20
  )
21
21
  from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
22
- from utilities.whenever import SECOND, get_now, to_local_plain
22
+ from utilities.whenever import SECOND, format_compact, get_now
23
23
 
24
24
  if TYPE_CHECKING:
25
25
  from collections.abc import Iterable
@@ -106,7 +106,7 @@ class TestMakeExceptHook:
106
106
  exc_type, exc_val, traceback = exc_info()
107
107
  hook(exc_type, exc_val, traceback)
108
108
  path = one(tmp_path.iterdir())
109
- assert search(r"^[\dT]+\.txt$", path.name)
109
+ assert search(r"^.+?\.txt$", path.name)
110
110
 
111
111
  def test_non_error(self) -> None:
112
112
  hook = make_except_hook()
@@ -116,7 +116,9 @@ class TestMakeExceptHook:
116
116
 
117
117
  def test_purge(self, *, tmp_path: Path) -> None:
118
118
  now = get_now()
119
- path = tmp_path.joinpath(to_local_plain(now - 2 * SECOND)).with_suffix(".txt")
119
+ path = tmp_path.joinpath(
120
+ format_compact(now - 2 * SECOND, path=True)
121
+ ).with_suffix(".txt")
120
122
  path.touch()
121
123
  assert len(list(tmp_path.iterdir())) == 1
122
124
  _make_except_hook_purge(tmp_path, SECOND)
@@ -9,6 +9,7 @@ from zoneinfo import ZoneInfo
9
9
 
10
10
  from hypothesis import HealthCheck, given, settings
11
11
  from hypothesis.strategies import DataObject, data, integers, none, sampled_from
12
+ from pathvalidate import validate_filepath
12
13
  from pytest import mark, param, raises
13
14
  from whenever import (
14
15
  Date,
@@ -39,6 +40,7 @@ from utilities.hypothesis import (
39
40
  zoned_date_times_2000,
40
41
  )
41
42
  from utilities.sentinel import Sentinel, sentinel
43
+ from utilities.types import TIME_ZONES
42
44
  from utilities.tzdata import HongKong, Tokyo, USCentral, USEastern
43
45
  from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
44
46
  from utilities.whenever import (
@@ -55,6 +57,7 @@ from utilities.whenever import (
55
57
  MINUTE,
56
58
  MONTH,
57
59
  NOW_LOCAL,
60
+ NOW_LOCAL_PLAIN,
58
61
  NOW_PLAIN,
59
62
  NOW_UTC,
60
63
  SECOND,
@@ -115,19 +118,18 @@ from utilities.whenever import (
115
118
  from_timestamp_nanos,
116
119
  get_now,
117
120
  get_now_local,
121
+ get_now_local_plain,
118
122
  get_now_plain,
119
123
  get_today,
120
124
  get_today_local,
121
125
  mean_datetime,
122
126
  min_max_date,
123
- parse_plain_local,
124
127
  round_date_or_date_time,
125
128
  sub_year_month,
126
129
  to_date,
127
130
  to_date_time_delta,
128
131
  to_days,
129
132
  to_hours,
130
- to_local_plain,
131
133
  to_microseconds,
132
134
  to_milliseconds,
133
135
  to_minutes,
@@ -158,6 +160,7 @@ if TYPE_CHECKING:
158
160
  MaybeCallableDateLike,
159
161
  MaybeCallableZonedDateTimeLike,
160
162
  TimeOrDateTimeDelta,
163
+ TimeZone,
161
164
  )
162
165
 
163
166
 
@@ -354,24 +357,46 @@ class TestFormatCompact:
354
357
  expected = time.round()
355
358
  assert parsed == expected
356
359
 
357
- @given(datetime=plain_date_times())
358
- def test_plain_datetime(self, *, datetime: PlainDateTime) -> None:
359
- result = format_compact(datetime)
360
+ @given(date_time=plain_date_times())
361
+ def test_plain_date_time(self, *, date_time: PlainDateTime) -> None:
362
+ result = format_compact(date_time)
360
363
  assert isinstance(result, str)
361
364
  parsed = PlainDateTime.parse_common_iso(result)
362
365
  assert parsed.nanosecond == 0
363
- expected = datetime.round()
366
+ expected = date_time.round()
364
367
  assert parsed == expected
365
368
 
366
- @given(datetime=zoned_date_times())
367
- def test_zoned_datetime(self, *, datetime: ZonedDateTime) -> None:
368
- result = format_compact(datetime)
369
+ @given(date_time=zoned_date_times(time_zone=zone_infos()))
370
+ def test_zoned_date_time(self, *, date_time: ZonedDateTime) -> None:
371
+ result = format_compact(date_time)
369
372
  assert isinstance(result, str)
370
- parsed = ZonedDateTime.parse_common_iso(result)
373
+ parsed = to_zoned_date_time(result)
371
374
  assert parsed.nanosecond == 0
372
- expected = datetime.round()
375
+ expected = date_time.round()
373
376
  assert parsed == expected
374
377
 
378
+ @mark.parametrize(
379
+ ("time_zone", "suffix"),
380
+ [
381
+ param("America/Argentina/Buenos_Aires", "America~Argentina~Buenos_Aires"),
382
+ param("Asia/Hong_Kong", "Asia~Hong_Kong"),
383
+ param("Etc/GMT", "Etc~GMT"),
384
+ param("Etc/GMT+1", "Etc~GMT+1"),
385
+ param("Etc/GMT-1", "Etc~GMT-1"),
386
+ ],
387
+ )
388
+ def test_zoned_date_time_path(self, *, time_zone: TimeZone, suffix: str) -> None:
389
+ assert time_zone in TIME_ZONES
390
+ date_time = ZonedDateTime(
391
+ 2000, 1, 2, 12, 34, 56, nanosecond=123456789, tz=time_zone
392
+ )
393
+ ser = format_compact(date_time, path=True)
394
+ validate_filepath(ser)
395
+ expected = f"20000102T123456[{suffix}]"
396
+ assert ser == expected
397
+ result = to_zoned_date_time(ser)
398
+ assert result.exact_eq(date_time.round())
399
+
375
400
 
376
401
  class TestFromTimeStamp:
377
402
  @given(
@@ -427,6 +452,15 @@ class TestGetNowLocal:
427
452
  assert NOW_LOCAL.tz == LOCAL_TIME_ZONE_NAME
428
453
 
429
454
 
455
+ class TestGetNowLocalPlain:
456
+ def test_function(self) -> None:
457
+ now = get_now_local_plain()
458
+ assert isinstance(now, PlainDateTime)
459
+
460
+ def test_constant(self) -> None:
461
+ assert isinstance(NOW_LOCAL_PLAIN, PlainDateTime)
462
+
463
+
430
464
  class TestGetNowPlain:
431
465
  @given(time_zone=zone_infos())
432
466
  def test_function(self, *, time_zone: ZoneInfo) -> None:
@@ -968,15 +1002,6 @@ class TestToHours:
968
1002
  _ = to_hours(delta)
969
1003
 
970
1004
 
971
- class TestToLocalPlainAndParsePlainLocal:
972
- @given(date_time=zoned_date_times())
973
- def test_main(self, *, date_time: ZonedDateTime) -> None:
974
- text = to_local_plain(date_time)
975
- assert isinstance(text, str)
976
- parsed = parse_plain_local(text)
977
- assert abs(parsed - date_time) <= SECOND
978
-
979
-
980
1005
  class TestToMicroseconds:
981
1006
  @given(days=integers())
982
1007
  def test_date_delta(self, *, days: int) -> None:
@@ -1399,9 +1424,13 @@ class TestToZonedDateTime:
1399
1424
  expected = date_time.to_tz(time_zone.key)
1400
1425
  assert result.exact_eq(expected)
1401
1426
 
1402
- @given(date_time=zoned_date_times(), time_zone=zone_infos())
1403
- def test_str(self, *, date_time: ZonedDateTime, time_zone: ZoneInfo) -> None:
1404
- result = to_zoned_date_time(date_time.format_common_iso(), time_zone=time_zone)
1427
+ @given(data=data(), date_time=zoned_date_times(), time_zone=zone_infos())
1428
+ def test_str(
1429
+ self, *, data: DataObject, date_time: ZonedDateTime, time_zone: ZoneInfo
1430
+ ) -> None:
1431
+ text = date_time.format_common_iso()
1432
+ text_use = data.draw(sampled_from([text, text.replace("/", "_")]))
1433
+ result = to_zoned_date_time(text_use, time_zone=time_zone)
1405
1434
  expected = date_time.to_tz(time_zone.key)
1406
1435
  assert result.exact_eq(expected)
1407
1436
 
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.162.11"
3
+ __version__ = "0.163.0"
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, override
6
6
  from fpdf import FPDF
7
7
  from fpdf.enums import XPos, YPos
8
8
 
9
- from utilities.whenever import get_now, to_local_plain
9
+ from utilities.whenever import format_compact, get_now_local
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from collections.abc import Iterator
@@ -47,7 +47,7 @@ def yield_pdf(*, header: str | None = None) -> Iterator[_BasePDF]:
47
47
  def footer(self) -> None:
48
48
  self.set_y(-15)
49
49
  self.set_font(family="Helvetica", style="I", size=8)
50
- page_no, now = (self.page_no(), to_local_plain(get_now()))
50
+ page_no, now = (self.page_no(), format_compact(get_now_local()))
51
51
  text = f"page {page_no}/{{}}; {now}"
52
52
  _ = self.cell(
53
53
  w=0,
@@ -115,7 +115,7 @@ if TYPE_CHECKING:
115
115
  from sqlalchemy import URL
116
116
 
117
117
  from utilities.numpy import NDArrayB, NDArrayF, NDArrayI, NDArrayO
118
- from utilities.types import Number, TimeZoneLike
118
+ from utilities.types import Number, TimeZone, TimeZoneLike
119
119
 
120
120
 
121
121
  type MaybeSearchStrategy[_T] = _T | SearchStrategy[_T]
@@ -1484,12 +1484,17 @@ def zone_infos(draw: DrawFn, /) -> ZoneInfo:
1484
1484
  """Strategy for generating time-zones."""
1485
1485
  time_zone = draw(timezones())
1486
1486
  if IS_LINUX: # skipif-not-linux
1487
- _ = assume(time_zone.key not in {"Etc/UTC", "localtime"})
1487
+ _ = assume(time_zone.key not in _LINUX_DISALLOW_TIME_ZONES)
1488
1488
  with assume_does_not_raise(TimeZoneNotFoundError):
1489
1489
  _ = get_now(time_zone)
1490
1490
  return time_zone
1491
1491
 
1492
1492
 
1493
+ _LINUX_DISALLOW_TIME_ZONES: set[TimeZone | Literal["localtime"]] = {
1494
+ "Etc/UTC",
1495
+ "localtime",
1496
+ }
1497
+
1493
1498
  ##
1494
1499
 
1495
1500