dycw-utilities 0.129.9__tar.gz → 0.129.14__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 (234) hide show
  1. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/PKG-INFO +14 -7
  2. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/pyproject.toml +24 -11
  3. dycw_utilities-0.129.14/src/tests/test_aiolimiter.py +39 -0
  4. dycw_utilities-0.129.14/src/tests/test_git.py +27 -0
  5. dycw_utilities-0.129.14/src/tests/test_inflect.py +20 -0
  6. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pathlib.py +21 -0
  7. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/__init__.py +1 -1
  8. dycw_utilities-0.129.14/src/utilities/aiolimiter.py +25 -0
  9. dycw_utilities-0.129.14/src/utilities/git.py +40 -0
  10. dycw_utilities-0.129.14/src/utilities/inflect.py +17 -0
  11. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/logging.py +9 -6
  12. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pathlib.py +12 -1
  13. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/scipy.py +1 -1
  14. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/.gitignore +0 -0
  15. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/LICENSE +0 -0
  16. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/README.md +0 -0
  17. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/__init__.py +0 -0
  18. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/conftest.py +0 -0
  19. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/__init__.py +0 -0
  20. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_missing/__init__.py +0 -0
  21. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_missing/module.py +0 -0
  22. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/__init__.py +0 -0
  23. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/outer_1.py +0 -0
  24. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/outer_2.py +0 -0
  25. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  26. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  27. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  28. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  29. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_without/__init__.py +0 -0
  30. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_without/module_1.py +0 -0
  31. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/package_without/module_2.py +0 -0
  32. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/standalone.py +0 -0
  33. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/modules/with_imports.py +0 -0
  34. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  35. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  36. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  37. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  38. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  39. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  40. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  41. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  42. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_altair.py +0 -0
  43. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_asyncio.py +0 -0
  44. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_asyncio_classes/__init__.py +0 -0
  45. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_asyncio_classes/loopers.py +0 -0
  46. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_asyncio_classes/redis.py +0 -0
  47. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_atomicwrites.py +0 -0
  48. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_atools.py +0 -0
  49. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_cachetools.py +0 -0
  50. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_click.py +0 -0
  51. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_concurrent.py +0 -0
  52. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_contextlib.py +0 -0
  53. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_contextvars.py +0 -0
  54. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_cryptography.py +0 -0
  55. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_cvxpy.py +0 -0
  56. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_dataclasses.py +0 -0
  57. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_datetime.py +0 -0
  58. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_enum.py +0 -0
  59. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_errors.py +0 -0
  60. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_eventkit.py +0 -0
  61. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_fastapi.py +0 -0
  62. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_fpdf2.py +0 -0
  63. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_functions.py +0 -0
  64. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_functools.py +0 -0
  65. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_getpass.py +0 -0
  66. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_hashlib.py +0 -0
  67. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_http.py +0 -0
  68. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_hypothesis.py +0 -0
  69. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_importlib.py +0 -0
  70. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_ipython.py +0 -0
  71. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_iterables.py +0 -0
  72. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_jupyter.py +0 -0
  73. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_libcst.py +0 -0
  74. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_lightweight_charts.py +0 -0
  75. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_logging.py +0 -0
  76. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_loguru.py +0 -0
  77. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_luigi.py +0 -0
  78. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_math.py +0 -0
  79. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_memory_profiler.py +0 -0
  80. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_modules.py +0 -0
  81. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_more_itertools.py +0 -0
  82. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_numpy.py +0 -0
  83. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_operator.py +0 -0
  84. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_optuna.py +0 -0
  85. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_orjson.py +0 -0
  86. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_os.py +0 -0
  87. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_parse.py +0 -0
  88. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_period.py +0 -0
  89. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pickle.py +0 -0
  90. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_platform.py +0 -0
  91. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_polars.py +0 -0
  92. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_polars_ols.py +0 -0
  93. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pottery.py +0 -0
  94. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pqdm.py +0 -0
  95. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_psutil.py +0 -0
  96. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pydantic.py +0 -0
  97. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pyinstrument.py +0 -0
  98. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pyrsistent.py +0 -0
  99. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pytest.py +0 -0
  100. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_pytest_regressions.py +0 -0
  101. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_python_dotenv.py +0 -0
  102. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_random.py +0 -0
  103. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_re.py +0 -0
  104. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_redis.py +0 -0
  105. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_reprlib.py +0 -0
  106. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_scipy.py +0 -0
  107. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_sentinel.py +0 -0
  108. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_shelve.py +0 -0
  109. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_slack_sdk.py +0 -0
  110. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_socket.py +0 -0
  111. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_sqlalchemy.py +0 -0
  112. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_sqlalchemy_polars.py +0 -0
  113. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_statsmodel.py +0 -0
  114. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_streamlit.py +0 -0
  115. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_string.py +0 -0
  116. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_sys.py +0 -0
  117. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_tempfile.py +0 -0
  118. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_tenacity.py +0 -0
  119. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_text.py +0 -0
  120. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_threading.py +0 -0
  121. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_timer.py +0 -0
  122. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback.py +0 -0
  123. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/__init__.py +0 -0
  124. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/chain.py +0 -0
  125. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  126. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  127. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  128. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/many.py +0 -0
  129. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/one.py +0 -0
  130. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/recursive.py +0 -0
  131. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  132. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  133. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/two.py +0 -0
  134. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_traceback_funcs/untraced.py +0 -0
  135. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_types.py +0 -0
  136. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_typing.py +0 -0
  137. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_typing_funcs/__init__.py +0 -0
  138. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_typing_funcs/no_future.py +0 -0
  139. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_typing_funcs/with_future.py +0 -0
  140. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_tzdata.py +0 -0
  141. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_tzlocal.py +0 -0
  142. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_uuid.py +0 -0
  143. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_version.py +0 -0
  144. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_warnings.py +0 -0
  145. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_whenever.py +0 -0
  146. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_zipfile.py +0 -0
  147. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/tests/test_zoneinfo.py +0 -0
  148. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/altair.py +0 -0
  149. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/asyncio.py +0 -0
  150. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/atomicwrites.py +0 -0
  151. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/atools.py +0 -0
  152. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/cachetools.py +0 -0
  153. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/click.py +0 -0
  154. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/concurrent.py +0 -0
  155. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/contextlib.py +0 -0
  156. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/contextvars.py +0 -0
  157. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/cryptography.py +0 -0
  158. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/cvxpy.py +0 -0
  159. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/dataclasses.py +0 -0
  160. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/datetime.py +0 -0
  161. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/enum.py +0 -0
  162. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/errors.py +0 -0
  163. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/eventkit.py +0 -0
  164. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/fastapi.py +0 -0
  165. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/fpdf2.py +0 -0
  166. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/functions.py +0 -0
  167. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/functools.py +0 -0
  168. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/getpass.py +0 -0
  169. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/hashlib.py +0 -0
  170. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/http.py +0 -0
  171. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/hypothesis.py +0 -0
  172. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/importlib.py +0 -0
  173. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/ipython.py +0 -0
  174. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/iterables.py +0 -0
  175. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/jupyter.py +0 -0
  176. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/libcst.py +0 -0
  177. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/lightweight_charts.py +0 -0
  178. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/loguru.py +0 -0
  179. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/luigi.py +0 -0
  180. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/math.py +0 -0
  181. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/memory_profiler.py +0 -0
  182. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/modules.py +0 -0
  183. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/more_itertools.py +0 -0
  184. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/numpy.py +0 -0
  185. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/operator.py +0 -0
  186. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/optuna.py +0 -0
  187. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/orjson.py +0 -0
  188. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/os.py +0 -0
  189. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/parse.py +0 -0
  190. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/period.py +0 -0
  191. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pickle.py +0 -0
  192. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/platform.py +0 -0
  193. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/polars.py +0 -0
  194. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/polars_ols.py +0 -0
  195. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pottery.py +0 -0
  196. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pqdm.py +0 -0
  197. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/psutil.py +0 -0
  198. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/py.typed +0 -0
  199. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pydantic.py +0 -0
  200. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pyinstrument.py +0 -0
  201. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pyrsistent.py +0 -0
  202. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pytest.py +0 -0
  203. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/pytest_regressions.py +0 -0
  204. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/python_dotenv.py +0 -0
  205. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/random.py +0 -0
  206. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/re.py +0 -0
  207. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/redis.py +0 -0
  208. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/reprlib.py +0 -0
  209. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/sentinel.py +0 -0
  210. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/shelve.py +0 -0
  211. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/slack_sdk.py +0 -0
  212. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/socket.py +0 -0
  213. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/sqlalchemy.py +0 -0
  214. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/sqlalchemy_polars.py +0 -0
  215. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/statsmodels.py +0 -0
  216. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/streamlit.py +0 -0
  217. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/string.py +0 -0
  218. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/sys.py +0 -0
  219. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/tempfile.py +0 -0
  220. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/tenacity.py +0 -0
  221. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/text.py +0 -0
  222. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/threading.py +0 -0
  223. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/timer.py +0 -0
  224. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/traceback.py +0 -0
  225. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/types.py +0 -0
  226. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/typing.py +0 -0
  227. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/tzdata.py +0 -0
  228. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/tzlocal.py +0 -0
  229. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/uuid.py +0 -0
  230. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/version.py +0 -0
  231. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/warnings.py +0 -0
  232. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/whenever.py +0 -0
  233. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/zipfile.py +0 -0
  234. {dycw_utilities-0.129.9 → dycw_utilities-0.129.14}/src/utilities/zoneinfo.py +0 -0
