dycw-utilities 0.129.8__tar.gz → 0.129.9__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 (228) hide show
  1. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/PKG-INFO +1 -1
  2. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/pyproject.toml +2 -2
  3. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback.py +122 -2
  4. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_whenever.py +16 -7
  5. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/__init__.py +1 -1
  6. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/traceback.py +241 -6
  7. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/.gitignore +0 -0
  8. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/LICENSE +0 -0
  9. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/README.md +0 -0
  10. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/__init__.py +0 -0
  11. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/conftest.py +0 -0
  12. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/__init__.py +0 -0
  13. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_missing/__init__.py +0 -0
  14. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_missing/module.py +0 -0
  15. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/__init__.py +0 -0
  16. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/outer_1.py +0 -0
  17. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/outer_2.py +0 -0
  18. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
  19. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
  20. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
  21. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
  22. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_without/__init__.py +0 -0
  23. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_without/module_1.py +0 -0
  24. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/package_without/module_2.py +0 -0
  25. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/standalone.py +0 -0
  26. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/modules/with_imports.py +0 -0
  27. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
  28. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
  29. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
  30. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
  31. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
  32. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
  33. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
  34. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
  35. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_altair.py +0 -0
  36. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_asyncio.py +0 -0
  37. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_asyncio_classes/__init__.py +0 -0
  38. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_asyncio_classes/loopers.py +0 -0
  39. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_asyncio_classes/redis.py +0 -0
  40. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_atomicwrites.py +0 -0
  41. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_atools.py +0 -0
  42. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_cachetools.py +0 -0
  43. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_click.py +0 -0
  44. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_concurrent.py +0 -0
  45. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_contextlib.py +0 -0
  46. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_contextvars.py +0 -0
  47. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_cryptography.py +0 -0
  48. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_cvxpy.py +0 -0
  49. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_dataclasses.py +0 -0
  50. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_datetime.py +0 -0
  51. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_enum.py +0 -0
  52. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_errors.py +0 -0
  53. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_eventkit.py +0 -0
  54. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_fastapi.py +0 -0
  55. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_fpdf2.py +0 -0
  56. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_functions.py +0 -0
  57. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_functools.py +0 -0
  58. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_getpass.py +0 -0
  59. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_hashlib.py +0 -0
  60. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_http.py +0 -0
  61. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_hypothesis.py +0 -0
  62. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_importlib.py +0 -0
  63. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_ipython.py +0 -0
  64. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_iterables.py +0 -0
  65. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_jupyter.py +0 -0
  66. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_libcst.py +0 -0
  67. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_lightweight_charts.py +0 -0
  68. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_logging.py +0 -0
  69. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_loguru.py +0 -0
  70. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_luigi.py +0 -0
  71. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_math.py +0 -0
  72. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_memory_profiler.py +0 -0
  73. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_modules.py +0 -0
  74. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_more_itertools.py +0 -0
  75. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_numpy.py +0 -0
  76. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_operator.py +0 -0
  77. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_optuna.py +0 -0
  78. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_orjson.py +0 -0
  79. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_os.py +0 -0
  80. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_parse.py +0 -0
  81. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pathlib.py +0 -0
  82. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_period.py +0 -0
  83. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pickle.py +0 -0
  84. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_platform.py +0 -0
  85. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_polars.py +0 -0
  86. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_polars_ols.py +0 -0
  87. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pottery.py +0 -0
  88. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pqdm.py +0 -0
  89. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_psutil.py +0 -0
  90. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pydantic.py +0 -0
  91. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pyinstrument.py +0 -0
  92. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pyrsistent.py +0 -0
  93. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pytest.py +0 -0
  94. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_pytest_regressions.py +0 -0
  95. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_python_dotenv.py +0 -0
  96. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_random.py +0 -0
  97. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_re.py +0 -0
  98. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_redis.py +0 -0
  99. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_reprlib.py +0 -0
  100. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_scipy.py +0 -0
  101. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_sentinel.py +0 -0
  102. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_shelve.py +0 -0
  103. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_slack_sdk.py +0 -0
  104. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_socket.py +0 -0
  105. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_sqlalchemy.py +0 -0
  106. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_sqlalchemy_polars.py +0 -0
  107. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_statsmodel.py +0 -0
  108. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_streamlit.py +0 -0
  109. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_string.py +0 -0
  110. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_sys.py +0 -0
  111. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_tempfile.py +0 -0
  112. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_tenacity.py +0 -0
  113. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_text.py +0 -0
  114. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_threading.py +0 -0
  115. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_timer.py +0 -0
  116. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/__init__.py +0 -0
  117. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/chain.py +0 -0
  118. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
  119. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
  120. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/error_bind.py +0 -0
  121. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/many.py +0 -0
  122. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/one.py +0 -0
  123. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/recursive.py +0 -0
  124. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
  125. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
  126. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/two.py +0 -0
  127. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_traceback_funcs/untraced.py +0 -0
  128. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_types.py +0 -0
  129. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_typing.py +0 -0
  130. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_typing_funcs/__init__.py +0 -0
  131. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_typing_funcs/no_future.py +0 -0
  132. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_typing_funcs/with_future.py +0 -0
  133. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_tzdata.py +0 -0
  134. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_tzlocal.py +0 -0
  135. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_uuid.py +0 -0
  136. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_version.py +0 -0
  137. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_warnings.py +0 -0
  138. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_zipfile.py +0 -0
  139. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/tests/test_zoneinfo.py +0 -0
  140. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/altair.py +0 -0
  141. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/asyncio.py +0 -0
  142. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/atomicwrites.py +0 -0
  143. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/atools.py +0 -0
  144. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/cachetools.py +0 -0
  145. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/click.py +0 -0
  146. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/concurrent.py +0 -0
  147. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/contextlib.py +0 -0
  148. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/contextvars.py +0 -0
  149. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/cryptography.py +0 -0
  150. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/cvxpy.py +0 -0
  151. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/dataclasses.py +0 -0
  152. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/datetime.py +0 -0
  153. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/enum.py +0 -0
  154. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/errors.py +0 -0
  155. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/eventkit.py +0 -0
  156. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/fastapi.py +0 -0
  157. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/fpdf2.py +0 -0
  158. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/functions.py +0 -0
  159. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/functools.py +0 -0
  160. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/getpass.py +0 -0
  161. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/hashlib.py +0 -0
  162. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/http.py +0 -0
  163. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/hypothesis.py +0 -0
  164. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/importlib.py +0 -0
  165. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/ipython.py +0 -0
  166. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/iterables.py +0 -0
  167. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/jupyter.py +0 -0
  168. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/libcst.py +0 -0
  169. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/lightweight_charts.py +0 -0
  170. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/logging.py +0 -0
  171. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/loguru.py +0 -0
  172. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/luigi.py +0 -0
  173. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/math.py +0 -0
  174. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/memory_profiler.py +0 -0
  175. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/modules.py +0 -0
  176. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/more_itertools.py +0 -0
  177. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/numpy.py +0 -0
  178. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/operator.py +0 -0
  179. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/optuna.py +0 -0
  180. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/orjson.py +0 -0
  181. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/os.py +0 -0
  182. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/parse.py +0 -0
  183. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pathlib.py +0 -0
  184. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/period.py +0 -0
  185. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pickle.py +0 -0
  186. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/platform.py +0 -0
  187. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/polars.py +0 -0
  188. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/polars_ols.py +0 -0
  189. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pottery.py +0 -0
  190. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pqdm.py +0 -0
  191. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/psutil.py +0 -0
  192. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/py.typed +0 -0
  193. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pydantic.py +0 -0
  194. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pyinstrument.py +0 -0
  195. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pyrsistent.py +0 -0
  196. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pytest.py +0 -0
  197. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/pytest_regressions.py +0 -0
  198. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/python_dotenv.py +0 -0
  199. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/random.py +0 -0
  200. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/re.py +0 -0
  201. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/redis.py +0 -0
  202. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/reprlib.py +0 -0
  203. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/scipy.py +0 -0
  204. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/sentinel.py +0 -0
  205. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/shelve.py +0 -0
  206. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/slack_sdk.py +0 -0
  207. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/socket.py +0 -0
  208. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/sqlalchemy.py +0 -0
  209. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/sqlalchemy_polars.py +0 -0
  210. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/statsmodels.py +0 -0
  211. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/streamlit.py +0 -0
  212. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/string.py +0 -0
  213. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/sys.py +0 -0
  214. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/tempfile.py +0 -0
  215. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/tenacity.py +0 -0
  216. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/text.py +0 -0
  217. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/threading.py +0 -0
  218. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/timer.py +0 -0
  219. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/types.py +0 -0
  220. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/typing.py +0 -0
  221. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/tzdata.py +0 -0
  222. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/tzlocal.py +0 -0
  223. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/uuid.py +0 -0
  224. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/version.py +0 -0
  225. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/warnings.py +0 -0
  226. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/whenever.py +0 -0
  227. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/zipfile.py +0 -0
  228. {dycw_utilities-0.129.8 → dycw_utilities-0.129.9}/src/utilities/zoneinfo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.129.8
