dycw-utilities 0.138.4__tar.gz → 0.138.6__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.138.4 → dycw_utilities-0.138.6}/PKG-INFO +1 -1
  2. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/pyproject.toml +2 -2
  3. dycw_utilities-0.138.6/src/tests/test_pathlib.py +263 -0
  4. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/__init__.py +1 -1
  5. dycw_utilities-0.138.6/src/utilities/pathlib.py +252 -0
  6. dycw_utilities-0.138.4/src/tests/test_pathlib.py +0 -145
  7. dycw_utilities-0.138.4/src/utilities/pathlib.py +0 -126
  8. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/.gitignore +0 -0
  9. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/LICENSE +0 -0
  10. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/README.md +0 -0
  11. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/__init__.py +0 -0
  12. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/conftest.py +0 -0
  13. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/__init__.py +0 -0
  14. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_missing/__init__.py +0 -0
  15. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_missing/module.py +0 -0
  16. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/__init__.py +0 -0
  17. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/outer_1.py +0 -0
  18. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/outer_2.py +0 -0
  19. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  20. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  21. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  22. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  23. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_without/__init__.py +0 -0
  24. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_without/module_1.py +0 -0
  25. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/package_without/module_2.py +0 -0
  26. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/standalone.py +0 -0
  27. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/modules/with_imports.py +0 -0
  28. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  29. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  30. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  31. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  32. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  33. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  34. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  35. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  36. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_aiolimiter.py +0 -0
  37. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_altair.py +0 -0
  38. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_asyncio.py +0 -0
  39. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_asyncio_classes/__init__.py +0 -0
  40. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_asyncio_classes/loopers.py +0 -0
  41. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_asyncio_classes/redis.py +0 -0
  42. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_atomicwrites.py +0 -0
  43. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_atools.py +0 -0
  44. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_cachetools.py +0 -0
  45. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_click.py +0 -0
  46. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_concurrent.py +0 -0
  47. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_contextlib.py +0 -0
  48. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_contextvars.py +0 -0
  49. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_cryptography.py +0 -0
  50. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_cvxpy.py +0 -0
  51. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_dataclasses.py +0 -0
  52. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_enum.py +0 -0
  53. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_errors.py +0 -0
  54. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_eventkit.py +0 -0
  55. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_fastapi.py +0 -0
  56. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_fpdf2.py +0 -0
  57. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_functions.py +0 -0
  58. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_functools.py +0 -0
  59. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_getpass.py +0 -0
  60. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_hashlib.py +0 -0
  61. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_http.py +0 -0
  62. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_hypothesis.py +0 -0
  63. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_importlib.py +0 -0
  64. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_inflect.py +0 -0
  65. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_ipython.py +0 -0
  66. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_iterables.py +0 -0
  67. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_jupyter.py +0 -0
  68. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_libcst.py +0 -0
  69. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_lightweight_charts.py +0 -0
  70. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_logging.py +0 -0
  71. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_luigi.py +0 -0
  72. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_math.py +0 -0
  73. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_memory_profiler.py +0 -0
  74. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_modules.py +0 -0
  75. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_more_itertools.py +0 -0
  76. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_numpy.py +0 -0
  77. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_objects/__init__.py +0 -0
  78. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_objects/objects.py +0 -0
  79. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_operator.py +0 -0
  80. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_optuna.py +0 -0
  81. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_orjson.py +0 -0
  82. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_os.py +0 -0
  83. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_parse.py +0 -0
  84. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_period.py +0 -0
  85. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pickle.py +0 -0
  86. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_platform.py +0 -0
  87. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_polars.py +0 -0
  88. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_polars_ols.py +0 -0
  89. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pottery.py +0 -0
  90. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pqdm.py +0 -0
  91. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_psutil.py +0 -0
  92. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pydantic.py +0 -0
  93. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pyinstrument.py +0 -0
  94. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pytest.py +0 -0
  95. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_pytest_regressions.py +0 -0
  96. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_python_dotenv.py +0 -0
  97. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_random.py +0 -0
  98. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_re.py +0 -0
  99. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_redis.py +0 -0
  100. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_reprlib.py +0 -0
  101. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_scipy.py +0 -0
  102. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_sentinel.py +0 -0
  103. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_shelve.py +0 -0
  104. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_slack_sdk.py +0 -0
  105. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_socket.py +0 -0
  106. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_sqlalchemy.py +0 -0
  107. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_sqlalchemy_polars.py +0 -0
  108. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_statsmodels.py +0 -0
  109. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_streamlit.py +0 -0
  110. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_string.py +0 -0
  111. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_tempfile.py +0 -0
  112. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_text.py +0 -0
  113. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_threading.py +0 -0
  114. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_timer.py +0 -0
  115. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_traceback.py +0 -0
  116. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_typed_settings.py +0 -0
  117. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_types.py +0 -0
  118. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_typing.py +0 -0
  119. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_typing_funcs/__init__.py +0 -0
  120. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_typing_funcs/no_future.py +0 -0
  121. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_typing_funcs/with_future.py +0 -0
  122. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_tzdata.py +0 -0
  123. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_tzlocal.py +0 -0
  124. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_uuid.py +0 -0
  125. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_version.py +0 -0
  126. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_warnings.py +0 -0
  127. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_whenever.py +0 -0
  128. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_zipfile.py +0 -0
  129. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/tests/test_zoneinfo.py +0 -0
  130. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/aiolimiter.py +0 -0
  131. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/altair.py +0 -0
  132. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/asyncio.py +0 -0
  133. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/atomicwrites.py +0 -0
  134. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/atools.py +0 -0
  135. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/cachetools.py +0 -0
  136. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/click.py +0 -0
  137. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/concurrent.py +0 -0
  138. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/contextlib.py +0 -0
  139. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/contextvars.py +0 -0
  140. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/cryptography.py +0 -0
  141. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/cvxpy.py +0 -0
  142. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/dataclasses.py +0 -0
  143. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/enum.py +0 -0
  144. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/errors.py +0 -0
  145. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/eventkit.py +0 -0
  146. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/fastapi.py +0 -0
  147. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/fpdf2.py +0 -0
  148. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/functions.py +0 -0
  149. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/functools.py +0 -0
  150. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/getpass.py +0 -0
  151. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/hashlib.py +0 -0
  152. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/http.py +0 -0
  153. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/hypothesis.py +0 -0
  154. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/importlib.py +0 -0
  155. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/inflect.py +0 -0
  156. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/ipython.py +0 -0
  157. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/iterables.py +0 -0
  158. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/jupyter.py +0 -0
  159. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/libcst.py +0 -0
  160. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/lightweight_charts.py +0 -0
  161. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/logging.py +0 -0
  162. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/luigi.py +0 -0
  163. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/math.py +0 -0
  164. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/memory_profiler.py +0 -0
  165. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/modules.py +0 -0
  166. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/more_itertools.py +0 -0
  167. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/numpy.py +0 -0
  168. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/operator.py +0 -0
  169. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/optuna.py +0 -0
  170. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/orjson.py +0 -0
  171. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/os.py +0 -0
  172. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/parse.py +0 -0
  173. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/period.py +0 -0
  174. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pickle.py +0 -0
  175. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/platform.py +0 -0
  176. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/polars.py +0 -0
  177. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/polars_ols.py +0 -0
  178. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pottery.py +0 -0
  179. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pqdm.py +0 -0
  180. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/psutil.py +0 -0
  181. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/py.typed +0 -0
  182. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pydantic.py +0 -0
  183. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pyinstrument.py +0 -0
  184. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pytest.py +0 -0
  185. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/pytest_regressions.py +0 -0
  186. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/python_dotenv.py +0 -0
  187. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/random.py +0 -0
  188. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/re.py +0 -0
  189. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/redis.py +0 -0
  190. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/reprlib.py +0 -0
  191. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/scipy.py +0 -0
  192. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/sentinel.py +0 -0
  193. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/shelve.py +0 -0
  194. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/slack_sdk.py +0 -0
  195. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/socket.py +0 -0
  196. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/sqlalchemy.py +0 -0
  197. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/sqlalchemy_polars.py +0 -0
  198. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/statsmodels.py +0 -0
  199. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/streamlit.py +0 -0
  200. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/string.py +0 -0
  201. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/tempfile.py +0 -0
  202. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/text.py +0 -0
  203. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/threading.py +0 -0
  204. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/timer.py +0 -0
  205. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/traceback.py +0 -0
  206. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/typed_settings.py +0 -0
  207. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/types.py +0 -0
  208. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/typing.py +0 -0
  209. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/tzdata.py +0 -0
  210. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/tzlocal.py +0 -0
  211. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/uuid.py +0 -0
  212. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/version.py +0 -0
  213. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/warnings.py +0 -0
  214. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/whenever.py +0 -0
  215. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/zipfile.py +0 -0
  216. {dycw_utilities-0.138.4 → dycw_utilities-0.138.6}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.138.4