@@ -1,13 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.129.9
3
+ Version: 0.129.14
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: typing-extensions<4.15,>=4.14.0
8
+ Provides-Extra: logging
9
+ Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'logging'
10
+ Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
11
+ Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'logging'
12
+ Requires-Dist: whenever<0.9,>=0.8.4; extra == 'logging'
8
13
  Provides-Extra: test
9
14
  Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
10
- Requires-Dist: hypothesis<6.136,>=6.135.0; extra == 'test'
15
+ Requires-Dist: hypothesis<6.136,>=6.135.2; extra == 'test'
11
16
  Requires-Dist: pytest-asyncio<1.1,>=1.0.0; extra == 'test'
12
17
  Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
13
18
  Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
@@ -19,6 +24,8 @@ Requires-Dist: pytest-rng<1.1,>=1.0.0; extra == 'test'
19
24
  Requires-Dist: pytest-timeout<2.5,>=2.4.0; extra == 'test'
20
25
  Requires-Dist: pytest-xdist<3.8,>=3.7.0; extra == 'test'
21
26
  Requires-Dist: pytest<8.4,>=8.3.5; extra == 'test'
27
+ Provides-Extra: zzz-test-aiolimiter
28
+ Requires-Dist: aiolimiter<1.3,>=1.2.1; extra == 'zzz-test-aiolimiter'
22
29
  Provides-Extra: zzz-test-altair
