dycw-utilities 0.136.9__tar.gz → 0.137.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 (216) hide show
  1. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/PKG-INFO +2 -2
  2. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/pyproject.toml +5 -5
  3. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_logging.py +10 -6
  4. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_period.py +136 -9
  5. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_sqlalchemy.py +2 -3
  6. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_whenever.py +37 -3
  7. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/__init__.py +1 -1
  8. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/fpdf2.py +2 -2
  9. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/logging.py +9 -4
  10. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/period.py +45 -0
  11. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pyinstrument.py +2 -2
  12. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/traceback.py +11 -3
  13. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/whenever.py +29 -4
  14. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/.gitignore +0 -0
  15. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/LICENSE +0 -0
  16. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/README.md +0 -0
  17. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/__init__.py +0 -0
  18. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/conftest.py +0 -0
  19. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/__init__.py +0 -0
  20. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_missing/__init__.py +0 -0
  21. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_missing/module.py +0 -0
  22. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/__init__.py +0 -0
  23. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/outer_1.py +0 -0
  24. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/outer_2.py +0 -0
  25. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  26. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  27. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  28. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  29. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_without/__init__.py +0 -0
  30. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_without/module_1.py +0 -0
  31. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/package_without/module_2.py +0 -0
  32. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/standalone.py +0 -0
  33. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/modules/with_imports.py +0 -0
  34. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  35. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  36. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  37. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  38. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  39. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  40. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  41. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  42. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_aiolimiter.py +0 -0
  43. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_altair.py +0 -0
  44. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_asyncio.py +0 -0
  45. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_asyncio_classes/__init__.py +0 -0
  46. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_asyncio_classes/loopers.py +0 -0
  47. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_asyncio_classes/redis.py +0 -0
  48. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_atomicwrites.py +0 -0
  49. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_atools.py +0 -0
  50. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_cachetools.py +0 -0
  51. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_click.py +0 -0
  52. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_concurrent.py +0 -0
  53. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_contextlib.py +0 -0
  54. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_contextvars.py +0 -0
  55. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_cryptography.py +0 -0
  56. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_cvxpy.py +0 -0
  57. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_dataclasses.py +0 -0
  58. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_enum.py +0 -0
  59. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_errors.py +0 -0
  60. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_eventkit.py +0 -0
  61. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_fastapi.py +0 -0
  62. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_fpdf2.py +0 -0
  63. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_functions.py +0 -0
  64. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_functools.py +0 -0
  65. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_getpass.py +0 -0
  66. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_git.py +0 -0
  67. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_hashlib.py +0 -0
  68. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_http.py +0 -0
  69. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_hypothesis.py +0 -0
  70. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_importlib.py +0 -0
  71. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_inflect.py +0 -0
  72. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_ipython.py +0 -0
  73. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_iterables.py +0 -0
  74. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_jupyter.py +0 -0
  75. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_libcst.py +0 -0
  76. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_lightweight_charts.py +0 -0
  77. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_luigi.py +0 -0
  78. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_math.py +0 -0
  79. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_memory_profiler.py +0 -0
  80. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_modules.py +0 -0
  81. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_more_itertools.py +0 -0
  82. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_numpy.py +0 -0
  83. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_objects/__init__.py +0 -0
  84. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_objects/objects.py +0 -0
  85. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_operator.py +0 -0
  86. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_optuna.py +0 -0
  87. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_orjson.py +0 -0
  88. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_os.py +0 -0
  89. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_parse.py +0 -0
  90. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pathlib.py +0 -0
  91. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pickle.py +0 -0
  92. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_platform.py +0 -0
  93. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_polars.py +0 -0
  94. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_polars_ols.py +0 -0
  95. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pottery.py +0 -0
  96. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pqdm.py +0 -0
  97. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_psutil.py +0 -0
  98. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pydantic.py +0 -0
  99. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pyinstrument.py +0 -0
  100. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pytest.py +0 -0
  101. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_pytest_regressions.py +0 -0
  102. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_python_dotenv.py +0 -0
  103. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_random.py +0 -0
  104. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_re.py +0 -0
  105. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_redis.py +0 -0
  106. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_reprlib.py +0 -0
  107. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_scipy.py +0 -0
  108. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_sentinel.py +0 -0
  109. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_shelve.py +0 -0
  110. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_slack_sdk.py +0 -0
  111. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_socket.py +0 -0
  112. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_statsmodels.py +0 -0
  114. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_string.py +0 -0
  116. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_tempfile.py +0 -0
  117. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_text.py +0 -0
  118. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_threading.py +0 -0
  119. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_timer.py +0 -0
  120. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_traceback.py +0 -0
  121. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_typed_settings.py +0 -0
  122. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_types.py +0 -0
  123. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_typing.py +0 -0
  124. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_typing_funcs/__init__.py +0 -0
  125. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_typing_funcs/no_future.py +0 -0
  126. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_typing_funcs/with_future.py +0 -0
  127. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_tzdata.py +0 -0
  128. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_tzlocal.py +0 -0
  129. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_uuid.py +0 -0
  130. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_version.py +0 -0
  131. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_warnings.py +0 -0
  132. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_zipfile.py +0 -0
  133. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/tests/test_zoneinfo.py +0 -0
  134. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/aiolimiter.py +0 -0
  135. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/altair.py +0 -0
  136. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/asyncio.py +0 -0
  137. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/atomicwrites.py +0 -0
  138. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/atools.py +0 -0
  139. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/cachetools.py +0 -0
  140. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/click.py +0 -0
  141. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/concurrent.py +0 -0
  142. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/contextlib.py +0 -0
  143. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/contextvars.py +0 -0
  144. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/cryptography.py +0 -0
  145. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/cvxpy.py +0 -0
  146. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/dataclasses.py +0 -0
  147. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/enum.py +0 -0
  148. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/errors.py +0 -0
  149. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/eventkit.py +0 -0
  150. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/fastapi.py +0 -0
  151. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/functions.py +0 -0
  152. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/functools.py +0 -0
  153. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/getpass.py +0 -0
  154. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/git.py +0 -0
  155. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/hashlib.py +0 -0
  156. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/http.py +0 -0
  157. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/hypothesis.py +0 -0
  158. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/importlib.py +0 -0
  159. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/inflect.py +0 -0
  160. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/ipython.py +0 -0
  161. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/iterables.py +0 -0
  162. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/jupyter.py +0 -0
  163. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/libcst.py +0 -0
  164. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/lightweight_charts.py +0 -0
  165. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/luigi.py +0 -0
  166. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/math.py +0 -0
  167. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/memory_profiler.py +0 -0
  168. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/modules.py +0 -0
  169. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/more_itertools.py +0 -0
  170. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/numpy.py +0 -0
  171. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/operator.py +0 -0
  172. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/optuna.py +0 -0
  173. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/orjson.py +0 -0
  174. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/os.py +0 -0
  175. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/parse.py +0 -0
  176. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pathlib.py +0 -0
  177. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pickle.py +0 -0
  178. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/platform.py +0 -0
  179. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/polars.py +0 -0
  180. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/polars_ols.py +0 -0
  181. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pottery.py +0 -0
  182. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pqdm.py +0 -0
  183. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/psutil.py +0 -0
  184. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/py.typed +0 -0
  185. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pydantic.py +0 -0
  186. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pytest.py +0 -0
  187. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/pytest_regressions.py +0 -0
  188. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/python_dotenv.py +0 -0
  189. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/random.py +0 -0
  190. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/re.py +0 -0
  191. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/redis.py +0 -0
  192. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/reprlib.py +0 -0
  193. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/scipy.py +0 -0
  194. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/sentinel.py +0 -0
  195. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/shelve.py +0 -0
  196. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/slack_sdk.py +0 -0
  197. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/socket.py +0 -0
  198. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/sqlalchemy.py +0 -0
  199. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/sqlalchemy_polars.py +0 -0
  200. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/statsmodels.py +0 -0
  201. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/streamlit.py +0 -0
  202. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/string.py +0 -0
  203. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/tempfile.py +0 -0
  204. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/text.py +0 -0
  205. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/threading.py +0 -0
  206. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/timer.py +0 -0
  207. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/typed_settings.py +0 -0
  208. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/types.py +0 -0
  209. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/typing.py +0 -0
  210. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/tzdata.py +0 -0
  211. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/tzlocal.py +0 -0
  212. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/uuid.py +0 -0
  213. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/version.py +0 -0
  214. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/warnings.py +0 -0
  215. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/zipfile.py +0 -0
  216. {dycw_utilities-0.136.9 → dycw_utilities-0.137.0}/src/utilities/zoneinfo.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.136.9
