dycw-utilities 0.129.6__tar.gz → 0.129.8__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.6 → dycw_utilities-0.129.8}/PKG-INFO +3 -4
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/pyproject.toml +6 -8
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/conftest.py +12 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_logging.py +13 -2
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_whenever.py +10 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/logging.py +46 -18
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/traceback.py +39 -8
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/whenever.py +64 -1
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/.gitignore +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/LICENSE +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/README.md +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/click.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/http.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/math.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/os.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/period.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/random.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/re.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/string.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/text.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/types.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/version.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/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.
|
3
|
+
Version: 0.129.8
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
@@ -93,12 +93,11 @@ Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-iterables'
|
|
93
93
|
Requires-Dist: whenever<0.9,>=0.8.4; extra == 'zzz-test-iterables'
|
94
94
|
Provides-Extra: zzz-test-jupyter
|
95
95
|
Requires-Dist: jupyterlab<4.3,>=4.2.0; extra == 'zzz-test-jupyter'
|
96
|
-
Requires-Dist: pandas<2.
|
96
|
+
Requires-Dist: pandas<2.4,>=2.3.0; extra == 'zzz-test-jupyter'
|
97
97
|
Requires-Dist: polars-lts-cpu<1.31,>=1.30.0; extra == 'zzz-test-jupyter'
|
98
98
|
Provides-Extra: zzz-test-logging
|
99
99
|
Requires-Dist: atomicwrites<1.5,>=1.4.1; extra == 'zzz-test-logging'
|
100
100
|
Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'zzz-test-logging'
|
101
|
-
Requires-Dist: concurrent-log-handler<0.10,>=0.9.26; extra == 'zzz-test-logging'
|
102
101
|
Requires-Dist: rich<14.1,>=14.0.0; extra == 'zzz-test-logging'
|
103
102
|
Requires-Dist: tomlkit<0.14,>=0.13.2; extra == 'zzz-test-logging'
|
104
103
|
Requires-Dist: tzlocal<5.4,>=5.3.1; extra == 'zzz-test-logging'
|
@@ -173,7 +172,7 @@ Requires-Dist: scipy<1.16,>=1.15.3; extra == 'zzz-test-scipy'
|
|
173
172
|
Provides-Extra: zzz-test-sentinel
|
174
173
|
Provides-Extra: zzz-test-shelve
|
175
174
|
Provides-Extra: zzz-test-slack-sdk
|
176
|
-
Requires-Dist: aiohttp<3.12.
|
175
|
+
Requires-Dist: aiohttp<3.12.10,>=3.12.9; extra == 'zzz-test-slack-sdk'
|
177
176
|
Requires-Dist: slack-sdk<3.36,>=3.35.0; extra == 'zzz-test-slack-sdk'
|
178
177
|
Provides-Extra: zzz-test-socket
|
179
178
|
Provides-Extra: zzz-test-sqlalchemy
|
@@ -8,7 +8,7 @@ requires = ["hatchling"]
|
|
8
8
|
# dependency groups
|
9
9
|
[dependency-groups]
|
10
10
|
dev = [
|
11
|
-
"aiohttp >= 3.12.
|
11
|
+
"aiohttp >= 3.12.9, < 3.12.10", # for slack
|
12
12
|
"aiosqlite >= 0.21.0, < 0.22",
|
13
13
|
"altair >= 5.5.0, < 5.6",
|
14
14
|
"asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
|
@@ -17,7 +17,6 @@ dev = [
|
|
17
17
|
"cachetools >= 5.5.2, < 5.6",
|
18
18
|
"click >= 8.2.1, < 8.3",
|
19
19
|
"coloredlogs >= 15.0.1, < 15.1",
|
20
|
-
"concurrent-log-handler >= 0.9.26, < 0.10",
|
21
20
|
"cryptography >= 45.0.3, < 45.1",
|
22
21
|
"cvxpy >= 1.6.5, < 1.7",
|
23
22
|
"eventkit >= 1.0.3, < 1.1",
|
@@ -54,7 +53,7 @@ dev = [
|
|
54
53
|
"python-dotenv >= 1.1.0, < 1.2",
|
55
54
|
"redis >= 6.2.0, < 6.3",
|
56
55
|
"rich >= 14.0.0, < 14.1",
|
57
|
-
"scikit-learn >= 1.
|
56
|
+
"scikit-learn >= 1.7.0, < 1.8",
|
58
57
|
"scipy >= 1.15.3, < 1.16",
|
59
58
|
"slack-sdk >= 3.35.0, < 3.36",
|
60
59
|
"sqlalchemy >= 2.0.41, < 2.1",
|
@@ -94,7 +93,7 @@ dependencies = [
|
|
94
93
|
name = "dycw-utilities"
|
95
94
|
readme = "README.md"
|
96
95
|
requires-python = ">= 3.12"
|
97
|
-
version = "0.129.
|
96
|
+
version = "0.129.8"
|
98
97
|
|
99
98
|
[project.optional-dependencies]
|
100
99
|
test = [
|
@@ -191,13 +190,12 @@ zzz-test-iterables = [
|
|
191
190
|
]
|
192
191
|
zzz-test-jupyter = [
|
193
192
|
"jupyterlab >= 4.2.0, < 4.3",
|
194
|
-
"pandas >= 2.
|
193
|
+
"pandas >= 2.3.0, < 2.4",
|
195
194
|
"polars-lts-cpu >= 1.30.0, < 1.31",
|
196
195
|
]
|
197
196
|
zzz-test-logging = [
|
198
197
|
"atomicwrites >= 1.4.1, < 1.5",
|
199
198
|
"coloredlogs >= 15.0.1, < 15.1",
|
200
|
-
"concurrent-log-handler >= 0.9.26, < 0.10",
|
201
199
|
"rich >= 14.0.0, < 14.1",
|
202
200
|
"tomlkit >= 0.13.2, < 0.14",
|
203
201
|
"tzlocal >= 5.3.1, < 5.4",
|
@@ -270,7 +268,7 @@ zzz-test-scipy = ["scipy >= 1.15.3, < 1.16"]
|
|
270
268
|
zzz-test-sentinel = []
|
271
269
|
zzz-test-shelve = []
|
272
270
|
zzz-test-slack-sdk = [
|
273
|
-
"aiohttp >= 3.12.
|
271
|
+
"aiohttp >= 3.12.9, < 3.12.10", # for slack
|
274
272
|
"slack-sdk >= 3.35.0, < 3.36",
|
275
273
|
]
|
276
274
|
zzz-test-socket = []
|
@@ -334,7 +332,7 @@ zzz-test-zoneinfo = [
|
|
334
332
|
# bump-my-version
|
335
333
|
[tool.bumpversion]
|
336
334
|
allow_dirty = true
|
337
|
-
current_version = "0.129.
|
335
|
+
current_version = "0.129.8"
|
338
336
|
|
339
337
|
[[tool.bumpversion.files]]
|
340
338
|
filename = "src/utilities/__init__.py"
|
@@ -39,6 +39,8 @@ def traceback_func_chain() -> Pattern[str]:
|
|
39
39
|
strip_and_dedent(
|
40
40
|
r"""
|
41
41
|
Date/time \| .+
|
42
|
+
Started \| .+
|
43
|
+
Duration \| .+
|
42
44
|
User \| .+
|
43
45
|
Host \| .+
|
44
46
|
Version \|\s
|
@@ -98,6 +100,8 @@ def traceback_func_one() -> Pattern[str]:
|
|
98
100
|
strip_and_dedent(
|
99
101
|
r"""
|
100
102
|
Date/time \| .+
|
103
|
+
Started \| .+
|
104
|
+
Duration \| .+
|
101
105
|
User \| .+
|
102
106
|
Host \| .+
|
103
107
|
Version \|\s
|
@@ -134,6 +138,8 @@ def traceback_func_many_long() -> Pattern[str]:
|
|
134
138
|
strip_and_dedent(
|
135
139
|
r"""
|
136
140
|
Date/time \| .+
|
141
|
+
Started \| .+
|
142
|
+
Duration \| .+
|
137
143
|
User \| .+
|
138
144
|
Host \| .+
|
139
145
|
Version \|\s
|
@@ -192,6 +198,8 @@ def traceback_func_many_short() -> Pattern[str]:
|
|
192
198
|
strip_and_dedent(
|
193
199
|
r"""
|
194
200
|
Date/time \| .+
|
201
|
+
Started \| .+
|
202
|
+
Duration \| .+
|
195
203
|
User \| .+
|
196
204
|
Host \| .+
|
197
205
|
Version \|\s
|
@@ -228,6 +236,8 @@ def traceback_func_task_group_one() -> Pattern[str]:
|
|
228
236
|
strip_and_dedent(
|
229
237
|
r"""
|
230
238
|
Date/time \| .+
|
239
|
+
Started \| .+
|
240
|
+
Duration \| .+
|
231
241
|
User \| .+
|
232
242
|
Host \| .+
|
233
243
|
Version \|\s
|
@@ -287,6 +297,8 @@ def traceback_func_two() -> Pattern[str]:
|
|
287
297
|
strip_and_dedent(
|
288
298
|
r"""
|
289
299
|
Date/time \| .+
|
300
|
+
Started \| .+
|
301
|
+
Duration \| .+
|
290
302
|
User \| .+
|
291
303
|
Host \| .+
|
292
304
|
Version \|\s
|
@@ -73,9 +73,13 @@ class TestAddFilters:
|
|
73
73
|
|
74
74
|
class TestBasicConfig:
|
75
75
|
@mark.parametrize("log", [param(True), param(False)])
|
76
|
-
|
76
|
+
@mark.parametrize("whenever", [param(True), param(False)])
|
77
|
+
@mark.parametrize("plain", [param(True), param(False)])
|
78
|
+
def test_main(
|
79
|
+
self, *, caplog: LogCaptureFixture, log: bool, whenever: bool, plain: bool
|
80
|
+
) -> None:
|
77
81
|
logger = unique_str() if log else None
|
78
|
-
basic_config(logger=
|
82
|
+
basic_config(obj=logger, whenever=whenever, plain=plain)
|
79
83
|
logger_use = getLogger()
|
80
84
|
logger_use.warning("message")
|
81
85
|
assert "message" in caplog.messages
|
@@ -484,6 +488,13 @@ class TestSizeAndTimeRotatingFileHandler:
|
|
484
488
|
content = fh.read()
|
485
489
|
assert content == "message\n"
|
486
490
|
|
491
|
+
@skipif_windows
|
492
|
+
def test_create_parents(self, *, tmp_path: Path) -> None:
|
493
|
+
logger = getLogger(unique_str())
|
494
|
+
filename = tmp_path.joinpath("foo", "bar", "bar", "log")
|
495
|
+
logger.addHandler(SizeAndTimeRotatingFileHandler(filename=filename))
|
496
|
+
assert filename.exists()
|
497
|
+
|
487
498
|
@skipif_windows
|
488
499
|
def test_size(self, *, tmp_path: Path) -> None:
|
489
500
|
logger = getLogger(unique_str())
|
@@ -2,6 +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
6
|
from re import escape
|
6
7
|
from typing import TYPE_CHECKING
|
7
8
|
from zoneinfo import ZoneInfo
|
@@ -40,6 +41,7 @@ from utilities.hypothesis import (
|
|
40
41
|
timedeltas_2w,
|
41
42
|
zoned_datetimes,
|
42
43
|
)
|
44
|
+
from utilities.text import unique_str
|
43
45
|
from utilities.tzdata import HongKong
|
44
46
|
from utilities.whenever import (
|
45
47
|
MAX_SERIALIZABLE_TIMEDELTA,
|
@@ -60,6 +62,7 @@ from utilities.whenever import (
|
|
60
62
|
SerializePlainDateTimeError,
|
61
63
|
SerializeTimeDeltaError,
|
62
64
|
SerializeZonedDateTimeError,
|
65
|
+
WheneverLogRecord,
|
63
66
|
_CheckValidZonedDateTimeUnequalError,
|
64
67
|
_EnsureTimedeltaNanosecondError,
|
65
68
|
_EnsureTimedeltaParseError,
|
@@ -492,3 +495,10 @@ class TestToDateTimeDelta:
|
|
492
495
|
_ToDateTimeDeltaError, match="Unable to create DateTimeDelta; got .*"
|
493
496
|
):
|
494
497
|
_ = _to_datetime_delta(timedelta)
|
498
|
+
|
499
|
+
|
500
|
+
class TestWheneverLogRecord:
|
501
|
+
def test_main(self) -> None:
|
502
|
+
logger = getLogger(unique_str())
|
503
|
+
setLogRecordFactory(WheneverLogRecord)
|
504
|
+
logger.warning("message")
|
@@ -107,8 +107,9 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
107
107
|
utc: bool = False,
|
108
108
|
atTime: dt.time | None = None,
|
109
109
|
) -> None:
|
110
|
-
|
111
|
-
|
110
|
+
path = Path(filename)
|
111
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
112
|
+
super().__init__(path, mode, encoding=encoding, delay=delay, errors=errors)
|
112
113
|
self._max_bytes = maxBytes if maxBytes >= 1 else None
|
113
114
|
self._backup_count = backupCount if backupCount >= 1 else None
|
114
115
|
self._filename = Path(self.baseFilename)
|
@@ -117,7 +118,7 @@ class SizeAndTimeRotatingFileHandler(BaseRotatingHandler):
|
|
117
118
|
self._suffix = self._filename.suffix
|
118
119
|
self._patterns = _compute_rollover_patterns(self._stem, self._suffix)
|
119
120
|
self._time_handler = TimedRotatingFileHandler(
|
120
|
-
|
121
|
+
path,
|
121
122
|
when=when,
|
122
123
|
interval=interval,
|
123
124
|
backupCount=backupCount,
|
@@ -415,26 +416,53 @@ def add_filters(handler: Handler, /, *filters: _FilterType) -> None:
|
|
415
416
|
|
416
417
|
def basic_config(
|
417
418
|
*,
|
418
|
-
|
419
|
+
obj: LoggerOrName | Handler | None = None,
|
419
420
|
format_: str = "{asctime} | {name} | {levelname:8} | {message}",
|
421
|
+
whenever: bool = False,
|
420
422
|
level: LogLevel = "INFO",
|
423
|
+
plain: bool = False,
|
421
424
|
) -> None:
|
422
425
|
"""Do the basic config."""
|
426
|
+
if whenever:
|
427
|
+
format_ = format_.replace("{asctime}", "{zoned_datetime}")
|
423
428
|
datefmt = maybe_sub_pct_y("%Y-%m-%d %H:%M:%S")
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
429
|
+
match obj:
|
430
|
+
case None:
|
431
|
+
basicConfig(format=format_, datefmt=datefmt, style="{", level=level)
|
432
|
+
case Logger() as logger:
|
433
|
+
logger.setLevel(level)
|
434
|
+
logger.addHandler(handler := StreamHandler())
|
435
|
+
basic_config(
|
436
|
+
obj=handler,
|
437
|
+
format_=format_,
|
438
|
+
whenever=whenever,
|
439
|
+
level=level,
|
440
|
+
plain=plain,
|
441
|
+
)
|
442
|
+
case str() as name:
|
443
|
+
basic_config(
|
444
|
+
obj=get_logger(logger=name),
|
445
|
+
format_=format_,
|
446
|
+
whenever=whenever,
|
447
|
+
level=level,
|
448
|
+
plain=plain,
|
449
|
+
)
|
450
|
+
case Handler() as handler:
|
451
|
+
handler.setLevel(level)
|
452
|
+
if plain:
|
453
|
+
formatter = Formatter(fmt=format_, datefmt=datefmt, style="{")
|
454
|
+
else:
|
455
|
+
try:
|
456
|
+
from coloredlogs import ColoredFormatter
|
457
|
+
except ModuleNotFoundError: # pragma: no cover
|
458
|
+
formatter = Formatter(fmt=format_, datefmt=datefmt, style="{")
|
459
|
+
else:
|
460
|
+
formatter = ColoredFormatter(
|
461
|
+
fmt=format_, datefmt=datefmt, style="{"
|
462
|
+
)
|
463
|
+
handler.setFormatter(formatter)
|
464
|
+
case _ as never:
|
465
|
+
assert_never(never)
|
438
466
|
|
439
467
|
|
440
468
|
##
|
@@ -26,6 +26,7 @@ from typing import (
|
|
26
26
|
runtime_checkable,
|
27
27
|
)
|
28
28
|
|
29
|
+
from utilities.datetime import get_datetime, get_now
|
29
30
|
from utilities.errors import ImpossibleCaseError
|
30
31
|
from utilities.functions import (
|
31
32
|
ensure_not_none,
|
@@ -45,8 +46,9 @@ from utilities.reprlib import (
|
|
45
46
|
yield_call_args_repr,
|
46
47
|
yield_mapping_repr,
|
47
48
|
)
|
48
|
-
from utilities.types import TBaseException, TCallable
|
49
|
+
from utilities.types import MaybeCallableDateTime, TBaseException, TCallable
|
49
50
|
from utilities.version import get_version
|
51
|
+
from utilities.whenever import serialize_duration
|
50
52
|
|
51
53
|
if TYPE_CHECKING:
|
52
54
|
from collections.abc import Callable, Iterable, Iterator
|
@@ -60,6 +62,7 @@ if TYPE_CHECKING:
|
|
60
62
|
_T = TypeVar("_T")
|
61
63
|
_CALL_ARGS = "_CALL_ARGS"
|
62
64
|
_INDENT = 4 * " "
|
65
|
+
_START = get_now()
|
63
66
|
|
64
67
|
|
65
68
|
##
|
@@ -78,6 +81,7 @@ class RichTracebackFormatter(Formatter):
|
|
78
81
|
/,
|
79
82
|
*,
|
80
83
|
defaults: StrMapping | None = None,
|
84
|
+
start: MaybeCallableDateTime | None = _START,
|
81
85
|
version: MaybeCallableVersionLike | None = None,
|
82
86
|
max_width: int = RICH_MAX_WIDTH,
|
83
87
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -89,7 +93,8 @@ class RichTracebackFormatter(Formatter):
|
|
89
93
|
post: Callable[[str], str] | None = None,
|
90
94
|
) -> None:
|
91
95
|
super().__init__(fmt, datefmt, style, validate, defaults=defaults)
|
92
|
-
self.
|
96
|
+
self._start = get_datetime(datetime=start)
|
97
|
+
self._version = get_version(version=version)
|
93
98
|
self._max_width = max_width
|
94
99
|
self._indent_size = indent_size
|
95
100
|
self._max_length = max_length
|
@@ -110,6 +115,7 @@ class RichTracebackFormatter(Formatter):
|
|
110
115
|
exc_value = ensure_not_none(exc_value, desc="exc_value")
|
111
116
|
error = get_rich_traceback(
|
112
117
|
exc_value,
|
118
|
+
start=self._start,
|
113
119
|
version=self._version,
|
114
120
|
max_width=self._max_width,
|
115
121
|
indent_size=self._indent_size,
|
@@ -263,6 +269,7 @@ class ExcChainTB(Generic[TBaseException]):
|
|
263
269
|
errors: list[
|
264
270
|
ExcGroupTB[TBaseException] | ExcTB[TBaseException] | TBaseException
|
265
271
|
] = field(default_factory=list)
|
272
|
+
start: MaybeCallableDateTime | None = field(default=_START, repr=False)
|
266
273
|
version: MaybeCallableVersionLike | None = field(default=None, repr=False)
|
267
274
|
max_width: int = RICH_MAX_WIDTH
|
268
275
|
indent_size: int = RICH_INDENT_SIZE
|
@@ -292,7 +299,7 @@ class ExcChainTB(Generic[TBaseException]):
|
|
292
299
|
"""Format the traceback."""
|
293
300
|
lines: list[str] = []
|
294
301
|
if header: # pragma: no cover
|
295
|
-
lines.extend(_yield_header_lines(version=self.version))
|
302
|
+
lines.extend(_yield_header_lines(start=self.start, version=self.version))
|
296
303
|
total = len(self.errors)
|
297
304
|
for i, errors in enumerate(self.errors, start=1):
|
298
305
|
lines.append(f"Exception chain {i}/{total}:")
|
@@ -315,6 +322,7 @@ class ExcGroupTB(Generic[TBaseException]):
|
|
315
322
|
errors: list[
|
316
323
|
ExcGroupTB[TBaseException] | ExcTB[TBaseException] | TBaseException
|
317
324
|
] = field(default_factory=list)
|
325
|
+
start: MaybeCallableDateTime | None = field(default=_START, repr=False)
|
318
326
|
version: MaybeCallableVersionLike | None = field(default=None, repr=False)
|
319
327
|
max_width: int = RICH_MAX_WIDTH
|
320
328
|
indent_size: int = RICH_INDENT_SIZE
|
@@ -333,7 +341,7 @@ class ExcGroupTB(Generic[TBaseException]):
|
|
333
341
|
"""Format the traceback."""
|
334
342
|
lines: list[str] = [] # skipif-ci
|
335
343
|
if header: # pragma: no cover
|
336
|
-
lines.extend(_yield_header_lines(version=self.version))
|
344
|
+
lines.extend(_yield_header_lines(start=self.start, version=self.version))
|
337
345
|
lines.append("Exception group:") # skipif-ci
|
338
346
|
match self.exc_group: # skipif-ci
|
339
347
|
case ExcTB() as exc_tb:
|
@@ -363,6 +371,7 @@ class ExcTB(Generic[TBaseException]):
|
|
363
371
|
|
364
372
|
frames: list[_Frame] = field(default_factory=list)
|
365
373
|
error: TBaseException
|
374
|
+
start: MaybeCallableDateTime | None = field(default=_START, repr=False)
|
366
375
|
version: MaybeCallableVersionLike | None = field(default=None, repr=False)
|
367
376
|
max_width: int = RICH_MAX_WIDTH
|
368
377
|
indent_size: int = RICH_INDENT_SIZE
|
@@ -391,7 +400,7 @@ class ExcTB(Generic[TBaseException]):
|
|
391
400
|
total = len(self)
|
392
401
|
lines: list[str] = []
|
393
402
|
if header: # pragma: no cover
|
394
|
-
lines.extend(_yield_header_lines(version=self.version))
|
403
|
+
lines.extend(_yield_header_lines(start=self.start, version=self.version))
|
395
404
|
for i, frame in enumerate(self.frames):
|
396
405
|
is_head = i < total - 1
|
397
406
|
lines.append(
|
@@ -485,6 +494,7 @@ def get_rich_traceback(
|
|
485
494
|
error: TBaseException,
|
486
495
|
/,
|
487
496
|
*,
|
497
|
+
start: MaybeCallableDateTime | None = _START,
|
488
498
|
version: MaybeCallableVersionLike | None = None,
|
489
499
|
max_width: int = RICH_MAX_WIDTH,
|
490
500
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -506,6 +516,7 @@ def get_rich_traceback(
|
|
506
516
|
err_recast = cast("TBaseException", err)
|
507
517
|
return _get_rich_traceback_non_chain(
|
508
518
|
err_recast,
|
519
|
+
start=start,
|
509
520
|
version=version,
|
510
521
|
max_width=max_width,
|
511
522
|
indent_size=indent_size,
|
@@ -520,6 +531,7 @@ def get_rich_traceback(
|
|
520
531
|
errors=[
|
521
532
|
_get_rich_traceback_non_chain(
|
522
533
|
e,
|
534
|
+
start=start,
|
523
535
|
version=version,
|
524
536
|
max_width=max_width,
|
525
537
|
indent_size=indent_size,
|
@@ -530,6 +542,7 @@ def get_rich_traceback(
|
|
530
542
|
)
|
531
543
|
for e in errs_recast
|
532
544
|
],
|
545
|
+
start=start,
|
533
546
|
version=version,
|
534
547
|
max_width=max_width,
|
535
548
|
indent_size=indent_size,
|
@@ -544,6 +557,7 @@ def _get_rich_traceback_non_chain(
|
|
544
557
|
error: ExceptionGroup[Any] | TBaseException,
|
545
558
|
/,
|
546
559
|
*,
|
560
|
+
start: MaybeCallableDateTime | None = _START,
|
547
561
|
version: MaybeCallableVersionLike | None = None,
|
548
562
|
max_width: int = RICH_MAX_WIDTH,
|
549
563
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -567,6 +581,7 @@ def _get_rich_traceback_non_chain(
|
|
567
581
|
errors = [
|
568
582
|
_get_rich_traceback_non_chain(
|
569
583
|
e,
|
584
|
+
start=start,
|
570
585
|
version=version,
|
571
586
|
max_width=max_width,
|
572
587
|
indent_size=indent_size,
|
@@ -580,6 +595,7 @@ def _get_rich_traceback_non_chain(
|
|
580
595
|
return ExcGroupTB(
|
581
596
|
exc_group=exc_group_or_exc_tb,
|
582
597
|
errors=errors,
|
598
|
+
start=start,
|
583
599
|
version=version,
|
584
600
|
max_width=max_width,
|
585
601
|
indent_size=indent_size,
|
@@ -591,6 +607,7 @@ def _get_rich_traceback_non_chain(
|
|
591
607
|
case BaseException() as base_exc:
|
592
608
|
return _get_rich_traceback_base_one(
|
593
609
|
base_exc,
|
610
|
+
start=start,
|
594
611
|
version=version,
|
595
612
|
max_width=max_width,
|
596
613
|
indent_size=indent_size,
|
@@ -607,6 +624,7 @@ def _get_rich_traceback_base_one(
|
|
607
624
|
error: TBaseException,
|
608
625
|
/,
|
609
626
|
*,
|
627
|
+
start: MaybeCallableDateTime | None = _START,
|
610
628
|
version: MaybeCallableVersionLike | None = None,
|
611
629
|
max_width: int = RICH_MAX_WIDTH,
|
612
630
|
indent_size: int = RICH_INDENT_SIZE,
|
@@ -638,6 +656,7 @@ def _get_rich_traceback_base_one(
|
|
638
656
|
return ExcTB(
|
639
657
|
frames=frames,
|
640
658
|
error=error,
|
659
|
+
start=start,
|
641
660
|
version=version,
|
642
661
|
max_width=max_width,
|
643
662
|
indent_size=indent_size,
|
@@ -793,13 +812,25 @@ def _merge_frames(
|
|
793
812
|
|
794
813
|
|
795
814
|
def _yield_header_lines(
|
796
|
-
*,
|
815
|
+
*,
|
816
|
+
start: MaybeCallableDateTime | None = _START,
|
817
|
+
version: MaybeCallableVersionLike | None = None,
|
797
818
|
) -> Iterator[str]:
|
798
819
|
"""Yield the header lines."""
|
799
|
-
from utilities.tzlocal import get_now_local
|
820
|
+
from utilities.tzlocal import get_local_time_zone, get_now_local
|
800
821
|
from utilities.whenever import serialize_zoned_datetime
|
801
822
|
|
802
|
-
|
823
|
+
now = get_now_local()
|
824
|
+
start_use = get_datetime(datetime=start)
|
825
|
+
start_use = (
|
826
|
+
None if start_use is None else start_use.astimezone(get_local_time_zone())
|
827
|
+
)
|
828
|
+
yield f"Date/time | {serialize_zoned_datetime(now)}"
|
829
|
+
start_str = "" if start_use is None else serialize_zoned_datetime(start_use)
|
830
|
+
yield f"Started | {start_str}"
|
831
|
+
duration = None if start_use is None else (now - start_use)
|
832
|
+
duration_str = "" if duration is None else serialize_duration(duration)
|
833
|
+
yield f"Duration | {duration_str}"
|
803
834
|
yield f"User | {getuser()}"
|
804
835
|
yield f"Host | {gethostname()}"
|
805
836
|
version_use = "" if version is None else get_version(version=version)
|
@@ -4,7 +4,9 @@ import datetime as dt
|
|
4
4
|
import re
|
5
5
|
from contextlib import suppress
|
6
6
|
from dataclasses import dataclass
|
7
|
-
from
|
7
|
+
from functools import cache
|
8
|
+
from logging import LogRecord
|
9
|
+
from typing import TYPE_CHECKING, Any, override
|
8
10
|
|
9
11
|
from whenever import (
|
10
12
|
Date,
|
@@ -33,6 +35,8 @@ from utilities.re import (
|
|
33
35
|
from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
|
34
36
|
|
35
37
|
if TYPE_CHECKING:
|
38
|
+
from zoneinfo import ZoneInfo
|
39
|
+
|
36
40
|
from utilities.types import (
|
37
41
|
DateLike,
|
38
42
|
DateTimeLike,
|
@@ -561,6 +565,64 @@ class SerializeZonedDateTimeError(Exception):
|
|
561
565
|
##
|
562
566
|
|
563
567
|
|
568
|
+
class WheneverLogRecord(LogRecord):
|
569
|
+
"""Log record powered by `whenever`."""
|
570
|
+
|
571
|
+
zoned_datetime: str
|
572
|
+
|
573
|
+
@override
|
574
|
+
def __init__(
|
575
|
+
self,
|
576
|
+
name: str,
|
577
|
+
level: int,
|
578
|
+
pathname: str,
|
579
|
+
lineno: int,
|
580
|
+
msg: object,
|
581
|
+
args: Any,
|
582
|
+
exc_info: Any,
|
583
|
+
func: str | None = None,
|
584
|
+
sinfo: str | None = None,
|
585
|
+
) -> None:
|
586
|
+
super().__init__(
|
587
|
+
name, level, pathname, lineno, msg, args, exc_info, func, sinfo
|
588
|
+
)
|
589
|
+
length = self._get_length()
|
590
|
+
plain = format(self._get_now().to_plain().format_common_iso(), f"{length}s")
|
591
|
+
time_zone = self._get_time_zone_key()
|
592
|
+
self.zoned_datetime = f"{plain}[{time_zone}]"
|
593
|
+
|
594
|
+
@classmethod
|
595
|
+
@cache
|
596
|
+
def _get_time_zone(cls) -> ZoneInfo:
|
597
|
+
"""Get the local timezone."""
|
598
|
+
try:
|
599
|
+
from utilities.tzlocal import get_local_time_zone
|
600
|
+
except ModuleNotFoundError: # pragma: no cover
|
601
|
+
return UTC
|
602
|
+
return get_local_time_zone()
|
603
|
+
|
604
|
+
@classmethod
|
605
|
+
@cache
|
606
|
+
def _get_time_zone_key(cls) -> str:
|
607
|
+
"""Get the local timezone as a string."""
|
608
|
+
return cls._get_time_zone().key
|
609
|
+
|
610
|
+
@classmethod
|
611
|
+
@cache
|
612
|
+
def _get_length(cls) -> int:
|
613
|
+
"""Get maximum length of a formatted string."""
|
614
|
+
now = cls._get_now().replace(nanosecond=1000).to_plain()
|
615
|
+
return len(now.format_common_iso())
|
616
|
+
|
617
|
+
@classmethod
|
618
|
+
def _get_now(cls) -> ZonedDateTime:
|
619
|
+
"""Get the current zoned datetime."""
|
620
|
+
return ZonedDateTime.now(cls._get_time_zone().key)
|
621
|
+
|
622
|
+
|
623
|
+
##
|
624
|
+
|
625
|
+
|
564
626
|
def _to_datetime_delta(timedelta: dt.timedelta, /) -> DateTimeDelta:
|
565
627
|
"""Serialize a timedelta."""
|
566
628
|
total_microseconds = datetime_duration_to_microseconds(timedelta)
|
@@ -610,6 +672,7 @@ __all__ = [
|
|
610
672
|
"SerializePlainDateTimeError",
|
611
673
|
"SerializeTimeDeltaError",
|
612
674
|
"SerializeZonedDateTimeError",
|
675
|
+
"WheneverLogRecord",
|
613
676
|
"check_valid_zoned_datetime",
|
614
677
|
"ensure_date",
|
615
678
|
"ensure_datetime",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_missing/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_missing/module.py
RENAMED
File without changes
|
{dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_with/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_without/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/src/tests/modules/package_without/module_1.py
RENAMED
File without changes
|
{dycw_utilities-0.129.6 → dycw_utilities-0.129.8}/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
|
File without changes
|