23
30
  Requires-Dist: altair<5.6,>=5.5.0; extra == 'zzz-test-altair'
24
31
  Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-altair'
@@ -77,9 +84,9 @@ Provides-Extra: zzz-test-hypothesis
77
84
  Requires-Dist: aiosqlite<0.22,>=0.21.0; extra == 'zzz-test-hypothesis'
78
85
  Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
79
86
  Requires-Dist: greenlet<3.3,>=3.2.0; extra == 'zzz-test-hypothesis'
80
- Requires-Dist: hypothesis<6.136,>=6.135.0; extra == 'zzz-test-hypothesis'
87
+ Requires-Dist: hypothesis<6.136,>=6.135.2; extra == 'zzz-test-hypothesis'
81
88
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
82
- Requires-Dist: numpy<2.3,>=2.2.6; extra == 'zzz-test-hypothesis'
89
+ Requires-Dist: numpy<2.4,>=2.3.0; extra == 'zzz-test-hypothesis'
83
90
  Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
84
91
  Requires-Dist: redis<6.3,>=6.2.0; extra == 'zzz-test-hypothesis'
85
92
  Requires-Dist: sqlalchemy<2.1,>=2.0.41; extra == 'zzz-test-hypothesis'
@@ -108,14 +115,14 @@ Provides-Extra: zzz-test-luigi
108
115
  Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-luigi'
