dycw-utilities 0.129.8__tar.gz → 0.129.10__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.
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/PKG-INFO +1 -1
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/pyproject.toml +2 -2
- dycw_utilities-0.129.10/src/tests/test_git.py +27 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback.py +122 -2
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_whenever.py +16 -7
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/__init__.py +1 -1
- dycw_utilities-0.129.10/src/utilities/git.py +40 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/traceback.py +241 -6
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/.gitignore +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/LICENSE +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/README.md +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/click.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/http.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/math.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/os.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/period.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/random.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/re.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/string.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/text.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/types.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/version.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/utilities/zoneinfo.py +0 -0
@@ -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.
|
96
|
+
version = "0.129.10"
|
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.
|
335
|
+
current_version = "0.129.10"
|
336
336
|
|
337
337
|
[[tool.bumpversion.files]]
|
338
338
|
filename = "src/utilities/__init__.py"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
from hypothesis import given, settings
|
6
|
+
from pytest import raises
|
7
|
+
|
8
|
+
from utilities.git import GetRepoRootError, get_repo_root
|
9
|
+
from utilities.hypothesis import git_repos
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from pathlib import Path
|
13
|
+
|
14
|
+
|
15
|
+
class TestGetRepoRoot:
|
16
|
+
@given(repo=git_repos())
|
17
|
+
@settings(max_examples=1)
|
18
|
+
def test_main(self, *, repo: Path) -> None:
|
19
|
+
root = get_repo_root(path=repo)
|
20
|
+
expected = repo.resolve()
|
21
|
+
assert root == expected
|
22
|
+
|
23
|
+
def test_error(self, *, tmp_path: Path) -> None:
|
24
|
+
with raises(
|
25
|
+
GetRepoRootError, match="Path is not part of a `git` repository: .*"
|
26
|
+
):
|
27
|
+
_ = get_repo_root(path=tmp_path)
|
@@ -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
|
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
|
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
|
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
|
502
|
-
|
503
|
-
|
504
|
-
|
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)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from pathlib import Path
|
5
|
+
from re import IGNORECASE, search
|
6
|
+
from subprocess import PIPE, CalledProcessError, check_output
|
7
|
+
from typing import TYPE_CHECKING, override
|
8
|
+
|
9
|
+
from utilities.pathlib import PWD
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from utilities.types import PathLike
|
13
|
+
|
14
|
+
|
15
|
+
def get_repo_root(*, path: PathLike = PWD) -> Path:
|
16
|
+
"""Get the repo root."""
|
17
|
+
try:
|
18
|
+
output = check_output(
|
19
|
+
["git", "rev-parse", "--show-toplevel"], stderr=PIPE, cwd=path, text=True
|
20
|
+
)
|
21
|
+
except CalledProcessError as error:
|
22
|
+
# newer versions of git report "Not a git repository", whilst older
|
23
|
+
# versions report "not a git repository"
|
24
|
+
if search("fatal: not a git repository", error.stderr, flags=IGNORECASE):
|
25
|
+
raise GetRepoRootError(cwd=path) from error
|
26
|
+
raise # pragma: no cover
|
27
|
+
else:
|
28
|
+
return Path(output.strip("\n"))
|
29
|
+
|
30
|
+
|
31
|
+
@dataclass(kw_only=True, slots=True)
|
32
|
+
class GetRepoRootError(Exception):
|
33
|
+
cwd: PathLike
|
34
|
+
|
35
|
+
@override
|
36
|
+
def __str__(self) -> str:
|
37
|
+
return f"Path is not part of a `git` repository: {self.cwd}"
|
38
|
+
|
39
|
+
|
40
|
+
__all__ = ["GetRepoRootError", "get_repo_root"]
|
@@ -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
|
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",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_missing/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_missing/module.py
RENAMED
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/outer_1.py
RENAMED
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_with/outer_2.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_without/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_without/module_1.py
RENAMED
File without changes
|
{dycw_utilities-0.129.8 → dycw_utilities-0.129.10}/src/tests/modules/package_without/module_2.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|