dycw-utilities 0.155.3__tar.gz → 0.156.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 (212) hide show
  1. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/PKG-INFO +1 -1
  2. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/pyproject.toml +3 -3
  3. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_click.py +14 -87
  4. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_hypothesis.py +3 -2
  5. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_sentinel.py +8 -1
  6. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/__init__.py +1 -1
  7. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/click.py +29 -15
  8. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/dataclasses.py +7 -16
  9. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/functions.py +3 -3
  10. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/hypothesis.py +3 -3
  11. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/iterables.py +2 -2
  12. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/more_itertools.py +2 -4
  13. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pqdm.py +2 -2
  14. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/sentinel.py +10 -0
  15. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/.gitignore +0 -0
  16. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/LICENSE +0 -0
  17. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/README.md +0 -0
  18. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/__init__.py +0 -0
  19. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/conftest.py +0 -0
  20. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/__init__.py +0 -0
  21. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_missing/__init__.py +0 -0
  22. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_missing/module.py +0 -0
  23. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/__init__.py +0 -0
  24. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/outer_1.py +0 -0
  25. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/outer_2.py +0 -0
  26. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  27. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  28. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  29. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  30. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_without/__init__.py +0 -0
  31. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_without/module_1.py +0 -0
  32. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/package_without/module_2.py +0 -0
  33. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/standalone.py +0 -0
  34. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/modules/with_imports.py +0 -0
  35. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  36. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  37. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  38. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  39. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  40. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  41. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  42. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  43. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_altair.py +0 -0
  44. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_asyncio.py +0 -0
  45. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_atomicwrites.py +0 -0
  46. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_atools.py +0 -0
  47. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_cachetools.py +0 -0
  48. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_concurrent.py +0 -0
  49. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_contextlib.py +0 -0
  50. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_contextvars.py +0 -0
  51. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_cryptography.py +0 -0
  52. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_cvxpy.py +0 -0
  53. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_dataclasses.py +0 -0
  54. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_enum.py +0 -0
  55. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_errors.py +0 -0
  56. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_eventkit.py +0 -0
  57. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_fastapi.py +0 -0
  58. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_fpdf2.py +0 -0
  59. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_functions.py +0 -0
  60. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_functools.py +0 -0
  61. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_getpass.py +0 -0
  62. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_gzip.py +0 -0
  63. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_hashlib.py +0 -0
  64. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_http.py +0 -0
  65. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_importlib.py +0 -0
  66. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_inflect.py +0 -0
  67. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_ipython.py +0 -0
  68. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_iterables.py +0 -0
  69. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_json.py +0 -0
  70. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_jupyter.py +0 -0
  71. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_libcst.py +0 -0
  72. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_lightweight_charts.py +0 -0
  73. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_logging.py +0 -0
  74. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_math.py +0 -0
  75. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_memory_profiler.py +0 -0
  76. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_modules.py +0 -0
  77. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_more_itertools.py +0 -0
  78. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_numpy.py +0 -0
  79. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_objects/__init__.py +0 -0
  80. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_objects/objects.py +0 -0
  81. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_operator.py +0 -0
  82. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_optuna.py +0 -0
  83. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_orjson.py +0 -0
  84. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_os.py +0 -0
  85. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_parse.py +0 -0
  86. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pathlib.py +0 -0
  87. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pickle.py +0 -0
  88. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_platform.py +0 -0
  89. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_polars.py +0 -0
  90. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_polars_ols.py +0 -0
  91. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_postgres.py +0 -0
  92. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pottery.py +0 -0
  93. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pqdm.py +0 -0
  94. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_psutil.py +0 -0
  95. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pyinstrument.py +0 -0
  96. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pytest.py +0 -0
  97. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pytest_randomly.py +0 -0
  98. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_pytest_regressions.py +0 -0
  99. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_random.py +0 -0
  100. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_re.py +0 -0
  101. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_redis.py +0 -0
  102. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_reprlib.py +0 -0
  103. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_scipy.py +0 -0
  104. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_shelve.py +0 -0
  105. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_slack_sdk.py +0 -0
  106. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_socket.py +0 -0
  107. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_sqlalchemy.py +0 -0
  108. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_sqlalchemy_polars.py +0 -0
  109. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_statsmodels.py +0 -0
  110. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_string.py +0 -0
  111. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_tempfile.py +0 -0
  112. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_text.py +0 -0
  113. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_threading.py +0 -0
  114. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_timer.py +0 -0
  115. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_traceback.py +0 -0
  116. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_typed_settings.py +0 -0
  117. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_types.py +0 -0
  118. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_typing.py +0 -0
  119. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_typing_funcs/__init__.py +0 -0
  120. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_typing_funcs/no_future.py +0 -0
  121. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_typing_funcs/with_future.py +0 -0
  122. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_tzdata.py +0 -0
  123. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_tzlocal.py +0 -0
  124. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_uuid.py +0 -0
  125. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_version.py +0 -0
  126. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_warnings.py +0 -0
  127. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_whenever.py +0 -0
  128. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_yield_access/__init__.py +0 -0
  129. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_yield_access/script.py +0 -0
  130. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_yield_access/script.sh +0 -0
  131. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_zipfile.py +0 -0
  132. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/tests/test_zoneinfo.py +0 -0
  133. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/altair.py +0 -0
  134. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/asyncio.py +0 -0
  135. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/atomicwrites.py +0 -0
  136. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/atools.py +0 -0
  137. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/cachetools.py +0 -0
  138. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/concurrent.py +0 -0
  139. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/contextlib.py +0 -0
  140. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/contextvars.py +0 -0
  141. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/cryptography.py +0 -0
  142. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/cvxpy.py +0 -0
  143. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/enum.py +0 -0
  144. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/errors.py +0 -0
  145. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/eventkit.py +0 -0
  146. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/fastapi.py +0 -0
  147. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/fpdf2.py +0 -0
  148. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/functools.py +0 -0
  149. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/getpass.py +0 -0
  150. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/gzip.py +0 -0
  151. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/hashlib.py +0 -0
  152. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/http.py +0 -0
  153. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/importlib.py +0 -0
  154. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/inflect.py +0 -0
  155. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/ipython.py +0 -0
  156. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/json.py +0 -0
  157. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/jupyter.py +0 -0
  158. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/libcst.py +0 -0
  159. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/lightweight_charts.py +0 -0
  160. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/logging.py +0 -0
  161. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/math.py +0 -0
  162. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/memory_profiler.py +0 -0
  163. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/modules.py +0 -0
  164. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/numpy.py +0 -0
  165. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/operator.py +0 -0
  166. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/optuna.py +0 -0
  167. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/orjson.py +0 -0
  168. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/os.py +0 -0
  169. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/parse.py +0 -0
  170. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pathlib.py +0 -0
  171. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pickle.py +0 -0
  172. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/platform.py +0 -0
  173. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/polars.py +0 -0
  174. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/polars_ols.py +0 -0
  175. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/postgres.py +0 -0
  176. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pottery.py +0 -0
  177. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/psutil.py +0 -0
  178. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/py.typed +0 -0
  179. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pyinstrument.py +0 -0
  180. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pytest.py +0 -0
  181. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pytest_plugins/__init__.py +0 -0
  182. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
  183. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
  184. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/pytest_regressions.py +0 -0
  185. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/random.py +0 -0
  186. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/re.py +0 -0
  187. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/redis.py +0 -0
  188. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/reprlib.py +0 -0
  189. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/scipy.py +0 -0
  190. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/shelve.py +0 -0
  191. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/slack_sdk.py +0 -0
  192. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/socket.py +0 -0
  193. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/sqlalchemy.py +0 -0
  194. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/sqlalchemy_polars.py +0 -0
  195. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/statsmodels.py +0 -0
  196. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/string.py +0 -0
  197. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/tempfile.py +0 -0
  198. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/text.py +0 -0
  199. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/threading.py +0 -0
  200. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/timer.py +0 -0
  201. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/traceback.py +0 -0
  202. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/typed_settings.py +0 -0
  203. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/types.py +0 -0
  204. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/typing.py +0 -0
  205. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/tzdata.py +0 -0
  206. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/tzlocal.py +0 -0
  207. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/uuid.py +0 -0
  208. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/version.py +0 -0
  209. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/warnings.py +0 -0
  210. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/whenever.py +0 -0
  211. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/zipfile.py +0 -0
  212. {dycw_utilities-0.155.3 → dycw_utilities-0.156.0}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.155.3