3
+ Version: 0.129.9
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -93,7 +93,7 @@ dependencies = [
93
93
  name = "dycw-utilities"
94
94
  readme = "README.md"
95
95
  requires-python = ">= 3.12"
96
- version = "0.129.8"
96
+ version = "0.129.9"
97
97
 
98
98
  [project.optional-dependencies]
99
99
  test = [
@@ -332,7 +332,7 @@ zzz-test-zoneinfo = [
332
332
  # bump-my-version
333
333
  [tool.bumpversion]
334
334
  allow_dirty = true
335
- current_version = "0.129.8"
335
+ current_version = "0.129.9"
336
336
 
337
337
  [[tool.bumpversion.files]]
338
338
  filename = "src/utilities/__init__.py"
@@ -2,9 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  from io import StringIO
4
4
  from logging import StreamHandler, getLogger
5
+ from pathlib import Path
6
+ from re import search
7
+ from sys import exc_info
5
8
  from typing import TYPE_CHECKING, ClassVar, Literal
6
9
 
7
- from pytest import raises
10
+ from hypothesis import given
11
+ from hypothesis.strategies import sampled_from
12
+ from pytest import CaptureFixture, raises
8
13
 
9
14
  from tests.conftest import SKIPIF_CI
10
15
  from tests.test_traceback_funcs.chain import func_chain_first
@@ -29,11 +34,15 @@ from utilities.traceback import (
29
34
  ExcChainTB,
30
35
  ExcGroupTB,
31
36
  ExcTB,
37
+ MakeExceptHookError,
32
38
  RichTracebackFormatter,
33
39
  _CallArgsError,
34
40
  _format_exception,
35
41
  _Frame,
42
+ _path_to_dots,
43
+ format_exception_stack,
36
44
  get_rich_traceback,
45
+ make_except_hook,
37
46
  trace,
38
47
  yield_exceptions,
39
48
  yield_extended_frame_summaries,
@@ -41,7 +50,7 @@ from utilities.traceback import (
41
50
  )
42
51
 
43
52
  if TYPE_CHECKING:
44
- from pathlib import Path
53
+ from collections.abc import Iterable
45
54
  from re import Pattern
46
55
  from traceback import FrameSummary
47
56
  from types import FrameType
@@ -62,6 +71,54 @@ class TestFormatException:
62
71
  assert result == expected
63
72
 
64
73
 
74
+ class TestFormatExceptionStack:
75
+ def test_main(self) -> None:
76
+ try:
77
+ _ = func_one(1, 2, 3, 4, c=5, d=6, e=7)
78
+ except AssertionError as error:
79
+ result = format_exception_stack(error).splitlines()
80
+ self._assert_lines(result)
81
+
82
+ def test_header(self) -> None:
83
+ try:
84
+ _ = func_one(1, 2, 3, 4, c=5, d=6, e=7)
85
+ except AssertionError as error:
86
+ result = format_exception_stack(error, header=True).splitlines()
87
+ patterns = [
88
+ r"^Date/time \| .+$",
89
+ r"^Started \| .+$",
90
+ r"^Duration \| .+$",
91
+ r"^User \| .+$",
92
+ r"^Host \| .+$",
93
+ r"^Version \|\s$",
94
+ r"^$",
95
+ ]
96
+ for line, pattern in zip(result[:7], patterns[:7], strict=False):
97
+ assert search(pattern, line), line
98
+ self._assert_lines(result[7:])
99
+
100
+ def test_capture_locals(self) -> None:
101
+ try:
102
+ _ = func_one(1, 2, 3, 4, c=5, d=6, e=7)
103
+ except AssertionError as error:
104
+ result = format_exception_stack(error, capture_locals=True).splitlines()
105
+ assert len(result) == 17
106
+ indices = [0, 3, 9, 16]
107
+ self._assert_lines([result[i] for i in indices])
108
+ for i in set(range(17)) - set(indices):
109
+ assert search(r"^ \| \w+ = .+$", result[i])
110
+
111
+ def _assert_lines(self, lines: Iterable[str], /) -> None:
112
+ expected = [
113
+ r"^1/3 \| tests\.test_traceback:\d+ \| test_\w+ \| _ = func_one\(1, 2, 3, 4, c=5, d=6, e=7\)$",
114
+ r"^2/3 \| utilities\.traceback:\d+ \| trace_sync \| return func_typed\(\*args, \*\*kwargs\)$",
115
+ r'^3/3 \| tests\.test_traceback_funcs\.one:16 \| func_one \| assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"$',
116
+ r"^AssertionError\(Result \(56\) must be divisible by 10\)$",
117
+ ]
118
+ for line, pattern in zip(lines, expected, strict=True):
119
+ assert search(pattern, line), line
120
+
121
+
65
122
  class TestFrame:
66
123
  frame: ClassVar[_Frame] = _Frame(
67
124
  module="module",
@@ -463,6 +520,33 @@ class TestGetRichTraceback:
463
520
  assert isinstance(exc_path.error, AssertionError)
464
521
 
465
522
 
523
+ class TestMakeExceptHook:
524
+ def test_main(self, *, capsys: CaptureFixture) -> None:
525
+ hook = make_except_hook()
526
+ try:
527
+ _ = 1 / 0
528
+ except ZeroDivisionError:
529
+ exc_type, exc_val, traceback = exc_info()
530
+ hook(exc_type, exc_val, traceback)
531
+ assert capsys.readouterr() != ""
532
+
533
+ def test_file(self, *, tmp_path: Path) -> None:
534
+ hook = make_except_hook(path=tmp_path)
535
+ try:
536
+ _ = 1 / 0
537
+ except ZeroDivisionError:
538
+ exc_type, exc_val, traceback = exc_info()
539
+ hook(exc_type, exc_val, traceback)
540
+ path = one(tmp_path.iterdir())
541
+ assert search(r"^\d{8}T\d{6}\.txt$", path.name)
542
+
543
+ def test_non_error(self) -> None:
544
+ hook = make_except_hook()
545
+ exc_type, exc_val, traceback = exc_info()
546
+ with raises(MakeExceptHookError, match="No exception to log"):
547
+ hook(exc_type, exc_val, traceback)
548
+
549
+
466
550
  class TestRichTracebackFormatter:
467
551
  def test_decorated(
468
552
  self, *, tmp_path: Path, traceback_func_one: Pattern[str]
@@ -519,6 +603,42 @@ class TestRichTracebackFormatter:
519
603
  assert result.startswith("> ")
520
604
 
521
605
 
606
+ class TestPathToDots:
607
+ @given(
608
+ case=sampled_from([
609
+ (
610
+ Path("repo", ".venv", "lib", "site-packages", "click", "core.py"),
611
+ "click.core",
612
+ ),
613
+ (
614
+ Path(
615
+ "repo", ".venv", "lib", "site-packages", "utilities", "traceback.py"
616
+ ),
617
+ "utilities.traceback",
618
+ ),
619
+ (Path("repo", ".venv", "bin", "cli.py"), "bin.cli"),
620
+ (Path("src", "utilities", "foo", "bar.py"), "utilities.foo.bar"),
621
+ (
622
+ Path(
623
+ "uv",
624
+ "python",
625
+ "cpython-3.13.0-macos-aarch64-none",
626
+ "lib",
627
+ "python3.13",
628
+ "asyncio",
629
+ "runners.py",
630
+ ),
631
+ "asyncio.runners",
632
+ ),
633
+ (Path("unknown", "file.py"), "unknown.file"),
634
+ ])
635
+ )
636
+ def test_main(self, *, case: tuple[Path, str]) -> None:
637
+ path, expected = case
638
+ result = _path_to_dots(path)
639
+ assert result == expected
640
+
641
+
522
642
  class TestYieldExceptions:
523
643
  def test_main(self) -> None:
524
644
  class FirstError(Exception): ...
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import datetime as dt
4
4
  from datetime import timezone
5
- from logging import getLogger, setLogRecordFactory
5
+ from logging import DEBUG
6
6
  from re import escape
7
7
  from typing import TYPE_CHECKING
8
8
  from zoneinfo import ZoneInfo
@@ -21,7 +21,7 @@ from hypothesis.strategies import (
21
21
  timezones,
22
22
  )
23
23
  from pytest import raises
24
- from whenever import DateTimeDelta
24
+ from whenever import DateTimeDelta, ZonedDateTime
25
25
 
26
26
  from tests.conftest import SKIPIF_CI_AND_WINDOWS
27
27
  from utilities.datetime import (
@@ -41,7 +41,6 @@ from utilities.hypothesis import (
41
41
  timedeltas_2w,
42
42
  zoned_datetimes,
43
43
  )
44
- from utilities.text import unique_str
45
44
  from utilities.tzdata import HongKong
46
45
  from utilities.whenever import (
47
46
  MAX_SERIALIZABLE_TIMEDELTA,
@@ -498,7 +497,17 @@ class TestToDateTimeDelta:
498
497
 
499
498
 
500
499
  class TestWheneverLogRecord:
501
- def test_main(self) -> None:
502
- logger = getLogger(unique_str())
503
- setLogRecordFactory(WheneverLogRecord)
504
- logger.warning("message")
500
+ def test_init(self) -> None:
501
+ _ = WheneverLogRecord("name", DEBUG, "pathname", 0, None, None, None)
502
+
503
+ def test_get_length(self) -> None:
504
+ assert isinstance(WheneverLogRecord._get_length(), int)
505
+
506
+ def test_get_now(self) -> None:
507
+ assert isinstance(WheneverLogRecord._get_now(), ZonedDateTime)
508
+
509
+ def test_get_time_zone(self) -> None:
510
+ assert isinstance(WheneverLogRecord._get_time_zone(), ZoneInfo)
511
+
512
+ def test_get_time_zone_key(self) -> None:
513
+ assert isinstance(WheneverLogRecord._get_time_zone_key(), str)
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.129.8"
3
+ __version__ = "0.129.9"
@@ -1,10 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
4
+ import sys
5
+ from asyncio import run
3
6
  from collections.abc import Callable, Iterable
4
7
  from dataclasses import dataclass, field, replace
5
- from functools import wraps
8
+ from functools import partial, wraps
6
9
  from getpass import getuser
7
10
  from inspect import iscoroutinefunction, signature
11
+ from itertools import repeat
8
12
  from logging import Formatter, Handler, LogRecord
9
13
  from pathlib import Path
10
14
  from socket import gethostname
@@ -26,8 +30,8 @@ from typing import (
26
30
  runtime_checkable,
27
31
  )
28
32
 
29
- from utilities.datetime import get_datetime, get_now
30
- from utilities.errors import ImpossibleCaseError
33
+ from utilities.datetime import get_datetime, get_now, serialize_compact
34
+ from utilities.errors import ImpossibleCaseError, repr_error
31
35
  from utilities.functions import (
32
36
  ensure_not_none,
33
37
  ensure_str,
@@ -35,7 +39,8 @@ from utilities.functions import (
35
39
  get_func_name,
36
40
  get_func_qualname,
37
41
  )
38
- from utilities.iterables import always_iterable, one
42
+ from utilities.iterables import OneEmptyError, always_iterable, one
43
+ from utilities.pathlib import get_path
39
44
  from utilities.reprlib import (
40
45
  RICH_EXPAND_ALL,
41
46
  RICH_INDENT_SIZE,
@@ -46,12 +51,18 @@ from utilities.reprlib import (
46
51
  yield_call_args_repr,
47
52
  yield_mapping_repr,
48
53
  )
49
- from utilities.types import MaybeCallableDateTime, TBaseException, TCallable
54
+ from utilities.types import (
55
+ MaybeCallableDateTime,
56
+ MaybeCallablePathLike,
57
+ PathLike,
58
+ TBaseException,
59
+ TCallable,
60
+ )
50
61
  from utilities.version import get_version
51
62
  from utilities.whenever import serialize_duration
52
63
 
53
64
  if TYPE_CHECKING:
54
- from collections.abc import Callable, Iterable, Iterator
65
+ from collections.abc import Callable, Iterable, Iterator, Sequence
55
66
  from logging import _FormatStyle
56
67
  from types import FrameType, TracebackType
57
68
 
@@ -68,6 +79,131 @@ _START = get_now()
68
79
  ##
69
80
 
70
81
 
82
+ def format_exception_stack(
83
+ error: BaseException,
84
+ /,
85
+ *,
86
+ header: bool = False,
87
+ start: MaybeCallableDateTime | None = _START,
88
+ version: MaybeCallableVersionLike | None = None,
89
+ capture_locals: bool = False,
90
+ max_width: int = RICH_MAX_WIDTH,
91
+ indent_size: int = RICH_INDENT_SIZE,
92
+ max_length: int | None = RICH_MAX_LENGTH,
93
+ max_string: int | None = RICH_MAX_STRING,
94
+ max_depth: int | None = RICH_MAX_DEPTH,
95
+ expand_all: bool = RICH_EXPAND_ALL,
96
+ ) -> str:
97
+ """Format an exception stack."""
98
+ lines: Sequence[str] = []
99
+ if header:
100
+ lines.extend(_yield_header_lines(start=start, version=version))
101
+ lines.extend(
102
+ _yield_formatted_frame_summary(
103
+ error,
104
+ capture_locals=capture_locals,
105
+ max_width=max_width,
106
+ indent_size=indent_size,
107
+ max_length=max_length,
108
+ max_string=max_string,
109
+ max_depth=max_depth,
110
+ expand_all=expand_all,
111
+ )
112
+ )
113
+ return "\n".join(lines)
114
+
115
+
116
+ ##
117
+
118
+
119
+ def make_except_hook(
120
+ *,
121
+ start: MaybeCallableDateTime | None = _START,
122
+ version: MaybeCallableVersionLike | None = None,
123
+ path: MaybeCallablePathLike | None = None,
124
+ max_width: int = RICH_MAX_WIDTH,
125
+ indent_size: int = RICH_INDENT_SIZE,
126
+ max_length: int | None = RICH_MAX_LENGTH,
127
+ max_string: int | None = RICH_MAX_STRING,
128
+ max_depth: int | None = RICH_MAX_DEPTH,
129
+ expand_all: bool = RICH_EXPAND_ALL,
130
+ slack_url: str | None = None,
131
+ ) -> Callable[
132
+ [type[BaseException] | None, BaseException | None, TracebackType | None], None
133
+ ]:
134
+ """Exception hook to log the traceback."""
135
+ return partial(
136
+ _make_except_hook_inner,
137
+ start=start,
138
+ version=version,
139
+ path=path,
140
+ max_width=max_width,
141
+ indent_size=indent_size,
142
+ max_length=max_length,
143
+ max_string=max_string,
144
+ max_depth=max_depth,
145
+ expand_all=expand_all,
146
+ slack_url=slack_url,
147
+ )
148
+
149
+
150
+ def _make_except_hook_inner(
151
+ exc_type: type[BaseException] | None,
152
+ exc_val: BaseException | None,
153
+ traceback: TracebackType | None,
154
+ /,
155
+ *,
156
+ start: MaybeCallableDateTime | None = _START,
157
+ version: MaybeCallableVersionLike | None = None,
158
+ path: MaybeCallablePathLike | None = None,
159
+ max_width: int = RICH_MAX_WIDTH,
160
+ indent_size: int = RICH_INDENT_SIZE,
161
+ max_length: int | None = RICH_MAX_LENGTH,
162
+ max_string: int | None = RICH_MAX_STRING,
163
+ max_depth: int | None = RICH_MAX_DEPTH,
164
+ expand_all: bool = RICH_EXPAND_ALL,
165
+ slack_url: str | None = None,
166
+ ) -> None:
167
+ """Exception hook to log the traceback."""
168
+ _ = (exc_type, traceback)
169
+ if exc_val is None:
170
+ raise MakeExceptHookError
171
+ slim = format_exception_stack(exc_val, header=True, start=start, version=version)
172
+ _ = sys.stderr.write(f"{slim}\n") # don't 'from sys import stderr'
173
+ if path is not None:
174
+ from utilities.atomicwrites import writer
175
+ from utilities.tzlocal import get_now_local
176
+
177
+ path = (
178
+ get_path(path=path)
179
+ .joinpath(serialize_compact(get_now_local()))
180
+ .with_suffix(".txt")
181
+ )
182
+ full = format_exception_stack(
183
+ exc_val,
184
+ header=True,
185
+ start=start,
186
+ version=version,
187
+ capture_locals=True,
188
+ max_width=max_width,
189
+ indent_size=indent_size,
190
+ max_length=max_length,
191
+ max_string=max_string,
192
+ max_depth=max_depth,
193
+ expand_all=expand_all,
194
+ )
195
+ with writer(path, overwrite=True) as temp:
196
+ _ = temp.write_text(full)
197
+ if slack_url is not None: # pragma: no cover
198
+ from utilities.slack_sdk import send_to_slack
199
+
200
+ send = f"```{slim}```"
201
+ run(send_to_slack(slack_url, send))
202
+
203
+
204
+ ##
205
+
206
+
71
207
  class RichTracebackFormatter(Formatter):
72
208
  """Formatter for rich tracebacks."""
73
209
 
@@ -811,6 +947,9 @@ def _merge_frames(
811
947
  return values[::-1]
812
948
 
813
949
 
950
+ ##
951
+
952
+
814
953
  def _yield_header_lines(
815
954
  *,
816
955
  start: MaybeCallableDateTime | None = _START,
@@ -838,12 +977,108 @@ def _yield_header_lines(
838
977
  yield ""
839
978
 
840
979
 
980
+ ##
981
+
982
+
983
+ def _yield_formatted_frame_summary(
984
+ error: BaseException,
985
+ /,
986
+ *,
987
+ capture_locals: bool = False,
988
+ max_width: int = RICH_MAX_WIDTH,
989
+ indent_size: int = RICH_INDENT_SIZE,
990
+ max_length: int | None = RICH_MAX_LENGTH,
991
+ max_string: int | None = RICH_MAX_STRING,
992
+ max_depth: int | None = RICH_MAX_DEPTH,
993
+ expand_all: bool = RICH_EXPAND_ALL,
994
+ ) -> Iterator[str]:
995
+ """Yield the formatted frame summary lines."""
996
+ stack = TracebackException.from_exception(
997
+ error, capture_locals=capture_locals
998
+ ).stack
999
+ n = len(stack)
1000
+ for i, frame in enumerate(stack, start=1):
1001
+ num = f"{i}/{n}"
1002
+ first, *rest = _yield_frame_summary_lines(
1003
+ frame,
1004
+ max_width=max_width,
1005
+ indent_size=indent_size,
1006
+ max_length=max_length,
1007
+ max_string=max_string,
1008
+ max_depth=max_depth,
1009
+ expand_all=expand_all,
1010
+ )
1011
+ yield f"{num} | {first}"
1012
+ blank = "".join(repeat(" ", len(num)))
1013
+ for rest_i in rest:
1014
+ yield f"{blank} | {rest_i}"
1015
+ yield repr_error(error)
1016
+
1017
+
1018
+ def _yield_frame_summary_lines(
1019
+ frame: FrameSummary,
1020
+ /,
1021
+ *,
1022
+ max_width: int = RICH_MAX_WIDTH,
1023
+ indent_size: int = RICH_INDENT_SIZE,
1024
+ max_length: int | None = RICH_MAX_LENGTH,
1025
+ max_string: int | None = RICH_MAX_STRING,
1026
+ max_depth: int | None = RICH_MAX_DEPTH,
1027
+ expand_all: bool = RICH_EXPAND_ALL,
1028
+ ) -> Iterator[str]:
1029
+ module = _path_to_dots(frame.filename)
1030
+ yield f"{module}:{frame.lineno} | {frame.name} | {frame.line}"
1031
+ if frame.locals is not None:
1032
+ yield from yield_mapping_repr(
1033
+ frame.locals,
1034
+ _max_width=max_width,
1035
+ _indent_size=indent_size,
1036
+ _max_length=max_length,
1037
+ _max_string=max_string,
1038
+ _max_depth=max_depth,
1039
+ _expand_all=expand_all,
1040
+ )
1041
+
1042
+
1043
+ def _path_to_dots(path: PathLike, /) -> str:
1044
+ new_path: Path | None = None
1045
+ for pattern in [
1046
+ "site-packages",
1047
+ ".venv", # after site-packages
1048
+ "src",
1049
+ r"python\d+\.\d+",
1050
+ ]:
1051
+ if (new_path := _trim_path(path, pattern)) is not None:
1052
+ break
1053
+ path_use = Path(path) if new_path is None else new_path
1054
+ return ".".join(path_use.with_suffix("").parts)
1055
+
1056
+
1057
+ def _trim_path(path: PathLike, pattern: str, /) -> Path | None:
1058
+ parts = Path(path).parts
1059
+ compiled = re.compile(f"^{pattern}$")
1060
+ try:
1061
+ i = one(i for i, p in enumerate(parts) if compiled.search(p))
1062
+ except OneEmptyError:
1063
+ return None
1064
+ return Path(*parts[i + 1 :])
1065
+
1066
+
1067
+ @dataclass(kw_only=True, slots=True)
1068
+ class MakeExceptHookError(Exception):
1069
+ @override
1070
+ def __str__(self) -> str:
1071
+ return "No exception to log"
1072
+
1073
+
841
1074
  __all__ = [
842
1075
  "ExcChainTB",
843
1076
  "ExcGroupTB",
844
1077
  "ExcTB",
845
1078
  "RichTracebackFormatter",
1079
+ "format_exception_stack",
846
1080
  "get_rich_traceback",
1081
+ "make_except_hook",
847
1082
  "trace",
848
1083
  "yield_exceptions",
849
1084
  "yield_extended_frame_summaries",