109
116
  Requires-Dist: whenever<0.9,>=0.8.4; extra == 'zzz-test-luigi'
110
117
  Provides-Extra: zzz-test-math
111
- Requires-Dist: numpy<2.3,>=2.2.6; extra == 'zzz-test-math'
118
+ Requires-Dist: numpy<2.4,>=2.3.0; extra == 'zzz-test-math'
112
119
  Provides-Extra: zzz-test-memory-profiler
113
120
  Requires-Dist: memory-profiler<0.62,>=0.61.0; extra == 'zzz-test-memory-profiler'
114
121
  Provides-Extra: zzz-test-modules
115
122
  Provides-Extra: zzz-test-more-itertools
116
123
  Requires-Dist: more-itertools<10.8,>=10.7.0; extra == 'zzz-test-more-itertools'
117
124
  Provides-Extra: zzz-test-numpy
118
- Requires-Dist: numpy<2.3,>=2.2.6; extra == 'zzz-test-numpy'
125
+ Requires-Dist: numpy<2.4,>=2.3.0; extra == 'zzz-test-numpy'
119
126
  Provides-Extra: zzz-test-operator
120
127
  Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-operator'
121
128
  Requires-Dist: whenever<0.9,>=0.8.4; extra == 'zzz-test-operator'
@@ -172,7 +179,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
172
179
  Provides-Extra: zzz-test-sentinel
173
180
  Provides-Extra: zzz-test-shelve
174
181
  Provides-Extra: zzz-test-slack-sdk
175
- Requires-Dist: aiohttp<3.12.10,>=3.12.9; extra == 'zzz-test-slack-sdk'
182
+ Requires-Dist: aiohttp<3.13,>=3.12.11; extra == 'zzz-test-slack-sdk'
176
183
  Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
177
184
  Provides-Extra: zzz-test-socket
178
185
  Provides-Extra: zzz-test-sqlalchemy
@@ -8,7 +8,8 @@ requires = ["hatchling"]
8
8
  # dependency groups
9
9
  [dependency-groups]