3
+ Version: 0.156.0
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -144,7 +144,7 @@ psutil = [
144
144
  "psutil >=7.0.0, <7.1",
145
145
  ]
146
146
  pyinstrument = [
147
- "pyinstrument >=5.0.3, <5.1",
147
+ "pyinstrument >=5.1.0, <5.2",
148
148
  ]
149
149
  pytest = [
150
150
  "pytest >=8.4.1, <8.5",
@@ -227,7 +227,7 @@ dependencies = [
227
227
  name = "dycw-utilities"
228
228
  readme = "README.md"
229
229
  requires-python = ">= 3.12"
230
- version = "0.155.3"
230
+ version = "0.156.0"
231
231
 
232
232
  [project.entry-points.pytest11]
233
233
  pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
@@ -259,7 +259,7 @@ test = [
259
259
  # bump-my-version
260
260
  [tool.bumpversion]
261
261
  allow_dirty = true
262
- current_version = "0.155.3"
262
+ current_version = "0.156.0"
263
263
 
264
264
  [[tool.bumpversion.files]]
265
265
  filename = "src/utilities/__init__.py"
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import enum
4
+ import pathlib
4
5
  from dataclasses import dataclass
5
6
  from enum import auto
6
7
  from operator import attrgetter
@@ -30,11 +31,7 @@ from utilities.click import (
30
31
  Date,
31
32
  DateDelta,
32
33
  DateTimeDelta,
33
- DirPath,
34
34
  Enum,
35
- ExistingDirPath,
36
- ExistingFilePath,
37
- FilePath,
38
35
  FrozenSetChoices,
39
36
  FrozenSetEnums,
40
37
  FrozenSetInts,
@@ -46,6 +43,7 @@ from utilities.click import (
46
43
  ListInts,
47
44
  ListStrs,
48
45
  MonthDay,
46
+ Path,
49
47
  PlainDateTime,
50
48
  Time,
51
49
  TimeDelta,
@@ -57,6 +55,7 @@ from utilities.hypothesis import (
57
55
  date_time_deltas,
58
56
  dates,
59
57
  month_days,
58
+ paths,
60
59
  plain_date_times,
61
60
  text_ascii,
62
61
  time_deltas,
@@ -68,7 +67,6 @@ from utilities.text import join_strs, strip_and_dedent
68
67
 
69
68
  if TYPE_CHECKING:
70
69
  from collections.abc import Callable, Iterable
71
- from pathlib import Path
72
70
 
73
71
 
74
72
  class TestContextSettingsHelpOptionNames:
@@ -81,91 +79,17 @@ class TestContextSettingsHelpOptionNames:
81
79
  assert result.exit_code == 0
82
80
 
83
81
 
84
- class TestFileAndDirPaths:
85
- def test_existing_dir_path(self, *, tmp_path: Path) -> None:
86
- @command()
87
- @argument("path", type=ExistingDirPath)
88
- def cli(*, path: Path) -> None:
89
- from pathlib import Path
90
-
91
- assert isinstance(path, Path)
82
+ class TestPath:
83
+ def test_path(self, *, tmp_path: pathlib.Path) -> None:
84
+ path_use = pathlib.Path("~", tmp_path)
92
85
 
93
- result = CliRunner().invoke(cli, [str(tmp_path)])
94
- assert result.exit_code == 0
95
-
96
- file_path = tmp_path.joinpath("file.txt")
97
- file_path.touch()
98
- result = CliRunner().invoke(cli, [str(file_path)])
99
- assert result.exit_code == 2
100
- assert search("is a file", result.stderr)
101
-
102
- non_existent = tmp_path.joinpath("non-existent")
103
- result = CliRunner().invoke(cli, [str(non_existent)])
104
- assert result.exit_code == 2
105
- assert search("does not exist", result.stderr)
106
-
107
- def test_existing_file_path(self, *, tmp_path: Path) -> None:
108
86
  @command()
109
- @argument("path", type=ExistingFilePath)
110
- def cli(*, path: Path) -> None:
111
- from pathlib import Path
112
-
113
- assert isinstance(path, Path)
87
+ @argument("path", type=Path())
88
+ def cli(*, path: pathlib.Path) -> None:
89
+ assert isinstance(path, pathlib.Path)
90
+ assert path == path.expanduser()
114
91
 
115
- result = CliRunner().invoke(cli, [str(tmp_path)])
116
- assert result.exit_code == 2
117
- assert search("is a directory", result.stderr)
118
-
119
- file_path = tmp_path.joinpath("file.txt")
120
- file_path.touch()
121
- result = CliRunner().invoke(cli, [str(file_path)])
122
- assert result.exit_code == 0
123
-
124
- non_existent = tmp_path.joinpath("non-existent")
125
- result = CliRunner().invoke(cli, [str(non_existent)])
126
- assert result.exit_code == 2
127
- assert search("does not exist", result.stderr)
128
-
129
- def test_dir_path(self, *, tmp_path: Path) -> None:
130
- @command()
131
- @argument("path", type=DirPath)
132
- def cli(*, path: Path) -> None:
133
- from pathlib import Path
134
-
135
- assert isinstance(path, Path)
136
-
137
- result = CliRunner().invoke(cli, [str(tmp_path)])
138
- assert result.exit_code == 0
139
-
140
- file_path = tmp_path.joinpath("file.txt")
141
- file_path.touch()
142
- result = CliRunner().invoke(cli, [str(file_path)])
143
- assert result.exit_code == 2
144
- assert search("is a file", result.stderr)
145
-
146
- non_existent = tmp_path.joinpath("non-existent")
147
- result = CliRunner().invoke(cli, [str(non_existent)])
148
- assert result.exit_code == 0
149
-
150
- def test_file_path(self, *, tmp_path: Path) -> None:
151
- @command()
152
- @argument("path", type=FilePath)
153
- def cli(*, path: Path) -> None:
154
- from pathlib import Path
155
-
156
- assert isinstance(path, Path)
157
-
158
- result = CliRunner().invoke(cli, [str(tmp_path)])
159
- assert result.exit_code == 2
160
- assert search("is a directory", result.stderr)
161
-
162
- file_path = tmp_path.joinpath("file.txt")
163
- file_path.touch()
164
- result = CliRunner().invoke(cli, [str(file_path)])
165
- assert result.exit_code == 0
166
-
167
- non_existent = tmp_path.joinpath("non-existent")
168
- result = CliRunner().invoke(cli, [str(non_existent)])
92
+ result = CliRunner().invoke(cli, [str(path_use)])
169
93
  assert result.exit_code == 0
170
94
 
171
95
 
@@ -301,6 +225,9 @@ class TestParameters:
301
225
  serialize=whenever.MonthDay.format_common_iso,
302
226
  failable=True,
303
227
  ),
228
+ _Case(
229
+ param=Path(), name="path", strategy=paths(), serialize=str, failable=False
230
+ ),
304
231
  _Case(
305
232
  param=PlainDateTime(),
306
233
  name="plain date-time",
@@ -125,7 +125,7 @@ from utilities.math import (
125
125
  )
126
126
  from utilities.os import temp_environ
127
127
  from utilities.platform import maybe_yield_lower_case
128
- from utilities.sentinel import Sentinel
128
+ from utilities.sentinel import is_sentinel
129
129
  from utilities.version import Version
130
130
  from utilities.whenever import (
131
131
  DATE_TWO_DIGIT_YEAR_MAX,
@@ -142,6 +142,7 @@ if TYPE_CHECKING:
142
142
  from collections.abc import Set as AbstractSet
143
143
  from zoneinfo import ZoneInfo
144
144
 
145
+ from utilities.sentinel import Sentinel
145
146
  from utilities.tempfile import TemporaryDirectory
146
147
  from utilities.types import Number
147
148
 
@@ -786,7 +787,7 @@ class TestSentinels:
786
787
  @given(data=data())
787
788
  def test_main(self, *, data: DataObject) -> None:
788
789
  sentinel = data.draw(sentinels())
789
- assert isinstance(sentinel, Sentinel)
790
+ assert is_sentinel(sentinel)
790
791
 
791
792
 
792
793
  class TestSetsFixedLength:
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from hypothesis import given
6
6
  from hypothesis.strategies import DataObject, data, sampled_from
@@ -10,6 +10,7 @@ from utilities.sentinel import (
10
10
  SENTINEL_REPR,
11
11
  ParseSentinelError,
12
12
  Sentinel,
13
+ is_sentinel,
13
14
  parse_sentinel,
14
15
  sentinel,
15
16
  )
@@ -18,6 +19,12 @@ if TYPE_CHECKING:
18
19
  from collections.abc import Callable
19
20
 
20
21
 
22
+ class TestIsSentinel:
23
+ @mark.parametrize(("obj", "expected"), [param(None, False), param(sentinel, True)])
24
+ def test_main(self, *, obj: Any, expected: bool) -> None:
25
+ assert is_sentinel(obj) is expected
26
+
27
+
21
28
  class TestParseSentinel:
22
29
  @given(data=data())
23
30
  def test_main(self, *, data: DataObject) -> None:
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.155.3"
3
+ __version__ = "0.156.0"
@@ -6,7 +6,6 @@ import pathlib
6
6
  import uuid
7
7
  from typing import TYPE_CHECKING, TypedDict, assert_never, override
8
8
 
9
- import click
10
9
  import whenever
11
10
  from click import Choice, Context, Parameter, ParamType
12
11
  from click.types import IntParamType, StringParamType
@@ -29,6 +28,7 @@ if TYPE_CHECKING:
29
28
  IPv6AddressLike,
30
29
  MaybeStr,
31
30
  MonthDayLike,
31
+ PathLike,
32
32
  PlainDateTimeLike,
33
33
  TimeDeltaLike,
34
34
  TimeLike,
@@ -37,16 +37,6 @@ if TYPE_CHECKING:
37
37
  )
38
38
 
39
39
 
40
- FilePath = click.Path(file_okay=True, dir_okay=False, path_type=pathlib.Path)
41
- DirPath = click.Path(file_okay=False, dir_okay=True, path_type=pathlib.Path)
42
- ExistingFilePath = click.Path(
43
- exists=True, file_okay=True, dir_okay=False, path_type=pathlib.Path
44
- )
45
- ExistingDirPath = click.Path(
46
- exists=True, file_okay=False, dir_okay=True, path_type=pathlib.Path
47
- )
48
-
49
-
50
40
  class _HelpOptionNames(TypedDict):
51
41
  help_option_names: Sequence[str]
52
42
 
@@ -252,6 +242,32 @@ class MonthDay(ParamType):
252
242
  assert_never(never)
253
243
 
254
244
 
245
+ class Path(ParamType):
246
+ """A path-valued parameter."""
247
+
248
+ name = "path"
249
+
250
+ @override
251
+ def __repr__(self) -> str:
252
+ return self.name.upper()
253
+
254
+ @override
255
+ def convert(
256
+ self, value: PathLike, param: Parameter | None, ctx: Context | None
257
+ ) -> pathlib.Path:
258
+ """Convert a value into the `Path` type."""
259
+ match value:
260
+ case pathlib.Path():
261
+ return value.expanduser()
262
+ case str():
263
+ try:
264
+ return pathlib.Path(value).expanduser()
265
+ except ValueError as error:
266
+ self.fail(str(error), param, ctx)
267
+ case never:
268
+ assert_never(never)
269
+
270
+
255
271
  class PlainDateTime(ParamType):
256
272
  """A local-datetime-valued parameter."""
257
273
 
@@ -592,11 +608,7 @@ __all__ = [
592
608
  "Date",
593
609
  "DateDelta",
594
610
  "DateTimeDelta",
595
- "DirPath",
596
611
  "Enum",
597
- "ExistingDirPath",
598
- "ExistingFilePath",
599
- "FilePath",
600
612
  "FrozenSetChoices",
601
613
  "FrozenSetEnums",
602
614
  "FrozenSetParameter",
@@ -609,6 +621,8 @@ __all__ = [
609
621
  "ListParameter",
610
622
  "ListStrs",
611
623
  "MonthDay",
624
+ "Path",
625
+ "Path",
612
626
  "PlainDateTime",
613
627
  "Time",
614
628
  "TimeDelta",
@@ -25,7 +25,7 @@ from utilities.parse import (
25
25
  serialize_object,
26
26
  )
27
27
  from utilities.re import ExtractGroupError, extract_group
28
- from utilities.sentinel import Sentinel, sentinel
28
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
29
29
  from utilities.text import (
30
30
  BRACKETS,
31
31
  LIST_SEPARATOR,
@@ -275,8 +275,7 @@ def mapping_to_dataclass[T: Dataclass](
275
275
  default = {
276
276
  f.name
277
277
  for f in fields_use
278
- if (not isinstance(f.default, Sentinel))
279
- or (not isinstance(f.default_factory, Sentinel))
278
+ if (not is_sentinel(f.default)) or (not is_sentinel(f.default_factory))
280
279
  }
281
280
  have = set(field_names_to_values) | default
282
281
  missing = {f.name for f in fields_use} - have
@@ -434,12 +433,10 @@ def replace_non_sentinel[T: Dataclass](
434
433
  """Replace attributes on a dataclass, filtering out sentinel values."""
435
434
  if in_place:
436
435
  for k, v in kwargs.items():
437
- if not isinstance(v, Sentinel):
436
+ if not is_sentinel(v):
438
437
  setattr(obj, k, v)
439
438
  return None
440
- return replace(
441
- obj, **{k: v for k, v in kwargs.items() if not isinstance(v, Sentinel)}
442
- )
439
+ return replace(obj, **{k: v for k, v in kwargs.items() if not is_sentinel(v)})
443
440
 
444
441
 
445
442
  ##
@@ -912,17 +909,11 @@ class _YieldFieldsInstance[T]:
912
909
  extra: Mapping[type[U], Callable[[U, U], bool]] | None = None,
913
910
  ) -> bool:
914
911
  """Check if the field value equals its default."""
915
- if isinstance(self.default, Sentinel) and isinstance(
916
- self.default_factory, Sentinel
917
- ):
912
+ if is_sentinel(self.default) and is_sentinel(self.default_factory):
918
913
  return False
919
- if (not isinstance(self.default, Sentinel)) and isinstance(
920
- self.default_factory, Sentinel
921
- ):
914
+ if (not is_sentinel(self.default)) and is_sentinel(self.default_factory):
922
915
  expected = self.default
923
- elif isinstance(self.default, Sentinel) and (
924
- not isinstance(self.default_factory, Sentinel)
925
- ):
916
+ elif is_sentinel(self.default) and (not is_sentinel(self.default_factory)):
926
917
  expected = self.default_factory()
927
918
  else: # pragma: no cover
928
919
  raise ImpossibleCaseError(
@@ -19,7 +19,7 @@ from typing import TYPE_CHECKING, Any, Literal, TypeGuard, cast, overload, overr
19
19
  from whenever import Date, PlainDateTime, Time, TimeDelta, ZonedDateTime
20
20
 
21
21
  from utilities.reprlib import get_repr, get_repr_and_class
22
- from utilities.sentinel import Sentinel, sentinel
22
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
23
23
  from utilities.types import (
24
24
  Dataclass,
25
25
  Number,
@@ -864,7 +864,7 @@ def min_nullable[T: SupportsRichComparison, U](
864
864
  ) -> T | U:
865
865
  """Compute the minimum of a set of values; ignoring nulls."""
866
866
  values = (i for i in iterable if i is not None)
867
- if isinstance(default, Sentinel):
867
+ if is_sentinel(default):
868
868
  try:
869
869
  return min(values)
870
870
  except ValueError:
@@ -894,7 +894,7 @@ def max_nullable[T: SupportsRichComparison, U](
894
894
  ) -> T | U:
895
895
  """Compute the maximum of a set of values; ignoring nulls."""
896
896
  values = (i for i in iterable if i is not None)
897
- if isinstance(default, Sentinel):
897
+ if is_sentinel(default):
898
898
  try:
899
899
  return max(values)
900
900
  except ValueError:
@@ -77,7 +77,7 @@ from utilities.math import (
77
77
  from utilities.os import get_env_var
78
78
  from utilities.pathlib import module_path, temp_cwd
79
79
  from utilities.platform import IS_WINDOWS
80
- from utilities.sentinel import Sentinel, sentinel
80
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
81
81
  from utilities.tempfile import TEMP_DIR, TemporaryDirectory
82
82
  from utilities.version import Version
83
83
  from utilities.whenever import (
@@ -363,14 +363,14 @@ def draw2[T](
363
363
  return value
364
364
  case None, SearchStrategy(), False:
365
365
  value2 = draw(default)
366
- if isinstance(value2, Sentinel):
366
+ if is_sentinel(value2):
367
367
  raise _Draw2DefaultGeneratedSentinelError
368
368
  return value2
369
369
  case Sentinel(), None, _:
370
370
  raise _Draw2InputResolvedToSentinelError
371
371
  case Sentinel(), SearchStrategy(), True:
372
372
  value2 = draw(default)
373
- if isinstance(value2, Sentinel):
373
+ if is_sentinel(value2):
374
374
  raise _Draw2DefaultGeneratedSentinelError
375
375
  return value2
376
376
  case Sentinel(), SearchStrategy(), False:
@@ -40,7 +40,7 @@ from utilities.math import (
40
40
  check_integer,
41
41
  )
42
42
  from utilities.reprlib import get_repr
43
- from utilities.sentinel import Sentinel, sentinel
43
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
44
44
  from utilities.types import SupportsAdd, SupportsLT
45
45
 
46
46
  if TYPE_CHECKING:
@@ -1237,7 +1237,7 @@ def reduce_mappings[K, V, W](
1237
1237
  ) -> Mapping[K, V | W]:
1238
1238
  """Reduce a function over the values of a set of mappings."""
1239
1239
  chained = chain_mappings(*sequence)
1240
- if isinstance(initial, Sentinel):
1240
+ if is_sentinel(initial):
1241
1241
  func2 = cast("Callable[[V, V], V]", func)
1242
1242
  return {k: reduce(func2, v) for k, v in chained.items()}
1243
1243
  func2 = cast("Callable[[W, V], W]", func)
@@ -21,7 +21,7 @@ from more_itertools import peekable as _peekable
21
21
  from utilities.functions import get_class_name
22
22
  from utilities.iterables import OneNonUniqueError, one
23
23
  from utilities.reprlib import get_repr
24
- from utilities.sentinel import Sentinel, sentinel
24
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
25
25
 
26
26
  if TYPE_CHECKING:
27
27
  from collections.abc import Iterable, Iterator, Mapping, Sequence
@@ -290,9 +290,7 @@ class peekable[T](_peekable): # noqa: N801
290
290
  def peek[U](self, *, default: U) -> T | U: ...
291
291
  @override
292
292
  def peek(self, *, default: Any = sentinel) -> Any: # pyright: ignore[reportIncompatibleMethodOverride]
293
- if isinstance(default, Sentinel):
294
- return super().peek()
295
- return super().peek(default=default)
293
+ return super().peek() if is_sentinel(default) else super().peek(default=default)
296
294
 
297
295
  def takewhile(self, predicate: Callable[[T], bool], /) -> Iterator[T]:
298
296
  while bool(self) and predicate(self.peek()):
@@ -9,7 +9,7 @@ from tqdm.auto import tqdm as tqdm_auto
9
9
  from utilities.functions import get_func_name
10
10
  from utilities.iterables import apply_to_varargs
11
11
  from utilities.os import get_cpu_use
12
- from utilities.sentinel import Sentinel, sentinel
12
+ from utilities.sentinel import Sentinel, is_sentinel, sentinel
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from collections.abc import Callable, Iterable
@@ -98,7 +98,7 @@ def pqdm_starmap[T](
98
98
  def _get_desc(
99
99
  desc: str | None | Sentinel, func: Callable[..., Any], /
100
100
  ) -> dict[str, str]:
101
- desc_use = get_func_name(func) if isinstance(desc, Sentinel) else desc
101
+ desc_use = get_func_name(func) if is_sentinel(desc) else desc
102
102
  return {} if desc_use is None else {"desc": desc_use}
103
103
 
104
104
 
@@ -4,6 +4,8 @@ from dataclasses import dataclass
4
4
  from re import IGNORECASE, search
5
5
  from typing import Any, override
6
6
 
7
+ from typing_extensions import TypeIs
8
+
7
9
 
8
10
  class _Meta(type):
9
11
  """Metaclass for the sentinel."""
@@ -34,6 +36,13 @@ class Sentinel(metaclass=_Meta):
34
36
 
35
37
  sentinel = Sentinel()
36
38
 
39
+ ##
40
+
41
+
42
+ def is_sentinel(obj: Any, /) -> TypeIs[Sentinel]:
43
+ """Check if an object is the sentinel."""
44
+ return obj is sentinel
45
+
37
46
 
38
47
  ##
39
48
 
@@ -58,6 +67,7 @@ __all__ = [
58
67
  "SENTINEL_REPR",
59
68
  "ParseSentinelError",
60
69
  "Sentinel",
70
+ "is_sentinel",
61
71
  "parse_sentinel",
62
72
  "sentinel",
63
73
  ]