3
+ Version: 0.138.6
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -105,7 +105,7 @@ dependencies = [
105
105
  name = "dycw-utilities"
106
106
  readme = "README.md"
107
107
  requires-python = ">= 3.12"
108
- version = "0.138.4"
108
+ version = "0.138.6"
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.138.4"
136
+ current_version = "0.138.6"
137
137
 
138
138
  [[tool.bumpversion.files]]
139
139
  filename = "src/utilities/__init__.py"
@@ -0,0 +1,263 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from pathlib import Path
5
+ from shutil import copytree
6
+ from typing import TYPE_CHECKING, Self
7
+
8
+ from hypothesis import HealthCheck, given, settings
9
+ from hypothesis.strategies import DataObject, data, integers, sets
10
+ from pytest import mark, param, raises
11
+
12
+ from tests.conftest import SKIPIF_CI_AND_WINDOWS
13
+ from utilities.dataclasses import replace_non_sentinel
14
+ from utilities.hypothesis import git_repos, paths, temp_paths
15
+ from utilities.pathlib import (
16
+ GetRootError,
17
+ _GetTailDisambiguate,
18
+ _GetTailEmptyError,
19
+ _GetTailLengthError,
20
+ _GetTailNonUniqueError,
21
+ ensure_suffix,
22
+ expand_path,
23
+ get_path,
24
+ get_root,
25
+ get_tail,
26
+ is_sub_path,
27
+ list_dir,
28
+ temp_cwd,
29
+ )
30
+ from utilities.sentinel import Sentinel, sentinel
31
+ from utilities.tempfile import TemporaryDirectory
32
+
33
+ if TYPE_CHECKING:
34
+ from utilities.types import MaybeCallablePathLike, PathLike
35
+
36
+
37
+ class TestEnsureSuffix:
38
+ @mark.parametrize(
39
+ ("path", "suffix", "expected"),
40
+ [
41
+ param("foo", ".txt", "foo.txt"),
42
+ param("foo.txt", ".txt", "foo.txt"),
43
+ param("foo.bar.baz", ".baz", "foo.bar.baz"),
44
+ param("foo.bar.baz", ".quux", "foo.bar.baz.quux"),
45
+ ],
46
+ ids=str,
47
+ )
48
+ def test_main(self, *, path: Path, suffix: str, expected: str) -> None:
49
+ result = str(ensure_suffix(path, suffix))
50
+ assert result == expected
51
+
52
+
53
+ class TestExpandPath:
54
+ @mark.parametrize(
55
+ ("path", "expected"),
56
+ [
57
+ param("foo", Path("foo")),
58
+ param("~", Path.home()),
59
+ param("~/foo", Path.home().joinpath("foo")),
60
+ param("$HOME", Path.home(), marks=SKIPIF_CI_AND_WINDOWS),
61
+ param(
62
+ "$HOME/foo", Path.home().joinpath("foo"), marks=SKIPIF_CI_AND_WINDOWS
63
+ ),
64
+ ],
65
+ ids=str,
66
+ )
67
+ def test_main(self, *, path: Path, expected: Path) -> None:
68
+ result = expand_path(path)
69
+ assert result == expected
70
+
71
+
72
+ class TestGetPath:
73
+ @given(path=paths())
74
+ def test_path(self, *, path: Path) -> None:
75
+ assert get_path(path=path) == path
76
+
77
+ @given(path=paths())
78
+ def test_str(self, *, path: Path) -> None:
79
+ assert get_path(path=str(path)) == path
80
+
81
+ def test_none(self) -> None:
82
+ assert get_path(path=None) == Path.cwd()
83
+
84
+ def test_sentinel(self) -> None:
85
+ assert get_path(path=sentinel) is sentinel
86
+
87
+ @given(path1=paths(), path2=paths())
88
+ def test_replace_non_sentinel(self, *, path1: Path, path2: Path) -> None:
89
+ @dataclass(kw_only=True, slots=True)
90
+ class Example:
91
+ path: Path = field(default_factory=Path.cwd)
92
+
93
+ def replace(
94
+ self, *, path: MaybeCallablePathLike | Sentinel = sentinel
95
+ ) -> Self:
96
+ return replace_non_sentinel(self, path=get_path(path=path))
97
+
98
+ obj = Example(path=path1)
99
+ assert obj.path == path1
100
+ assert obj.replace().path == path1
101
+ assert obj.replace(path=path2).path == path2
102
+
103
+ @given(path=paths())
104
+ def test_callable(self, *, path: Path) -> None:
105
+ assert get_path(path=lambda: path) == path
106
+
107
+
108
+ class TestGetRoot:
109
+ @given(data=data(), repo=git_repos())
110
+ @settings(
111
+ max_examples=1, suppress_health_check={HealthCheck.function_scoped_fixture}
112
+ )
113
+ def test_git_dir(self, *, data: DataObject, repo: Path) -> None:
114
+ path = repo.joinpath(data.draw(paths()))
115
+ path.mkdir(parents=True, exist_ok=True)
116
+ root = get_root(path=path)
117
+ expected = repo.resolve()
118
+ assert root == expected
119
+
120
+ @given(data=data(), repo=git_repos())
121
+ @settings(
122
+ max_examples=1, suppress_health_check={HealthCheck.function_scoped_fixture}
123
+ )
124
+ def test_git_file(self, *, data: DataObject, repo: Path) -> None:
125
+ path = repo.joinpath(data.draw(paths(min_depth=1)))
126
+ path.touch()
127
+ root = get_root(path=path)
128
+ expected = repo.resolve()
129
+ assert root == expected
130
+
131
+ @given(data=data())
132
+ @settings(
133
+ max_examples=1, suppress_health_check={HealthCheck.function_scoped_fixture}
134
+ )
135
+ def test_envrc(self, *, data: DataObject, tmp_path: Path) -> None:
136
+ tmp_path.joinpath(".envrc").touch()
137
+ path = tmp_path.joinpath(data.draw(paths()))
138
+ result = get_root(path=path)
139
+ expected = tmp_path.resolve()
140
+ assert result == expected
141
+
142
+ @given(data=data(), repo=git_repos())
143
+ @settings(
144
+ max_examples=1, suppress_health_check={HealthCheck.function_scoped_fixture}
145
+ )
146
+ def test_git_and_envrc(self, *, data: DataObject, repo: Path) -> None:
147
+ repo.joinpath(".envrc").touch()
148
+ path = repo.joinpath(data.draw(paths()))
149
+ root = get_root(path=path)
150
+ expected = repo.resolve()
151
+ assert root == expected
152
+
153
+ @given(data=data(), repo=git_repos())
154
+ @settings(
155
+ max_examples=1, suppress_health_check={HealthCheck.function_scoped_fixture}
156
+ )
157
+ def test_git_with_envrc_inside(self, *, data: DataObject, repo: Path) -> None:
158
+ path = repo.joinpath(data.draw(paths(min_depth=1)))
159
+ path.mkdir(parents=True)
160
+ path.joinpath(".envrc").touch()
161
+ root = get_root(path=path)
162
+ expected = path.resolve()
163
+ assert root == expected
164
+
165
+ @given(data=data(), repo=git_repos())
166
+ @settings(
167
+ max_examples=1, suppress_health_check={HealthCheck.function_scoped_fixture}
168
+ )
169
+ def test_envrc_with_git_inside(self, *, data: DataObject, repo: Path) -> None:
170
+ with TemporaryDirectory() as temp:
171
+ temp.joinpath(".envrc").touch()
172
+ path = temp.joinpath(data.draw(paths(min_depth=1)))
173
+ _ = copytree(repo, path)
174
+ root = get_root(path=path)
175
+ expected = path.resolve()
176
+ assert root == expected
177
+
178
+ def test_error(self, *, tmp_path: Path) -> None:
179
+ with raises(GetRootError, match="Unable to determine root from '.*'"):
180
+ _ = get_root(path=tmp_path)
181
+
182
+
183
+ class TestGetTail:
184
+ @mark.parametrize(
185
+ ("path", "root", "disambiguate", "expected"),
186
+ [
187
+ param("foo/bar/baz", "foo", "raise", Path("bar/baz")),
188
+ param("foo/bar/baz", "foo/bar", "raise", Path("baz")),
189
+ param("a/b/c/d/a/b/c/d/e", "b/c", "earlier", Path("d/a/b/c/d/e")),
190
+ param("a/b/c/d/a/b/c/d/e", "b/c", "later", Path("d/e")),
191
+ ],
192
+ )
193
+ def test_main(
194
+ self,
195
+ *,
196
+ path: PathLike,
197
+ root: PathLike,
198
+ disambiguate: _GetTailDisambiguate,
199
+ expected: Path,
200
+ ) -> None:
201
+ tail = get_tail(path, root, disambiguate=disambiguate)
202
+ assert tail == expected
203
+
204
+ def test_error_length(self) -> None:
205
+ with raises(
206
+ _GetTailLengthError,
207
+ match="Unable to get the tail of 'foo' with root of length 2",
208
+ ):
209
+ _ = get_tail("foo", "bar/baz")
210
+
211
+ def test_error_empty(self) -> None:
212
+ with raises(
213
+ _GetTailEmptyError,
214
+ match="Unable to get the tail of 'foo/bar' with root 'baz'",
215
+ ):
216
+ _ = get_tail("foo/bar", "baz")
217
+
218
+ def test_error_non_unique(self) -> None:
219
+ with raises(
220
+ _GetTailNonUniqueError,
221
+ match="Path '.*' must contain exactly one tail with root 'b'; got '.*', '.*' and perhaps more",
222
+ ):
223
+ _ = get_tail("a/b/c/a/b/c", "b")
224
+
225
+
226
+ class TestIsSubPath:
227
+ @mark.parametrize(
228
+ ("x", "y", "strict", "expected"),
229
+ [
230
+ param("foo", "foo", False, True),
231
+ param("foo", "foo", True, False),
232
+ param("foo/bar", "foo", False, True),
233
+ param("foo/bar", "foo", True, True),
234
+ param("foo/bar", "foo/baz", False, False),
235
+ param("foo/bar", "foo/baz", True, False),
236
+ param("foo", "foo/bar", False, False),
237
+ param("foo", "foo/bar", True, False),
238
+ ],
239
+ )
240
+ def test_main(
241
+ self, *, x: PathLike, y: PathLike, strict: bool, expected: bool
242
+ ) -> None:
243
+ result = is_sub_path(x, y, strict=strict)
244
+ assert result is expected
245
+
246
+
247
+ class TestListDir:
248
+ @given(root=temp_paths(), nums=sets(integers(0, 100), max_size=10))
249
+ def test_main(self, *, root: Path, nums: set[str]) -> None:
250
+ for n in nums:
251
+ path = root.joinpath(f"{n}.txt")
252
+ path.touch()
253
+ result = list_dir(root)
254
+ expected = sorted(Path(root, f"{n}.txt") for n in nums)
255
+ assert result == expected
256
+
257
+
258
+ class TestTempCWD:
259
+ def test_main(self, *, tmp_path: Path) -> None:
260
+ assert Path.cwd() != tmp_path
261
+ with temp_cwd(tmp_path):
262
+ assert Path.cwd() == tmp_path
263
+ assert Path.cwd() != tmp_path
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.138.4"
3
+ __version__ = "0.138.6"
@@ -0,0 +1,252 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from contextlib import contextmanager
5
+ from dataclasses import dataclass
6
+ from itertools import chain
7
+ from os import chdir
8
+ from os.path import expandvars
9
+ from pathlib import Path
10
+ from re import IGNORECASE, search
11
+ from subprocess import PIPE, CalledProcessError, check_output
12
+ from typing import TYPE_CHECKING, Literal, assert_never, overload, override
13
+
14
+ from utilities.errors import ImpossibleCaseError
15
+ from utilities.sentinel import Sentinel, sentinel
16
+
17
+ if TYPE_CHECKING:
18
+ from collections.abc import Iterator, Sequence
19
+
20
+ from utilities.types import MaybeCallablePathLike, PathLike
21
+
22
+
23
+ PWD = Path.cwd()
24
+
25
+
26
+ def ensure_suffix(path: PathLike, suffix: str, /) -> Path:
27
+ """Ensure a path has a given suffix."""
28
+ path = Path(path)
29
+ parts = path.name.split(".")
30
+ parts = list(chain([parts[0]], (f".{p}" for p in parts[1:])))
31
+ if (len(parts) == 0) or (parts[-1] != suffix):
32
+ parts.append(suffix)
33
+ name = "".join(parts)
34
+ return path.with_name(name)
35
+
36
+
37
+ ##
38
+
39
+
40
+ def expand_path(path: PathLike, /) -> Path:
41
+ """Expand a path."""
42
+ path = str(path)
43
+ path = expandvars(path)
44
+ return Path(path).expanduser()
45
+
46
+
47
+ ##
48
+
49
+
50
+ @overload
51
+ def get_path(*, path: MaybeCallablePathLike | None) -> Path: ...
52
+ @overload
53
+ def get_path(*, path: Sentinel) -> Sentinel: ...
54
+ def get_path(
55
+ *, path: MaybeCallablePathLike | None | Sentinel = sentinel
56
+ ) -> Path | None | Sentinel:
57
+ """Get the path."""
58
+ match path:
59
+ case Path() | Sentinel():
60
+ return path
61
+ case str():
62
+ return Path(path)
63
+ case None:
64
+ return Path.cwd()
65
+ case Callable() as func:
66
+ return get_path(path=func())
67
+ case _ as never:
68
+ assert_never(never)
69
+
70
+
71
+ ##
72
+
73
+
74
+ def get_root(*, path: MaybeCallablePathLike | None = None) -> Path:
75
+ """Get the root of a path."""
76
+ path = get_path(path=path)
77
+ path_dir = path.parent if path.is_file() else path
78
+ try:
79
+ output = check_output(
80
+ ["git", "rev-parse", "--show-toplevel"],
81
+ stderr=PIPE,
82
+ cwd=path_dir,
83
+ text=True,
84
+ )
85
+ except CalledProcessError as error:
86
+ # newer versions of git report "Not a git repository", whilst older
87
+ # versions report "not a git repository"
88
+ if not search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
89
+ raise # pragma: no cover
90
+ root_git = None
91
+ else:
92
+ root_git = Path(output.strip("\n")).resolve()
93
+ all_paths = list(chain([path_dir], path_dir.parents))
94
+ try:
95
+ root_envrc = next(
96
+ p.resolve()
97
+ for p in all_paths
98
+ if any(p_i.name == ".envrc" for p_i in p.iterdir())
99
+ )
100
+ except StopIteration:
101
+ root_envrc = None
102
+ match root_git, root_envrc:
103
+ case None, None:
104
+ raise GetRootError(path=path)
105
+ case Path(), None:
106
+ return root_git
107
+ case None, Path():
108
+ return root_envrc
109
+ case Path(), Path():
110
+ if root_git == root_envrc:
111
+ return root_git
112
+ if is_sub_path(root_git, root_envrc, strict=True):
113
+ return root_git
114
+ if is_sub_path(root_envrc, root_git, strict=True):
115
+ return root_envrc
116
+ raise ImpossibleCaseError( # pragma: no cover
117
+ case=[f"{root_git=}", f"{root_envrc=}"]
118
+ )
119
+ case _ as never:
120
+ assert_never(never)
121
+
122
+
123
+ @dataclass(kw_only=True, slots=True)
124
+ class GetRootError(Exception):
125
+ path: PathLike
126
+
127
+ @override
128
+ def __str__(self) -> str:
129
+ return f"Unable to determine root from {str(self.path)!r}"
130
+
131
+
132
+ ##
133
+
134
+
135
+ type _GetTailDisambiguate = Literal["raise", "earlier", "later"]
136
+
137
+
138
+ def get_tail(
139
+ path: PathLike, root: PathLike, /, *, disambiguate: _GetTailDisambiguate = "raise"
140
+ ) -> Path:
141
+ """Get the tail of a path following a root match."""
142
+ path_parts, root_parts = [Path(p).parts for p in [path, root]]
143
+ len_path, len_root = map(len, [path_parts, root_parts])
144
+ if len_root > len_path:
145
+ raise _GetTailLengthError(path=path, root=root, len_root=len_root)
146
+ candidates = {
147
+ i + len_root: path_parts[i : i + len_root]
148
+ for i in range(len_path + 1 - len_root)
149
+ }
150
+ matches = {k: v for k, v in candidates.items() if v == root_parts}
151
+ match len(matches), disambiguate:
152
+ case 0, _:
153
+ raise _GetTailEmptyError(path=path, root=root)
154
+ case 1, _:
155
+ return _get_tail_core(path, next(iter(matches)))
156
+ case _, "raise":
157
+ first, second, *_ = matches
158
+ raise _GetTailNonUniqueError(
159
+ path=path,
160
+ root=root,
161
+ first=_get_tail_core(path, first),
162
+ second=_get_tail_core(path, second),
163
+ )
164
+ case _, "earlier":
165
+ return _get_tail_core(path, next(iter(matches)))
166
+ case _, "later":
167
+ return _get_tail_core(path, next(iter(reversed(matches))))
168
+ case _ as never:
169
+ assert_never(never)
170
+
171
+
172
+ def _get_tail_core(path: PathLike, i: int, /) -> Path:
173
+ parts = Path(path).parts
174
+ return Path(*parts[i:])
175
+
176
+
177
+ @dataclass(kw_only=True, slots=True)
178
+ class GetTailError(Exception):
179
+ path: PathLike
180
+ root: PathLike
181
+
182
+
183
+ @dataclass(kw_only=True, slots=True)
184
+ class _GetTailLengthError(GetTailError):
185
+ len_root: int
186
+
187
+ @override
188
+ def __str__(self) -> str:
189
+ return f"Unable to get the tail of {str(self.path)!r} with root of length {self.len_root}"
190
+
191
+
192
+ @dataclass(kw_only=True, slots=True)
193
+ class _GetTailEmptyError(GetTailError):
194
+ @override
195
+ def __str__(self) -> str:
196
+ return (
197
+ f"Unable to get the tail of {str(self.path)!r} with root {str(self.root)!r}"
198
+ )
199
+
200
+
201
+ @dataclass(kw_only=True, slots=True)
202
+ class _GetTailNonUniqueError(GetTailError):
203
+ first: Path
204
+ second: Path
205
+
206
+ @override
207
+ def __str__(self) -> str:
208
+ return f"Path {str(self.path)!r} must contain exactly one tail with root {str(self.root)!r}; got {str(self.first)!r}, {str(self.second)!r} and perhaps more"
209
+
210
+
211
+ ##
212
+
213
+
214
+ def is_sub_path(x: PathLike, y: PathLike, /, *, strict: bool = False) -> bool:
215
+ """Check if a path is a sub path of another."""
216
+ x, y = [Path(i).resolve() for i in [x, y]]
217
+ return x.is_relative_to(y) and not (strict and y.is_relative_to(x))
218
+
219
+
220
+ ##
221
+
222
+
223
+ def list_dir(path: PathLike, /) -> Sequence[Path]:
224
+ """List the contents of a directory."""
225
+ return sorted(Path(path).iterdir())
226
+
227
+
228
+ ##
229
+
230
+
231
+ @contextmanager
232
+ def temp_cwd(path: PathLike, /) -> Iterator[None]:
233
+ """Context manager with temporary current working directory set."""
234
+ prev = Path.cwd()
235
+ chdir(path)
236
+ try:
237
+ yield
238
+ finally:
239
+ chdir(prev)
240
+
241
+
242
+ __all__ = [
243
+ "PWD",
244
+ "GetTailError",
245
+ "ensure_suffix",
246
+ "expand_path",
247
+ "get_path",
248
+ "get_tail",
249
+ "is_sub_path",
250
+ "list_dir",
251
+ "temp_cwd",
252
+ ]
@@ -1,145 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass, field
4
- from pathlib import Path
5
- from typing import TYPE_CHECKING, Self
6
-
7
- from hypothesis import given, settings
8
- from hypothesis.strategies import integers, sets
9
- from pytest import mark, param, raises
10
-
11
- from tests.conftest import SKIPIF_CI_AND_WINDOWS
12
- from utilities.dataclasses import replace_non_sentinel
13
- from utilities.hypothesis import git_repos, paths, temp_paths
14
- from utilities.pathlib import (
15
- GetRootError,
16
- ensure_suffix,
17
- expand_path,
18
- get_path,
19
- get_root,
20
- list_dir,
21
- temp_cwd,
22
- )
23
- from utilities.sentinel import Sentinel, sentinel
24
-
25
- if TYPE_CHECKING:
26
- from utilities.types import MaybeCallablePathLike
27
-
28
-
29
- class TestEnsureSuffix:
30
- @mark.parametrize(
31
- ("path", "suffix", "expected"),
32
- [
33
- param("foo", ".txt", "foo.txt"),
34
- param("foo.txt", ".txt", "foo.txt"),
35
- param("foo.bar.baz", ".baz", "foo.bar.baz"),
36
- param("foo.bar.baz", ".quux", "foo.bar.baz.quux"),
37
- ],
38
- ids=str,
39
- )
40
- def test_main(self, *, path: Path, suffix: str, expected: str) -> None:
41
- result = str(ensure_suffix(path, suffix))
42
- assert result == expected
43
-
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
-
64
- class TestGetPath:
65
- @given(path=paths())
66
- def test_path(self, *, path: Path) -> None:
67
- assert get_path(path=path) == path
68
-
69
- @given(path=paths())
70
- def test_str(self, *, path: Path) -> None:
71
- assert get_path(path=str(path)) == path
72
-
73
- def test_none(self) -> None:
74
- assert get_path(path=None) == Path.cwd()
75
-
76
- def test_sentinel(self) -> None:
77
- assert get_path(path=sentinel) is sentinel
78
-
79
- @given(path1=paths(), path2=paths())
80
- def test_replace_non_sentinel(self, *, path1: Path, path2: Path) -> None:
81
- @dataclass(kw_only=True, slots=True)
82
- class Example:
83
- path: Path = field(default_factory=Path.cwd)
84
-
85
- def replace(
86
- self, *, path: MaybeCallablePathLike | Sentinel = sentinel
87
- ) -> Self:
88
- return replace_non_sentinel(self, path=get_path(path=path))
89
-
90
- obj = Example(path=path1)
91
- assert obj.path == path1
92
- assert obj.replace().path == path1
93
- assert obj.replace(path=path2).path == path2
94
-
95
- @given(path=paths())
96
- def test_callable(self, *, path: Path) -> None:
97
- assert get_path(path=lambda: path) == path
98
-
99
-
100
- class TestGetRoot:
101
- @given(repo=git_repos())
102
- @settings(max_examples=1)
103
- def test_git(self, *, repo: Path) -> None:
104
- root = get_root(path=repo)
105
- expected = repo.resolve()
106
- assert root == expected
107
-
108
- @given(root=temp_paths())
109
- @settings(max_examples=1)
110
- def test_envrc(self, *, root: Path) -> None:
111
- root.joinpath(".envrc").touch()
112
- result = get_root(path=root)
113
- assert result == root
114
-
115
- @given(root=temp_paths())
116
- @settings(max_examples=1)
117
- def test_envrc_from_inside(self, *, root: Path) -> None:
118
- root.joinpath(".envrc").touch()
119
- path = root.joinpath("foo", "bar", "baz")
120
- path.mkdir(parents=True)
121
- result = get_root(path=path)
122
- assert result == root
123
-
124
- def test_error(self, *, tmp_path: Path) -> None:
125
- with raises(GetRootError, match="Unable to determine root from '.*'"):
126
- _ = get_root(path=tmp_path)
127
-
128
-
129
- class TestListDir:
130
- @given(root=temp_paths(), nums=sets(integers(0, 100), max_size=10))
131
- def test_main(self, *, root: Path, nums: set[str]) -> None:
132
- for n in nums:
133
- path = root.joinpath(f"{n}.txt")
134
- path.touch()
135
- result = list_dir(root)
136
- expected = sorted(Path(root, f"{n}.txt") for n in nums)
137
- assert result == expected
138
-
139
-
140
- class TestTempCWD:
141
- def test_main(self, *, tmp_path: Path) -> None:
142
- assert Path.cwd() != tmp_path
143
- with temp_cwd(tmp_path):
144
- assert Path.cwd() == tmp_path
145
- assert Path.cwd() != tmp_path