3
+ Version: 0.137.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
7
7
  Requires-Dist: atomicwrites<1.5,>=1.4.1
8
8
  Requires-Dist: typing-extensions<4.15,>=4.14.0
9
9
  Requires-Dist: tzlocal<5.4,>=5.3.1
10
- Requires-Dist: whenever<0.9,>=0.8.5
10
+ Requires-Dist: whenever<0.9,>=0.8.6
11
11
  Provides-Extra: logging
12
12
  Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
13
13
  Provides-Extra: test
@@ -21,7 +21,7 @@ core = [
21
21
  "atomicwrites >= 1.4.1, < 1.5",
22
22
  "typing-extensions >= 4.14.0, < 4.15",
23
23
  "tzlocal >= 5.3.1, < 5.4",
24
- "whenever >= 0.8.5, < 0.9",
24
+ "whenever >= 0.8.6, < 0.9",
25
25
  ]
26
26
  cryptography = ["cryptography >= 45.0.4, < 45.1"]
27
27
  cvxpy = ["cvxpy >= 1.6.5, < 1.7"]
@@ -76,7 +76,7 @@ pytest = [
76
76
  pytest-regressions = ["pytest-regressions >= 2.8.0, < 2.9"]
77
77
  pytest-regressions-test = ["orjson", "polars-lts-cpu"]
78
78
  pytest-test = ["orjson", "pytest-rng", "pytest-rerunfailures"]
79
- python-dotenv = ["python-dotenv >= 1.1.0, < 1.2"]
79
+ python-dotenv = ["python-dotenv >= 1.1.1, < 1.2"]
80
80
  redis = ["redis >= 6.2.0, < 6.3", "orjson"]
81
81
  redis-test = ["pytest-rerunfailures"]
82
82
  reprlib-test = ["rich"]
@@ -100,12 +100,12 @@ dependencies = [
100
100
  "atomicwrites >= 1.4.1, < 1.5",
101
101
  "typing-extensions >= 4.14.0, < 4.15",
102
102
  "tzlocal >= 5.3.1, < 5.4",
103
- "whenever >= 0.8.5, < 0.9",
103
+ "whenever >= 0.8.6, < 0.9",
104
104
  ]
105
105
  name = "dycw-utilities"
106
106
  readme = "README.md"
107
107
  requires-python = ">= 3.12"
108
- version = "0.136.9"
108
+ version = "0.137.0"
109
109
 
110
110
  [project.optional-dependencies]
111
111
  logging = [
@@ -133,7 +133,7 @@ test = [
133
133
  # bump-my-version
134
134
  [tool.bumpversion]
135
135
  allow_dirty = true
136
- current_version = "0.136.9"
136
+ current_version = "0.137.0"
137
137
 
138
138
  [[tool.bumpversion.files]]
139
139
  filename = "src/utilities/__init__.py"
@@ -38,7 +38,7 @@ from utilities.logging import (
38
38
  from utilities.text import unique_str
39
39
  from utilities.types import LogLevel
40
40
  from utilities.typing import get_args
41
- from utilities.whenever import format_compact, get_now
41
+ from utilities.whenever import format_compact, get_now, to_local_plain
42
42
 
43
43
  if TYPE_CHECKING:
44
44
  from collections.abc import Mapping
@@ -192,7 +192,7 @@ class TestComputeRolloverActions:
192
192
 
193
193
  await sleep(1)
194
194
  tmp_path.joinpath("log.txt").touch()
195
- now = format_compact(get_now())
195
+ now = format_compact(to_local_plain(get_now()))
196
196
  tmp_path.joinpath(f"log.99__{now}__{now}.txt").touch()
197
197
  actions = _compute_rollover_actions(tmp_path, "log", ".txt")
198
198
  assert len(actions.deletions) == 2
@@ -319,7 +319,7 @@ class TestRotatingLogFile:
319
319
  def test_from_path_with_index_and_end(
320
320
  self, *, index: int, end: ZonedDateTime
321
321
  ) -> None:
322
- path = Path(f"log.{index}__{format_compact(end)}.txt")
322
+ path = Path(f"log.{index}__{format_compact(to_local_plain(end))}.txt")
323
323
  result = _RotatingLogFile.from_path(path, "log", ".txt")
324
324
  assert result is not None
325
325
  assert result.stem == "log"
@@ -333,7 +333,9 @@ class TestRotatingLogFile:
333
333
  self, *, index: int, datetimes: tuple[ZonedDateTime, ZonedDateTime]
334
334
  ) -> None:
335
335
  start, end = datetimes
336
- path = Path(f"log.{index}__{format_compact(start)}__{format_compact(end)}.txt")
336
+ path = Path(
337
+ f"log.{index}__{format_compact(to_local_plain(start))}__{format_compact(to_local_plain(end))}.txt"
338
+ )
337
339
  result = _RotatingLogFile.from_path(path, "log", ".txt")
338
340
  assert result is not None
339
341
  assert result.stem == "log"
@@ -363,7 +365,9 @@ class TestRotatingLogFile:
363
365
  file = _RotatingLogFile(
364
366
  directory=root, stem="log", suffix=".txt", index=index, end=end
365
367
  )
366
- assert file.path == root.joinpath(f"log.{index}__{format_compact(end)}.txt")
368
+ assert file.path == root.joinpath(
369
+ f"log.{index}__{format_compact(to_local_plain(end))}.txt"
370
+ )
367
371
 
368
372
  @given(
369
373
  root=temp_paths(),
@@ -378,7 +382,7 @@ class TestRotatingLogFile:
378
382
  directory=root, stem="log", suffix=".txt", index=index, start=start, end=end
379
383
  )
380
384
  assert file.path == root.joinpath(
381
- f"log.{index}__{format_compact(start)}__{format_compact(end)}.txt"
385
+ f"log.{index}__{format_compact(to_local_plain(start))}__{format_compact(to_local_plain(end))}.txt"
382
386
  )
383
387
 
384
388
 
@@ -4,8 +4,9 @@ from re import search
4
4
  from typing import TYPE_CHECKING
5
5
 
6
6
  from hypothesis import HealthCheck, given, settings
7
- from hypothesis.strategies import DataObject, data, sampled_from
8
- from pytest import raises
7
+ from hypothesis.strategies import DataObject, data
8
+ from pytest import mark, param, raises
9
+ from whenever import Date, ZonedDateTime
9
10
 
10
11
  from utilities.hypothesis import (
11
12
  assume_does_not_raise,
@@ -30,7 +31,7 @@ from utilities.zoneinfo import UTC, get_time_zone_name
30
31
  if TYPE_CHECKING:
31
32
  from collections.abc import Callable
32
33
 
33
- from whenever import Date, DateDelta, PlainDateTime, TimeDelta, ZonedDateTime
34
+ from whenever import DateDelta, PlainDateTime, TimeDelta
34
35
 
35
36
 
36
37
  class TestDatePeriod:
@@ -58,13 +59,30 @@ class TestDatePeriod:
58
59
  period = DatePeriod(start, end)
59
60
  assert period.delta == (end - start)
60
61
 
62
+ @mark.parametrize(
63
+ ("end", "expected"),
64
+ [
65
+ param(Date(2000, 1, 1), "20000101="),
66
+ param(Date(2000, 1, 2), "20000101-02"),
67
+ param(Date(2000, 1, 31), "20000101-31"),
68
+ param(Date(2000, 2, 1), "20000101-0201"),
69
+ param(Date(2000, 2, 29), "20000101-0229"),
70
+ param(Date(2000, 12, 31), "20000101-1231"),
71
+ param(Date(2001, 1, 1), "20000101-20010101"),
72
+ ],
73
+ )
74
+ def test_format_compact(self, *, end: Date, expected: str) -> None:
75
+ period = DatePeriod(Date(2000, 1, 1), end)
76
+ assert period.format_compact() == expected
77
+
61
78
  @given(dates=pairs(dates(), sorted=True))
62
79
  def test_hashable(self, *, dates: tuple[Date, Date]) -> None:
63
80
  start, end = dates
64
81
  period = DatePeriod(start, end)
65
82
  _ = hash(period)
66
83
 
67
- @given(dates=pairs(dates(), sorted=True), func=sampled_from([repr, str]))
84
+ @given(dates=pairs(dates(), sorted=True))
85
+ @mark.parametrize("func", [param(repr), param(str)])
68
86
  def test_repr(self, *, dates: tuple[Date, Date], func: Callable[..., str]) -> None:
69
87
  start, end = dates
70
88
  period = DatePeriod(start, end)
@@ -145,17 +163,126 @@ class TestZonedDateTimePeriod:
145
163
  period = ZonedDateTimePeriod(start, end)
146
164
  assert period.delta == (end - start)
147
165
 
166
+ @mark.parametrize(
167
+ ("end", "expected"),
168
+ [
169
+ param(
170
+ ZonedDateTime(2000, 1, 1, 10, 20, 30, tz=UTC.key),
171
+ "20000101T102030[UTC]=",
172
+ ),
173
+ param(
174
+ ZonedDateTime(2000, 1, 1, 10, 20, 31, tz=UTC.key),
175
+ "20000101T102030-102031[UTC]",
176
+ ),
177
+ param(
178
+ ZonedDateTime(2000, 1, 1, 10, 20, 59, tz=UTC.key),
179
+ "20000101T102030-102059[UTC]",
180
+ ),
181
+ param(
182
+ ZonedDateTime(2000, 1, 1, 10, 21, tz=UTC.key),
183
+ "20000101T102030-1021[UTC]",
184
+ ),
185
+ param(
186
+ ZonedDateTime(2000, 1, 1, 10, 21, 1, tz=UTC.key),
187
+ "20000101T102030-102101[UTC]",
188
+ ),
189
+ param(
190
+ ZonedDateTime(2000, 1, 1, 10, 59, 59, tz=UTC.key),
191
+ "20000101T102030-105959[UTC]",
192
+ ),
193
+ param(ZonedDateTime(2000, 1, 1, 11, tz=UTC.key), "20000101T102030-11[UTC]"),
194
+ param(
195
+ ZonedDateTime(2000, 1, 1, 11, 0, 1, tz=UTC.key),
196
+ "20000101T102030-110001[UTC]",
197
+ ),
198
+ param(
199
+ ZonedDateTime(2000, 1, 1, 23, 59, 59, tz=UTC.key),
200
+ "20000101T102030-235959[UTC]",
201
+ ),
202
+ param(ZonedDateTime(2000, 1, 2, tz=UTC.key), "20000101T102030-02T00[UTC]"),
203
+ param(
204
+ ZonedDateTime(2000, 1, 2, 0, 0, 1, tz=UTC.key),
205
+ "20000101T102030-02T000001[UTC]",
206
+ ),
207
+ param(
208
+ ZonedDateTime(2000, 1, 2, 0, 0, 59, tz=UTC.key),
209
+ "20000101T102030-02T000059[UTC]",
210
+ ),
211
+ param(
212
+ ZonedDateTime(2000, 1, 2, 0, 1, tz=UTC.key),
213
+ "20000101T102030-02T0001[UTC]",
214
+ ),
215
+ param(
216
+ ZonedDateTime(2000, 1, 31, 23, 59, 59, tz=UTC.key),
217
+ "20000101T102030-31T235959[UTC]",
218
+ ),
219
+ param(
220
+ ZonedDateTime(2000, 2, 1, tz=UTC.key), "20000101T102030-0201T00[UTC]"
221
+ ),
222
+ param(
223
+ ZonedDateTime(2000, 2, 1, 0, 0, 1, tz=UTC.key),
224
+ "20000101T102030-0201T000001[UTC]",
225
+ ),
226
+ param(
227
+ ZonedDateTime(2000, 2, 1, 0, 0, 59, tz=UTC.key),
228
+ "20000101T102030-0201T000059[UTC]",
229
+ ),
230
+ param(
231
+ ZonedDateTime(2000, 2, 1, 0, 1, tz=UTC.key),
232
+ "20000101T102030-0201T0001[UTC]",
233
+ ),
234
+ param(
235
+ ZonedDateTime(2000, 12, 31, 23, 59, 59, tz=UTC.key),
236
+ "20000101T102030-1231T235959[UTC]",
237
+ ),
238
+ param(
239
+ ZonedDateTime(2001, 1, 1, tz=UTC.key),
240
+ "20000101T102030-20010101T00[UTC]",
241
+ ),
242
+ param(
243
+ ZonedDateTime(2001, 1, 1, 0, 0, 1, tz=UTC.key),
244
+ "20000101T102030-20010101T000001[UTC]",
245
+ ),
246
+ param(
247
+ ZonedDateTime(2001, 1, 1, 0, 0, 59, tz=UTC.key),
248
+ "20000101T102030-20010101T000059[UTC]",
249
+ ),
250
+ param(
251
+ ZonedDateTime(2001, 1, 1, 0, 1, tz=UTC.key),
252
+ "20000101T102030-20010101T0001[UTC]",
253
+ ),
254
+ ],
255
+ )
256
+ def test_format_compact(self, *, end: ZonedDateTime, expected: str) -> None:
257
+ start = ZonedDateTime(2000, 1, 1, 10, 20, 30, tz=UTC.key)
258
+ period = ZonedDateTimePeriod(start, end)
259
+ assert period.format_compact() == expected
260
+
261
+ @mark.parametrize(
262
+ ("datetime", "expected"),
263
+ [
264
+ param(
265
+ ZonedDateTime(2000, 1, 1, 10, 20, 30, tz=UTC.key),
266
+ "20000101T102030[UTC]=",
267
+ ),
268
+ param(ZonedDateTime(2000, 1, 1, 10, 20, tz=UTC.key), "20000101T1020[UTC]="),
269
+ param(ZonedDateTime(2000, 1, 1, 10, tz=UTC.key), "20000101T10[UTC]="),
270
+ ],
271
+ )
272
+ def test_format_compact_extra(
273
+ self, *, datetime: ZonedDateTime, expected: str
274
+ ) -> None:
275
+ period = ZonedDateTimePeriod(datetime, datetime)
276
+ assert period.format_compact() == expected
277
+
148
278
  @given(datetimes=pairs(zoned_datetimes(), sorted=True))
149
279
  def test_hashable(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
150
280
  start, end = datetimes
151
281
  period = ZonedDateTimePeriod(start, end)
152
282
  _ = hash(period)
153
283
 
154
- @given(
155
- data=data(),
156
- datetimes=pairs(zoned_datetimes(), sorted=True),
157
- func=sampled_from([repr, str]),
158
- )
284
+ @given(data=data(), datetimes=pairs(zoned_datetimes(), sorted=True))
285
+ @mark.parametrize("func", [param(repr), param(str)])
159
286
  def test_repr(
160
287
  self,
161
288
  *,
@@ -89,7 +89,7 @@ from utilities.sqlalchemy import (
89
89
  )
90
90
  from utilities.text import strip_and_dedent
91
91
  from utilities.typing import get_args, get_literal_elements
92
- from utilities.whenever import SECOND, format_compact, get_now
92
+ from utilities.whenever import SECOND, format_compact, get_now, to_local_plain
93
93
 
94
94
  if TYPE_CHECKING:
95
95
  from collections.abc import Callable, Iterator
@@ -102,9 +102,8 @@ if TYPE_CHECKING:
102
102
 
103
103
  def _table_names() -> str:
104
104
  """Generate at unique string."""
105
- now = get_now()
106
105
  key = str(uuid4()).replace("-", "")
107
- return f"{format_compact(now)}_{key}"
106
+ return f"{format_compact(to_local_plain(get_now()))}_{key}"
108
107
 
109
108
 
110
109
  @overload
@@ -20,6 +20,7 @@ from whenever import (
20
20
  DateDelta,
21
21
  DateTimeDelta,
22
22
  PlainDateTime,
23
+ Time,
23
24
  TimeDelta,
24
25
  TimeZoneNotFoundError,
25
26
  ZonedDateTime,
@@ -33,7 +34,9 @@ from utilities.hypothesis import (
33
34
  freqs,
34
35
  months,
35
36
  pairs,
37
+ plain_datetimes,
36
38
  sentinels,
39
+ times,
37
40
  zoned_datetimes,
38
41
  )
39
42
  from utilities.sentinel import Sentinel, sentinel
@@ -96,6 +99,7 @@ from utilities.whenever import (
96
99
  to_date,
97
100
  to_date_time_delta,
98
101
  to_days,
102
+ to_local_plain,
99
103
  to_nanos,
100
104
  to_time_delta,
101
105
  to_zoned_date_time,
@@ -125,13 +129,36 @@ class TestDatetimeUTC:
125
129
 
126
130
 
127
131
  class TestFormatCompact:
128
- @given(datetime=zoned_datetimes())
129
- def test_main(self, *, datetime: ZonedDateTime) -> None:
132
+ @given(date=dates())
133
+ def test_date(self, *, date: Date) -> None:
134
+ result = format_compact(date)
135
+ assert isinstance(result, str)
136
+ parsed = Date.parse_common_iso(result)
137
+ assert parsed == date
138
+
139
+ @given(time=times())
140
+ def test_time(self, *, time: Time) -> None:
141
+ result = format_compact(time)
142
+ assert isinstance(result, str)
143
+ parsed = Time.parse_common_iso(result)
144
+ assert parsed == time
145
+
146
+ @given(datetime=plain_datetimes())
147
+ def test_plain_datetime(self, *, datetime: PlainDateTime) -> None:
130
148
  result = format_compact(datetime)
131
149
  assert isinstance(result, str)
132
150
  parsed = PlainDateTime.parse_common_iso(result)
133
151
  assert parsed.nanosecond == 0
134
- expected = datetime.round().to_tz(LOCAL_TIME_ZONE_NAME).to_plain()
152
+ expected = datetime.round()
153
+ assert parsed == expected
154
+
155
+ @given(datetime=zoned_datetimes())
156
+ def test_zoned_datetime(self, *, datetime: ZonedDateTime) -> None:
157
+ result = format_compact(datetime)
158
+ assert isinstance(result, str)
159
+ parsed = ZonedDateTime.parse_common_iso(result)
160
+ assert parsed.nanosecond == 0
161
+ expected = datetime.round()
135
162
  assert parsed == expected
136
163
 
137
164
 
@@ -610,6 +637,13 @@ class TestToDays:
610
637
  _ = to_days(delta)
611
638
 
612
639
 
640
+ class TestToLocalPlain:
641
+ @given(date_time=zoned_datetimes())
642
+ def test_main(self, *, date_time: ZonedDateTime) -> None:
643
+ result = to_local_plain(date_time)
644
+ assert isinstance(result, PlainDateTime)
645
+
646
+
613
647
  class TestToZonedDateTime:
614
648
  @given(date_time=zoned_datetimes())
615
649
  def test_date_time(self, *, date_time: ZonedDateTime) -> None:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.136.9"
3
+ __version__ = "0.137.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 format_compact, get_now
9
+ from utilities.whenever import format_compact, get_now, to_local_plain
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(), format_compact(get_now())
50
+ page_no, now = (self.page_no(), format_compact(to_local_plain(get_now())))
51
51
  text = f"page {page_no}/{{}}; {now}"
52
52
  _ = self.cell(
53
53
  w=0,
@@ -45,7 +45,12 @@ from utilities.re import (
45
45
  )
46
46
  from utilities.sentinel import Sentinel, sentinel
47
47
  from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
48
- from utilities.whenever import WheneverLogRecord, format_compact, get_now, get_now_local
48
+ from utilities.whenever import (
49
+ WheneverLogRecord,
50
+ format_compact,
51
+ get_now_local,
52
+ to_local_plain,
53
+ )
49
54
 
50
55
  if TYPE_CHECKING:
51
56
  from collections.abc import Callable, Iterable, Mapping
@@ -378,7 +383,7 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
378
383
  if not self.delay: # pragma: no cover
379
384
  self.stream = self._open()
380
385
  self._time_handler.rolloverAt = ( # skipif-ci-and-windows
381
- self._time_handler.computeRollover(get_now().timestamp())
386
+ self._time_handler.computeRollover(get_now_local().timestamp())
382
387
  )
383
388
 
384
389
  def _should_rollover(self, record: LogRecord, /) -> bool:
@@ -533,9 +538,9 @@ class _RotatingLogFile:
533
538
  case int() as index, None, None:
534
539
  tail = str(index)
535
540
  case int() as index, None, ZonedDateTime() as end:
536
- tail = f"{index}__{format_compact(end)}"
541
+ tail = f"{index}__{format_compact(to_local_plain(end))}"
537
542
  case int() as index, ZonedDateTime() as start, ZonedDateTime() as end:
538
- tail = f"{index}__{format_compact(start)}__{format_compact(end)}"
543
+ tail = f"{index}__{format_compact(to_local_plain(start))}__{format_compact(to_local_plain(end))}"
539
544
  case _: # pragma: no cover
540
545
  raise ImpossibleCaseError(
541
546
  case=[f"{self.index=}", f"{self.start=}", f"{self.end=}"]
@@ -9,6 +9,7 @@ from whenever import Date, DateDelta, TimeDelta, ZonedDateTime
9
9
  from utilities.dataclasses import replace_non_sentinel
10
10
  from utilities.functions import get_class_name
11
11
  from utilities.sentinel import Sentinel, sentinel
12
+ from utilities.whenever import format_compact
12
13
  from utilities.zoneinfo import get_time_zone_name
13
14
 
14
15
  if TYPE_CHECKING:
@@ -53,6 +54,17 @@ class DatePeriod:
53
54
  """The delta of the period."""
54
55
  return self.end - self.start
55
56
 
57
+ def format_compact(self) -> str:
58
+ """Format the period in a compact fashion."""
59
+ fc, start, end = format_compact, self.start, self.end
60
+ if self.start == self.end:
61
+ return f"{fc(start)}="
62
+ if self.start.year_month() == self.end.year_month():
63
+ return f"{fc(start)}-{fc(end, fmt='%d')}"
64
+ if self.start.year == self.end.year:
65
+ return f"{fc(start)}-{fc(end, fmt='%m%d')}"
66
+ return f"{fc(start)}-{fc(end)}"
67
+
56
68
  def replace(
57
69
  self, *, start: Date | Sentinel = sentinel, end: Date | Sentinel = sentinel
58
70
  ) -> Self:
@@ -101,6 +113,39 @@ class ZonedDateTimePeriod:
101
113
  """The duration of the period."""
102
114
  return self.end - self.start
103
115
 
116
+ def format_compact(self) -> str:
117
+ """Format the period in a compact fashion."""
118
+ fc, start, end = format_compact, self.start, self.end
119
+ if start == end:
120
+ if end.second != 0:
121
+ return f"{fc(start)}="
122
+ if end.minute != 0:
123
+ return f"{fc(start, fmt='%Y%m%dT%H%M')}="
124
+ return f"{fc(start, fmt='%Y%m%dT%H')}="
125
+ if start.date() == end.date():
126
+ if end.second != 0:
127
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%H%M%S')}"
128
+ if end.minute != 0:
129
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%H%M')}"
130
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%H')}"
131
+ if start.date().year_month() == end.date().year_month():
132
+ if end.second != 0:
133
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%dT%H%M%S')}"
134
+ if end.minute != 0:
135
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%dT%H%M')}"
136
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%dT%H')}"
137
+ if start.year == end.year:
138
+ if end.second != 0:
139
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%m%dT%H%M%S')}"
140
+ if end.minute != 0:
141
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%m%dT%H%M')}"
142
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%m%dT%H')}"
143
+ if end.second != 0:
144
+ return f"{fc(start.to_plain())}-{fc(end)}"
145
+ if end.minute != 0:
146
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%Y%m%dT%H%M')}"
147
+ return f"{fc(start.to_plain())}-{fc(end, fmt='%Y%m%dT%H')}"
148
+
104
149
  def replace(
105
150
  self,
106
151
  *,
@@ -8,7 +8,7 @@ from pyinstrument.profiler import Profiler
8
8
 
9
9
  from utilities.atomicwrites import writer
10
10
  from utilities.pathlib import get_path
11
- from utilities.whenever import format_compact, get_now
11
+ from utilities.whenever import format_compact, get_now, to_local_plain
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from collections.abc import Iterator
@@ -22,7 +22,7 @@ def profile(*, path: MaybeCallablePathLike | None = Path.cwd) -> Iterator[None]:
22
22
  with Profiler() as profiler:
23
23
  yield
24
24
  filename = get_path(path=path).joinpath(
25
- f"profile__{format_compact(get_now())}.html"
25
+ f"profile__{format_compact(to_local_plain(get_now()))}.html"
26
26
  )
27
27
  with writer(filename) as temp, temp.open(mode="w") as fh:
28
28
  _ = fh.write(profiler.output_html())
@@ -27,7 +27,13 @@ from utilities.reprlib import (
27
27
  yield_mapping_repr,
28
28
  )
29
29
  from utilities.version import get_version
30
- from utilities.whenever import format_compact, get_now, to_zoned_date_time
30
+ from utilities.whenever import (
31
+ format_compact,
32
+ get_now,
33
+ get_now_local,
34
+ to_local_plain,
35
+ to_zoned_date_time,
36
+ )
31
37
 
32
38
  if TYPE_CHECKING:
33
39
  from collections.abc import Callable, Iterator, Sequence
@@ -89,7 +95,7 @@ def _yield_header_lines(
89
95
  version: MaybeCallableVersionLike | None = None,
90
96
  ) -> Iterator[str]:
91
97
  """Yield the header lines."""
92
- now = get_now()
98
+ now = get_now_local()
93
99
  start_use = to_zoned_date_time(date_time=start)
94
100
  yield f"Date/time | {format_compact(now)}"
95
101
  start_str = "" if start_use is None else format_compact(start_use)
@@ -250,7 +256,9 @@ def _make_except_hook_inner(
250
256
  _ = sys.stderr.write(f"{slim}\n") # don't 'from sys import stderr'
251
257
  if path is not None:
252
258
  path = (
253
- get_path(path=path).joinpath(format_compact(get_now())).with_suffix(".txt")
259
+ get_path(path=path)
260
+ .joinpath(format_compact(to_local_plain(get_now())))
261
+ .with_suffix(".txt")
254
262
  )
255
263
  full = format_exception_stack(
256
264
  exc_val,
@@ -23,6 +23,7 @@ from whenever import (
23
23
  DateDelta,
24
24
  DateTimeDelta,
25
25
  PlainDateTime,
26
+ Time,
26
27
  TimeDelta,
27
28
  ZonedDateTime,
28
29
  )
@@ -160,10 +161,25 @@ def datetime_utc(
160
161
  ##
161
162
 
162
163
 
163
- def format_compact(datetime: ZonedDateTime, /) -> str:
164
- """Convert a zoned datetime to the local time zone, then format."""
165
- py_datetime = datetime.round().to_tz(LOCAL_TIME_ZONE_NAME).to_plain().py_datetime()
166
- return py_datetime.strftime(get_strftime("%Y%m%dT%H%M%S"))
164
+ def format_compact(
165
+ obj: Date | Time | PlainDateTime | ZonedDateTime, /, *, fmt: str | None = None
166
+ ) -> str:
167
+ """Format the date/datetime in a compact fashion."""
168
+ match obj:
169
+ case Date() as date:
170
+ obj_use = date.py_date()
171
+ fmt_use = "%Y%m%d" if fmt is None else fmt
172
+ case Time() as time:
173
+ obj_use = time.py_time()
174
+ fmt_use = "%H%M%S" if fmt is None else fmt
175
+ case PlainDateTime() as datetime:
176
+ obj_use = datetime.round().py_datetime()
177
+ fmt_use = "%Y%m%dT%H%M%S" if fmt is None else fmt
178
+ case ZonedDateTime() as datetime:
179
+ return f"{format_compact(datetime.to_plain(), fmt=fmt)}[{datetime.tz}]"
180
+ case _ as never:
181
+ assert_never(never)
182
+ return obj_use.strftime(get_strftime(fmt_use))
167
183
 
168
184
 
169
185
  ##
@@ -652,6 +668,14 @@ def to_date_time_delta(nanos: int, /) -> DateTimeDelta:
652
668
  ##
653
669
 
654
670
 
671
+ def to_local_plain(date_time: ZonedDateTime, /) -> PlainDateTime:
672
+ """Convert a datetime to its local/plain variant."""
673
+ return date_time.to_tz(LOCAL_TIME_ZONE_NAME).to_plain()
674
+
675
+
676
+ ##
677
+
678
+
655
679
  def to_nanos(delta: DateTimeDelta, /) -> int:
656
680
  """Compute the number of nanoseconds in a date-time delta."""
657
681
  months, days, _, _ = delta.in_months_days_secs_nanos()
@@ -885,6 +909,7 @@ __all__ = [
885
909
  "to_date",
886
910
  "to_date_time_delta",
887
911
  "to_days",
912
+ "to_local_plain",
888
913
  "to_nanos",
889
914
  "to_zoned_date_time",
890
915
  ]