10
10
  dev = [
11
- "aiohttp >= 3.12.9, < 3.12.10", # for slack
11
+ "aiohttp >= 3.12.11, < 3.13", # for slack
12
+ "aiolimiter >= 1.2.1, < 1.3",
12
13
  "aiosqlite >= 0.21.0, < 0.22",
13
14
  "altair >= 5.5.0, < 5.6",
14
15
  "asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
@@ -24,8 +25,9 @@ dev = [
24
25
  "fpdf2 >= 2.8.3, < 2.9",
25
26
  "greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
26
27
  "httpx >= 0.28.1, < 0.29", # for fastapi
27
- "hypothesis >= 6.135.0, < 6.136",
28
+ "hypothesis >= 6.135.2, < 6.136",
28
29
  "img2pdf >= 0.6.0, < 0.7",
30
+ "inflect >= 7.5.0, < 7.6",
29
31
  "lightweight-charts >= 2.1, < 2.2",
30
32
  "loguru >= 0.7.3, < 0.8",
31
33
  "luigi >= 3.6.0, < 3.7",
@@ -33,7 +35,7 @@ dev = [
33
35
  "more-itertools >= 10.7.0, < 10.8",
34
36
  "nest-asyncio >= 1.6.0, < 1.7", # for sqlalchemy async
35
37
  "nox >= 2025.2.9, < 2025.3",
36
- "numpy >= 2.2.6, < 2.3",
38
+ "numpy >= 2.3.0, < 2.4",
37
39
  "libcst >= 1.8.0, < 1.9",
38
40
  "optuna >= 4.3.0, < 4.4",
39
41
  "orjson >= 3.10.18, < 3.11",
@@ -82,6 +84,7 @@ dev = [
82
84
  "pytest-xdist >= 3.7.0, < 3.8",
83
85
  # CI
84
86
  "win32_setctime >= 1.2.0, < 1.3", # https://github.com/Delgan/loguru/issues/147
87
+ "aiolimiter>=1.2.1",
85
88
  ]
86
89
 
87
90
  # project
@@ -93,12 +96,18 @@ dependencies = [
93
96
  name = "dycw-utilities"
94
97
  readme = "README.md"
95
98
  requires-python = ">= 3.12"
96
- version = "0.129.9"
99
+ version = "0.129.14"
97
100
 
98
101
  [project.optional-dependencies]
102
+ logging = [
103
+ "atomicwrites >= 1.4.1, < 1.5",
104
+ "coloredlogs >= 15.0.1, < 15.1",
105
+ "tzlocal >= 5.3.1, < 5.4",
106
+ "whenever >= 0.8.4, < 0.9",
107
+ ]
99
108
  test = [
100
109
  "dycw-pytest-only >= 2.1.1, < 2.2",
101
- "hypothesis >= 6.135.0, < 6.136",
110
+ "hypothesis >= 6.135.2, < 6.136",
102
111
  "pytest >= 8.3.5, < 8.4",
103
112
  "pytest-asyncio >= 1.0.0, < 1.1",
104
113
  "pytest-cov >= 6.1.1, < 6.2",
@@ -111,6 +120,9 @@ test = [
111
120
  "pytest-timeout >= 2.4.0, < 2.5",
112
121
  "pytest-xdist >= 3.7.0, < 3.8",
113
122
  ]
123
+ zzz-test-aiolimiter = [
124
+ "aiolimiter >= 1.2.1, < 1.3",
125
+ ]
114
126
  zzz-test-altair = [
115
127
  "altair >= 5.5.0, < 5.6",
116
128
  "atomicwrites >= 1.4.1, < 1.5",
@@ -173,9 +185,9 @@ zzz-test-hypothesis = [
173
185
  "aiosqlite >= 0.21.0, < 0.22",
174
186
  "asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
175
187
  "greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
176
- "hypothesis >= 6.135.0, < 6.136",
188
+ "hypothesis >= 6.135.2, < 6.136",
177
189
  "luigi >= 3.6.0, < 3.7",
178
- "numpy >= 2.2.6, < 2.3",
190
+ "numpy >= 2.3.0, < 2.4",
179
191
  "pathvalidate >= 3.2.3, < 3.3",
180
192
  "redis >= 6.2.0, < 6.3",
181
193
  "sqlalchemy >= 2.0.41, < 2.1",
@@ -206,11 +218,11 @@ zzz-test-luigi = [
206
218
  "luigi >= 3.6.0, < 3.7",
207
219
  "whenever >= 0.8.4, < 0.9",
208
220
  ]
209
- zzz-test-math = ["numpy >= 2.2.6, < 2.3"]
221
+ zzz-test-math = ["numpy >= 2.3.0, < 2.4"]
210
222
  zzz-test-memory-profiler = ["memory-profiler >= 0.61.0, < 0.62"]
211
223
  zzz-test-modules = []
212
224
  zzz-test-more-itertools = ["more-itertools >= 10.7.0, < 10.8"]
213
- zzz-test-numpy = ["numpy >= 2.2.6, < 2.3"]
225
+ zzz-test-numpy = ["numpy >= 2.3.0, < 2.4"]
214
226
  zzz-test-operator = [
215
227
  "polars-lts-cpu >= 1.30.0, < 1.31",
216
228
  "whenever >= 0.8.4, < 0.9",
@@ -268,7 +280,7 @@ zzz-test-scipy = ["scipy >= 1.15.3, < 1.16"]
268
280
  zzz-test-sentinel = []
269
281
  zzz-test-shelve = []
270
282
  zzz-test-slack-sdk = [
271
- "aiohttp >= 3.12.9, < 3.12.10", # for slack
283
+ "aiohttp >= 3.12.11, < 3.13", # for slack
272
284
  "slack-sdk >= 3.35.0, < 3.36",
273
285
  ]
274
286
  zzz-test-socket = []
@@ -332,7 +344,7 @@ zzz-test-zoneinfo = [
332
344
  # bump-my-version
333
345
  [tool.bumpversion]
334
346
  allow_dirty = true
335
- current_version = "0.129.9"
347
+ current_version = "0.129.14"
336
348
 
337
349
  [[tool.bumpversion.files]]
338
350
  filename = "src/utilities/__init__.py"
@@ -443,6 +455,7 @@ filterwarnings = [
443
455
  "ignore:Implicitly cleaning up <TemporaryDirectory '.*'>:ResourceWarning",
444
456
  "ignore:ResourceTracker called reentrantly for resource cleanup, which is unsupported:UserWarning",
445
457
  "ignore:Task .* without outputs has no custom complete.* method:UserWarning", # luigi
458
+ "ignore:The garbage collector is trying to clean up non-checked-in connection <AdaptedConnection <Connection(.*)>>:sqlalchemy.exc.SAWarning", # sqlalchemy
446
459
  "ignore:There is no current event loop:DeprecationWarning", # eventkit
447
460
  "ignore:Using fork.* can cause Polars to deadlock in the child process:RuntimeWarning", # polars/pqdm
448
461
  "ignore:coroutine 'AsyncConnection.close' was never awaited:RuntimeWarning",
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from asyncio import sleep
4
+ from typing import ClassVar
5
+
6
+ from utilities.aiolimiter import get_async_limiter
7
+ from utilities.text import unique_str
8
+ from utilities.timer import Timer
9
+
10
+
11
+ class TestGetAsyncLimiter:
12
+ async def test_main(self) -> None:
13
+ counter = 0
14
+
15
+ async def increment() -> None:
16
+ nonlocal counter
17
+ counter += 1
18
+ await sleep(0.01)
19
+
20
+ name = unique_str()
21
+ with Timer() as timer:
22
+ for _ in range(2):
23
+ async with get_async_limiter(name, rate=0.5):
24
+ await increment()
25
+ assert timer >= 0.48
26
+
27
+ shared: ClassVar[str] = unique_str()
28
+
29
+ async def test_shared1(self) -> None:
30
+ async with get_async_limiter(self.shared):
31
+ await sleep(0.01)
32
+
33
+ async def test_shared2(self) -> None:
34
+ async with get_async_limiter(self.shared):
35
+ await sleep(0.01)
36
+
37
+ async def test_shared3(self) -> None:
38
+ async with get_async_limiter(self.shared):
39
+ await sleep(0.01)
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from hypothesis import given, settings
6
+ from pytest import raises
7
+
8
+ from utilities.git import GetRepoRootError, get_repo_root
9
+ from utilities.hypothesis import git_repos
10
+
11
+ if TYPE_CHECKING:
12
+ from pathlib import Path
13
+
14
+
15
+ class TestGetRepoRoot:
16
+ @given(repo=git_repos())
17
+ @settings(max_examples=1)
18
+ def test_main(self, *, repo: Path) -> None:
19
+ root = get_repo_root(path=repo)
20
+ expected = repo.resolve()
21
+ assert root == expected
22
+
23
+ def test_error(self, *, tmp_path: Path) -> None:
24
+ with raises(
25
+ GetRepoRootError, match="Path is not part of a `git` repository: .*"
26
+ ):
27
+ _ = get_repo_root(path=tmp_path)
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from pytest import mark, param
4
+
5
+ from utilities.inflect import counted_noun
6
+
7
+
8
+ class TestCountedNoun:
9
+ @mark.parametrize(
10
+ ("num", "expected"),
11
+ [
12
+ param(0, "0 words"),
13
+ param(1, "1 word"),
14
+ param(2, "2 words"),
15
+ param(3, "3 words"),
16
+ ],
17
+ )
18
+ def test_main(self, *, num: int, expected: str) -> None:
19
+ result = counted_noun(num, "word")
20
+ assert result == expected
@@ -8,11 +8,13 @@ from hypothesis import given, settings
8
8
  from hypothesis.strategies import integers, sets
9
9
  from pytest import mark, param, raises
10
10
 
11
+ from tests.conftest import SKIPIF_CI_AND_WINDOWS
11
12
  from utilities.dataclasses import replace_non_sentinel
12
13
  from utilities.hypothesis import git_repos, paths, temp_paths
13
14
  from utilities.pathlib import (
14
15
  GetRootError,
15
16
  ensure_suffix,
17
+ expand_path,
16
18
  get_path,
17
19
  get_root,
18
20
  list_dir,
@@ -40,6 +42,25 @@ class TestEnsureSuffix:
40
42
  assert result == expected
41
43
 
42
44
 
45
+ class TestExpandPath:
46
+ @mark.parametrize(
47
+ ("path", "expected"),
48
+ [
49
+ param("foo", Path("foo")),
50
+ param("~", Path.home()),
51
+ param("~/foo", Path.home().joinpath("foo")),
52
+ param("$HOME", Path.home(), marks=SKIPIF_CI_AND_WINDOWS),
53
+ param(
54
+ "$HOME/foo", Path.home().joinpath("foo"), marks=SKIPIF_CI_AND_WINDOWS
55
+ ),
56
+ ],
57
+ ids=str,
58
+ )
59
+ def test_main(self, *, path: Path, expected: Path) -> None:
60
+ result = expand_path(path)
61
+ assert result == expected
62
+
63
+
43
64
  class TestGetPath:
44
65
  @given(path=paths())
45
66
  def test_path(self, *, path: Path) -> None:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.129.9"
3
+ __version__ = "0.129.14"
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from asyncio import get_running_loop
4
+ from typing import TYPE_CHECKING
5
+
6
+ from aiolimiter import AsyncLimiter
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Hashable
10
+
11
+ _LIMITERS: dict[tuple[int, Hashable], AsyncLimiter] = {}
12
+
13
+
14
+ def get_async_limiter(key: Hashable, /, *, rate: float = 1.0) -> AsyncLimiter:
15
+ """Get a loop-aware rate limiter."""
16
+ id_ = id(get_running_loop())
17
+ full = (id_, key)
18
+ try:
19
+ return _LIMITERS[full]
20
+ except KeyError:
21
+ limiter = _LIMITERS[full] = AsyncLimiter(1.0, time_period=rate)
22
+ return limiter
23
+
24
+
25
+ __all__ = ["get_async_limiter"]
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from re import IGNORECASE, search
6
+ from subprocess import PIPE, CalledProcessError, check_output
7
+ from typing import TYPE_CHECKING, override
8
+
9
+ from utilities.pathlib import PWD
10
+
11
+ if TYPE_CHECKING:
12
+ from utilities.types import PathLike
13
+
14
+
15
+ def get_repo_root(*, path: PathLike = PWD) -> Path:
16
+ """Get the repo root."""
17
+ try:
18
+ output = check_output(
19
+ ["git", "rev-parse", "--show-toplevel"], stderr=PIPE, cwd=path, text=True
20
+ )
21
+ except CalledProcessError as error:
22
+ # newer versions of git report "Not a git repository", whilst older
23
+ # versions report "not a git repository"
24
+ if search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
25
+ raise GetRepoRootError(cwd=path) from error
26
+ raise # pragma: no cover
27
+ else:
28
+ return Path(output.strip("\n"))
29
+
30
+
31
+ @dataclass(kw_only=True, slots=True)
32
+ class GetRepoRootError(Exception):
33
+ cwd: PathLike
34
+
35
+ @override
36
+ def __str__(self) -> str:
37
+ return f"Path is not part of a `git` repository: {self.cwd}"
38
+
39
+
40
+ __all__ = ["GetRepoRootError", "get_repo_root"]
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import cast
4
+
5
+ from inflect import Word, engine
6
+
7
+ _ENGINE = engine()
8
+
9
+
10
+ def counted_noun(num: int, noun: str, /) -> str:
11
+ """Construct a counted noun."""
12
+ word = cast("Word", noun)
13
+ sin_or_plu = _ENGINE.plural_noun(word, count=num)
14
+ return f"{num} {sin_or_plu}"
15
+
16
+
17
+ __all__ = ["counted_noun"]
@@ -85,6 +85,9 @@ except ModuleNotFoundError: # pragma: no cover
85
85
  type _When = Literal[
86
86
  "S", "M", "H", "D", "midnight", "W0", "W1", "W2", "W3", "W4", "W5", "W6"
87
87
  ]
88
+ _BACKUP_COUNT: int = 100
89
+ _MAX_BYTES: int = 10 * 1024**2
90
+ _WHEN: _When = "D"
88
91
 
89
92
 
90
93
  class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
@@ -100,10 +103,10 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
100
103
  encoding: str | None = None,
101
104
  delay: bool = False,
102
105
  errors: Literal["strict", "ignore", "replace"] | None = None,
103
- maxBytes: int = 0,
104
- when: _When = "midnight",
106
+ maxBytes: int = _MAX_BYTES,
107
+ when: _When = _WHEN,
105
108
  interval: int = 1,
106
- backupCount: int = 0,
109
+ backupCount: int = _BACKUP_COUNT,
107
110
  utc: bool = False,
108
111
  atTime: dt.time | None = None,
109
112
  ) -> None:
@@ -554,10 +557,10 @@ def setup_logging(
554
557
  console_filters: Iterable[_FilterType] | None = None,
555
558
  console_fmt: str = "❯ {_zoned_datetime_str} | {name}:{funcName}:{lineno} | {message}", # noqa: RUF001
556
559
  files_dir: MaybeCallablePathLike | None = get_default_logging_path,
557
- files_when: _When = "D",
560
+ files_when: _When = _WHEN,
558
561
  files_interval: int = 1,
559
- files_backup_count: int = 10,
560
- files_max_bytes: int = 10 * 1024**2,
562
+ files_backup_count: int = _BACKUP_COUNT,
563
+ files_max_bytes: int = _MAX_BYTES,
561
564
  files_filters: Iterable[_FilterType] | None = None,
562
565
  files_fmt: str = "{_zoned_datetime_str} | {name}:{funcName}:{lineno} | {levelname:8} | {message}",
563
566
  filters: MaybeIterable[_FilterType] | None = None,
@@ -5,6 +5,7 @@ from contextlib import contextmanager, suppress
5
5
  from dataclasses import dataclass
6
6
  from itertools import chain
7
7
  from os import chdir
8
+ from os.path import expandvars
8
9
  from pathlib import Path
9
10
  from re import IGNORECASE, search
10
11
  from subprocess import PIPE, CalledProcessError, check_output
@@ -34,6 +35,16 @@ def ensure_suffix(path: PathLike, suffix: str, /) -> Path:
34
35
  ##
35
36
 
36
37
 
38
+ def expand_path(path: PathLike, /) -> Path:
39
+ """Expand a path."""
40
+ path = str(path)
41
+ path = expandvars(path)
42
+ return Path(path).expanduser()
43
+
44
+
45
+ ##
46
+
47
+
37
48
  @overload
38
49
  def get_path(*, path: MaybeCallablePathLike | None) -> Path: ...
39
50
  @overload
@@ -111,4 +122,4 @@ def temp_cwd(path: PathLike, /) -> Iterator[None]:
111
122
  chdir(prev)
112
123
 
113
124
 
114
- __all__ = ["PWD", "ensure_suffix", "get_path", "list_dir", "temp_cwd"]
125
+ __all__ = ["PWD", "ensure_suffix", "expand_path", "get_path", "list_dir", "temp_cwd"]
@@ -19,7 +19,7 @@ def _ppf_1d(array: NDArrayF, cutoff: float, /) -> NDArrayF:
19
19
  out = full_like(array, nan, dtype=float)
20
20
  out[j] = _ppf_1d(array[j], cutoff)
21
21
  return out
22
- low, high = min(array), max(array)
22
+ low, high = array.min(), array.max()
23
23
  if is_zero(span := high - low):
24
24
  return zeros_like(array, dtype=float)
25
25
  centred = (array - low) / span