dycw-utilities 0.131.12__tar.gz → 0.131.13__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.131.12 → dycw_utilities-0.131.13}/PKG-INFO +1 -1
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/pyproject.toml +2 -2
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_atools.py +6 -5
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_cachetools.py +22 -9
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_hypothesis.py +0 -55
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_orjson.py +2 -1
- dycw_utilities-0.131.13/src/tests/test_period.py +259 -0
- dycw_utilities-0.131.13/src/tests/test_tzdata.py +14 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_zoneinfo.py +3 -5
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/atools.py +7 -9
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/cachetools.py +8 -10
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/hypothesis.py +0 -136
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/orjson.py +1 -1
- dycw_utilities-0.131.13/src/utilities/period.py +154 -0
- dycw_utilities-0.131.13/src/utilities/tzdata.py +11 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/whenever2.py +4 -3
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/zoneinfo.py +2 -2
- dycw_utilities-0.131.12/src/tests/test_period.py +0 -387
- dycw_utilities-0.131.12/src/tests/test_tzdata.py +0 -60
- dycw_utilities-0.131.12/src/utilities/period.py +0 -324
- dycw_utilities-0.131.12/src/utilities/tzdata.py +0 -63
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/.gitignore +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/LICENSE +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/README.md +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_aiolimiter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_inflect.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_whenever2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/aiolimiter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/click.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/git.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/http.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/math.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/os.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/random.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/re.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/string.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/text.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/types.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/version.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.13}/src/utilities/zipfile.py +0 -0
@@ -94,7 +94,7 @@ dependencies = [
|
|
94
94
|
name = "dycw-utilities"
|
95
95
|
readme = "README.md"
|
96
96
|
requires-python = ">= 3.12"
|
97
|
-
version = "0.131.
|
97
|
+
version = "0.131.13"
|
98
98
|
|
99
99
|
[project.optional-dependencies]
|
100
100
|
logging = [
|
@@ -121,7 +121,7 @@ test = [
|
|
121
121
|
# bump-my-version
|
122
122
|
[tool.bumpversion]
|
123
123
|
allow_dirty = true
|
124
|
-
current_version = "0.131.
|
124
|
+
current_version = "0.131.13"
|
125
125
|
|
126
126
|
[[tool.bumpversion.files]]
|
127
127
|
filename = "src/utilities/__init__.py"
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from asyncio import
|
4
|
-
|
3
|
+
from utilities.asyncio import sleep_dur
|
5
4
|
from utilities.atools import call_memoized
|
5
|
+
from utilities.whenever2 import SECOND
|
6
6
|
|
7
7
|
|
8
8
|
class TestCallMemoized:
|
@@ -20,6 +20,7 @@ class TestCallMemoized:
|
|
20
20
|
|
21
21
|
async def test_refresh(self) -> None:
|
22
22
|
counter = 0
|
23
|
+
delta = 0.05 * SECOND
|
23
24
|
|
24
25
|
async def increment() -> int:
|
25
26
|
nonlocal counter
|
@@ -27,9 +28,9 @@ class TestCallMemoized:
|
|
27
28
|
return counter
|
28
29
|
|
29
30
|
for _ in range(2):
|
30
|
-
assert (await call_memoized(increment,
|
31
|
+
assert (await call_memoized(increment, delta)) == 1
|
31
32
|
assert counter == 1
|
32
|
-
await
|
33
|
+
await sleep_dur(duration=2 * delta)
|
33
34
|
for _ in range(2):
|
34
|
-
assert (await call_memoized(increment,
|
35
|
+
assert (await call_memoized(increment, delta)) == 2
|
35
36
|
assert counter == 2
|
@@ -1,20 +1,32 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from
|
3
|
+
from typing import TYPE_CHECKING
|
4
4
|
|
5
5
|
from hypothesis import example, given
|
6
|
-
from hypothesis.strategies import
|
6
|
+
from hypothesis.strategies import integers, none
|
7
7
|
|
8
|
+
from utilities.asyncio import sleep_dur
|
8
9
|
from utilities.cachetools import TTLSet, cache
|
10
|
+
from utilities.hypothesis import time_deltas_whenever
|
11
|
+
from utilities.whenever2 import SECOND
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from whenever import TimeDelta
|
9
15
|
|
10
16
|
|
11
17
|
class TestCache:
|
12
18
|
@example(max_size=None, max_duration=None)
|
13
|
-
@example(max_size=None, max_duration=
|
19
|
+
@example(max_size=None, max_duration=SECOND)
|
14
20
|
@example(max_size=1, max_duration=None)
|
15
|
-
@example(max_size=1, max_duration=
|
16
|
-
@given(
|
17
|
-
|
21
|
+
@example(max_size=1, max_duration=SECOND)
|
22
|
+
@given(
|
23
|
+
max_size=integers(1, 10) | none(),
|
24
|
+
max_duration=time_deltas_whenever(
|
25
|
+
min_value=0.1 * SECOND, max_value=10.0 * SECOND
|
26
|
+
)
|
27
|
+
| none(),
|
28
|
+
)
|
29
|
+
def test_main(self, *, max_size: int, max_duration: TimeDelta) -> None:
|
18
30
|
counter = 0
|
19
31
|
|
20
32
|
@cache(max_size=max_size, max_duration=max_duration)
|
@@ -48,10 +60,11 @@ class TestTTLSet:
|
|
48
60
|
set_ = TTLSet(range(3))
|
49
61
|
assert len(set_) == 3
|
50
62
|
|
51
|
-
def test_max_duration(self) -> None:
|
52
|
-
|
63
|
+
async def test_max_duration(self) -> None:
|
64
|
+
delta = 0.1 * SECOND
|
65
|
+
set_ = TTLSet(range(3), max_duration=delta)
|
53
66
|
assert set_ == {0, 1, 2}
|
54
|
-
|
67
|
+
await sleep_dur(duration=2 * delta)
|
55
68
|
assert set_ == set()
|
56
69
|
|
57
70
|
def test_max_size(self) -> None:
|
@@ -68,8 +68,6 @@ from utilities.hypothesis import (
|
|
68
68
|
int_arrays,
|
69
69
|
lists_fixed_length,
|
70
70
|
min_and_max_datetimes,
|
71
|
-
min_and_maybe_max_datetimes,
|
72
|
-
min_and_maybe_max_sizes,
|
73
71
|
months,
|
74
72
|
namespace_mixins,
|
75
73
|
numbers,
|
@@ -729,59 +727,6 @@ class TestMinAndMaxDateTimes:
|
|
729
727
|
assert max_datetime == max_value
|
730
728
|
|
731
729
|
|
732
|
-
class TestMinAndMaybeMaxDateTimes:
|
733
|
-
@given(
|
734
|
-
data=data(),
|
735
|
-
min_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
|
736
|
-
max_value=zoned_datetimes()
|
737
|
-
| none()
|
738
|
-
| sentinels()
|
739
|
-
| just(zoned_datetimes() | none() | sentinels()),
|
740
|
-
)
|
741
|
-
def test_main(
|
742
|
-
self,
|
743
|
-
*,
|
744
|
-
data: DataObject,
|
745
|
-
min_value: MaybeSearchStrategy[dt.datetime | None],
|
746
|
-
max_value: MaybeSearchStrategy[dt.datetime | None | Sentinel],
|
747
|
-
) -> None:
|
748
|
-
min_datetime, max_datetime = data.draw(
|
749
|
-
min_and_maybe_max_datetimes(min_value=min_value, max_value=max_value)
|
750
|
-
)
|
751
|
-
assert (max_datetime is None) or (min_datetime <= max_datetime)
|
752
|
-
if isinstance(min_value, dt.datetime):
|
753
|
-
assert min_datetime == min_value
|
754
|
-
if isinstance(max_value, dt.datetime) or (max_value is None):
|
755
|
-
assert max_datetime == max_value
|
756
|
-
|
757
|
-
|
758
|
-
class TestMinAndMaybeMaxSizes:
|
759
|
-
@given(
|
760
|
-
data=data(),
|
761
|
-
min_value=integers(min_value=0) | none() | just(integers(min_value=0) | none()),
|
762
|
-
max_value=integers(min_value=0)
|
763
|
-
| none()
|
764
|
-
| sentinels()
|
765
|
-
| just(integers(min_value=0) | none() | sentinels()),
|
766
|
-
)
|
767
|
-
def test_main(
|
768
|
-
self,
|
769
|
-
*,
|
770
|
-
data: DataObject,
|
771
|
-
min_value: MaybeSearchStrategy[int | None],
|
772
|
-
max_value: MaybeSearchStrategy[int | None | Sentinel],
|
773
|
-
) -> None:
|
774
|
-
min_size, max_size = data.draw(
|
775
|
-
min_and_maybe_max_sizes(min_value=min_value, max_value=max_value)
|
776
|
-
)
|
777
|
-
assert min_size >= 0
|
778
|
-
assert (max_size is None) or (min_size <= max_size)
|
779
|
-
if isinstance(min_value, int):
|
780
|
-
assert min_size == min_value
|
781
|
-
if isinstance(max_value, int) or (max_value is None):
|
782
|
-
assert max_size == max_value
|
783
|
-
|
784
|
-
|
785
730
|
class TestMonths:
|
786
731
|
@given(data=data())
|
787
732
|
def test_main(self, *, data: DataObject) -> None:
|
@@ -81,6 +81,7 @@ from utilities.polars import check_polars_dataframe, zoned_datetime
|
|
81
81
|
from utilities.sentinel import Sentinel, sentinel
|
82
82
|
from utilities.types import LogLevel, MaybeIterable, PathLike
|
83
83
|
from utilities.typing import get_args
|
84
|
+
from utilities.tzlocal import LOCAL_TIME_ZONE
|
84
85
|
from utilities.whenever2 import MINUTE, SECOND, get_now
|
85
86
|
|
86
87
|
if TYPE_CHECKING:
|
@@ -169,7 +170,7 @@ class TestGetLogRecords:
|
|
169
170
|
"level": UInt64,
|
170
171
|
"path_name": String,
|
171
172
|
"line_num": UInt64,
|
172
|
-
"datetime": zoned_datetime(time_zone=
|
173
|
+
"datetime": zoned_datetime(time_zone=LOCAL_TIME_ZONE),
|
173
174
|
"func_name": String,
|
174
175
|
"stack_info": String,
|
175
176
|
"extra": Object,
|
@@ -0,0 +1,259 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from re import search
|
4
|
+
from typing import TYPE_CHECKING
|
5
|
+
|
6
|
+
from hypothesis import HealthCheck, given, settings
|
7
|
+
from hypothesis.strategies import DataObject, data, sampled_from
|
8
|
+
from pytest import raises
|
9
|
+
|
10
|
+
from utilities.hypothesis import (
|
11
|
+
assume_does_not_raise,
|
12
|
+
date_deltas_whenever,
|
13
|
+
dates_whenever,
|
14
|
+
pairs,
|
15
|
+
plain_datetimes_whenever,
|
16
|
+
time_deltas_whenever,
|
17
|
+
zoned_datetimes_whenever,
|
18
|
+
)
|
19
|
+
from utilities.period import (
|
20
|
+
DatePeriod,
|
21
|
+
ZonedDateTimePeriod,
|
22
|
+
_PeriodAsDict,
|
23
|
+
_PeriodInvalidError,
|
24
|
+
_PeriodTimeZoneError,
|
25
|
+
)
|
26
|
+
from utilities.tzdata import USCentral, USEastern
|
27
|
+
from utilities.whenever2 import DAY
|
28
|
+
from utilities.zoneinfo import UTC, get_time_zone_name
|
29
|
+
|
30
|
+
if TYPE_CHECKING:
|
31
|
+
from collections.abc import Callable
|
32
|
+
|
33
|
+
from whenever import Date, DateDelta, PlainDateTime, TimeDelta, ZonedDateTime
|
34
|
+
|
35
|
+
|
36
|
+
class TestDatePeriod:
|
37
|
+
@given(dates=pairs(dates_whenever(), sorted=True), delta=date_deltas_whenever())
|
38
|
+
@settings(suppress_health_check={HealthCheck.filter_too_much})
|
39
|
+
def test_add(self, *, dates: tuple[Date, Date], delta: DateDelta) -> None:
|
40
|
+
start, end = dates
|
41
|
+
period = DatePeriod(start, end)
|
42
|
+
with assume_does_not_raise(ValueError, match="Resulting date out of range"):
|
43
|
+
result = period + delta
|
44
|
+
expected = DatePeriod(start + delta, end + delta)
|
45
|
+
assert result == expected
|
46
|
+
|
47
|
+
@given(date=dates_whenever(), dates=pairs(dates_whenever(), sorted=True))
|
48
|
+
def test_contains(self, *, date: Date, dates: tuple[Date, Date]) -> None:
|
49
|
+
start, end = dates
|
50
|
+
period = DatePeriod(start, end)
|
51
|
+
result = date in period
|
52
|
+
expected = start <= date <= end
|
53
|
+
assert result is expected
|
54
|
+
|
55
|
+
@given(dates=pairs(dates_whenever(), sorted=True))
|
56
|
+
def test_delta(self, *, dates: tuple[Date, Date]) -> None:
|
57
|
+
start, end = dates
|
58
|
+
period = DatePeriod(start, end)
|
59
|
+
assert period.delta == (end - start)
|
60
|
+
|
61
|
+
@given(dates=pairs(dates_whenever(), sorted=True))
|
62
|
+
def test_hashable(self, *, dates: tuple[Date, Date]) -> None:
|
63
|
+
start, end = dates
|
64
|
+
period = DatePeriod(start, end)
|
65
|
+
_ = hash(period)
|
66
|
+
|
67
|
+
@given(dates=pairs(dates_whenever(), sorted=True), func=sampled_from([repr, str]))
|
68
|
+
def test_repr(self, *, dates: tuple[Date, Date], func: Callable[..., str]) -> None:
|
69
|
+
start, end = dates
|
70
|
+
period = DatePeriod(start, end)
|
71
|
+
result = func(period)
|
72
|
+
assert search(r"^DatePeriod\(\d{4}-\d{2}-\d{2}, \d{4}-\d{2}-\d{2}\)$", result)
|
73
|
+
|
74
|
+
@given(
|
75
|
+
dates1=pairs(dates_whenever(), sorted=True),
|
76
|
+
dates2=pairs(dates_whenever(), sorted=True),
|
77
|
+
)
|
78
|
+
def test_sortable(
|
79
|
+
self, *, dates1: tuple[Date, Date], dates2: tuple[Date, Date]
|
80
|
+
) -> None:
|
81
|
+
start1, end1 = dates1
|
82
|
+
start2, end2 = dates2
|
83
|
+
period1 = DatePeriod(start1, end1)
|
84
|
+
period2 = DatePeriod(start2, end2)
|
85
|
+
_ = sorted([period1, period2])
|
86
|
+
|
87
|
+
@given(dates=pairs(dates_whenever(), sorted=True), delta=date_deltas_whenever())
|
88
|
+
@settings(suppress_health_check={HealthCheck.filter_too_much})
|
89
|
+
def test_sub(self, *, dates: tuple[Date, Date], delta: DateDelta) -> None:
|
90
|
+
start, end = dates
|
91
|
+
period = DatePeriod(start, end)
|
92
|
+
with assume_does_not_raise(ValueError, match="Resulting date out of range"):
|
93
|
+
result = period - delta
|
94
|
+
expected = DatePeriod(start - delta, end - delta)
|
95
|
+
assert result == expected
|
96
|
+
|
97
|
+
@given(dates=pairs(dates_whenever(), sorted=True))
|
98
|
+
def test_to_dict(self, *, dates: tuple[Date, Date]) -> None:
|
99
|
+
start, end = dates
|
100
|
+
period = DatePeriod(start, end)
|
101
|
+
result = period.to_dict()
|
102
|
+
expected = _PeriodAsDict(start=start, end=end)
|
103
|
+
assert result == expected
|
104
|
+
|
105
|
+
@given(dates=pairs(dates_whenever(), unique=True, sorted=True))
|
106
|
+
def test_error_period_invalid(self, *, dates: tuple[Date, Date]) -> None:
|
107
|
+
start, end = dates
|
108
|
+
with raises(_PeriodInvalidError, match="Invalid period; got .* > .*"):
|
109
|
+
_ = DatePeriod(end, start)
|
110
|
+
|
111
|
+
|
112
|
+
class TestZonedDateTimePeriod:
|
113
|
+
@given(
|
114
|
+
datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
|
115
|
+
delta=time_deltas_whenever(),
|
116
|
+
)
|
117
|
+
@settings(suppress_health_check={HealthCheck.filter_too_much})
|
118
|
+
def test_add(
|
119
|
+
self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime], delta: TimeDelta
|
120
|
+
) -> None:
|
121
|
+
start, end = datetimes
|
122
|
+
period = ZonedDateTimePeriod(start, end)
|
123
|
+
with assume_does_not_raise(ValueError, match="Instant is out of range"):
|
124
|
+
result = period + delta
|
125
|
+
expected = ZonedDateTimePeriod(start + delta, end + delta)
|
126
|
+
assert result == expected
|
127
|
+
|
128
|
+
@given(
|
129
|
+
datetime=zoned_datetimes_whenever(),
|
130
|
+
datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
|
131
|
+
)
|
132
|
+
def test_contains(
|
133
|
+
self, *, datetime: ZonedDateTime, datetimes: tuple[ZonedDateTime, ZonedDateTime]
|
134
|
+
) -> None:
|
135
|
+
start, end = datetimes
|
136
|
+
period = ZonedDateTimePeriod(start, end)
|
137
|
+
result = datetime in period
|
138
|
+
expected = start <= datetime <= end
|
139
|
+
assert result is expected
|
140
|
+
|
141
|
+
@given(
|
142
|
+
datetime=zoned_datetimes_whenever(),
|
143
|
+
datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
|
144
|
+
)
|
145
|
+
def test_contain_datetime(
|
146
|
+
self, *, datetime: ZonedDateTime, datetimes: tuple[ZonedDateTime, ZonedDateTime]
|
147
|
+
) -> None:
|
148
|
+
start, end = datetimes
|
149
|
+
period = ZonedDateTimePeriod(start, end)
|
150
|
+
result = datetime in period
|
151
|
+
expected = start <= datetime <= end
|
152
|
+
assert result is expected
|
153
|
+
|
154
|
+
@given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
|
155
|
+
def test_delta(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
|
156
|
+
start, end = datetimes
|
157
|
+
period = ZonedDateTimePeriod(start, end)
|
158
|
+
assert period.delta == (end - start)
|
159
|
+
|
160
|
+
@given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
|
161
|
+
def test_hashable(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
|
162
|
+
start, end = datetimes
|
163
|
+
period = ZonedDateTimePeriod(start, end)
|
164
|
+
_ = hash(period)
|
165
|
+
|
166
|
+
@given(
|
167
|
+
data=data(),
|
168
|
+
datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
|
169
|
+
func=sampled_from([repr, str]),
|
170
|
+
)
|
171
|
+
def test_repr(
|
172
|
+
self,
|
173
|
+
*,
|
174
|
+
data: DataObject,
|
175
|
+
datetimes: tuple[ZonedDateTime, ZonedDateTime],
|
176
|
+
func: Callable[..., str],
|
177
|
+
) -> None:
|
178
|
+
start, end = datetimes
|
179
|
+
datetimes = data.draw(pairs(zoned_datetimes_whenever(), sorted=True))
|
180
|
+
start, end = datetimes
|
181
|
+
period = ZonedDateTimePeriod(start, end)
|
182
|
+
result = func(period)
|
183
|
+
assert search(
|
184
|
+
r"^ZonedDateTimePeriod\(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?, \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?\[.+\]\)$",
|
185
|
+
result,
|
186
|
+
)
|
187
|
+
|
188
|
+
@given(
|
189
|
+
dates1=pairs(zoned_datetimes_whenever(), sorted=True),
|
190
|
+
dates2=pairs(zoned_datetimes_whenever(), sorted=True),
|
191
|
+
)
|
192
|
+
def test_sortable(
|
193
|
+
self,
|
194
|
+
*,
|
195
|
+
dates1: tuple[ZonedDateTime, ZonedDateTime],
|
196
|
+
dates2: tuple[ZonedDateTime, ZonedDateTime],
|
197
|
+
) -> None:
|
198
|
+
start1, end1 = dates1
|
199
|
+
start2, end2 = dates2
|
200
|
+
period1 = ZonedDateTimePeriod(start1, end1)
|
201
|
+
period2 = ZonedDateTimePeriod(start2, end2)
|
202
|
+
_ = sorted([period1, period2])
|
203
|
+
|
204
|
+
@given(
|
205
|
+
datetimes=pairs(zoned_datetimes_whenever(), sorted=True),
|
206
|
+
delta=time_deltas_whenever(),
|
207
|
+
)
|
208
|
+
@settings(suppress_health_check={HealthCheck.filter_too_much})
|
209
|
+
def test_sub(
|
210
|
+
self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime], delta: TimeDelta
|
211
|
+
) -> None:
|
212
|
+
start, end = datetimes
|
213
|
+
period = ZonedDateTimePeriod(start, end)
|
214
|
+
with assume_does_not_raise(ValueError, match="Instant is out of range"):
|
215
|
+
result = period - delta
|
216
|
+
expected = ZonedDateTimePeriod(start - delta, end - delta)
|
217
|
+
assert result == expected
|
218
|
+
|
219
|
+
@given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
|
220
|
+
def test_to_dict(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
|
221
|
+
start, end = datetimes
|
222
|
+
period = ZonedDateTimePeriod(start, end)
|
223
|
+
result = period.to_dict()
|
224
|
+
expected = _PeriodAsDict(start=start, end=end)
|
225
|
+
assert result == expected
|
226
|
+
|
227
|
+
@given(datetimes=pairs(zoned_datetimes_whenever(), sorted=True))
|
228
|
+
def test_to_tz(self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]) -> None:
|
229
|
+
start, end = datetimes
|
230
|
+
period = ZonedDateTimePeriod(start, end)
|
231
|
+
with assume_does_not_raise(OverflowError, match="date value out of range"):
|
232
|
+
result = period.to_tz(UTC)
|
233
|
+
assert result.time_zone == UTC
|
234
|
+
name = get_time_zone_name(UTC)
|
235
|
+
expected = ZonedDateTimePeriod(start.to_tz(name), end.to_tz(name))
|
236
|
+
assert result == expected
|
237
|
+
|
238
|
+
@given(datetimes=pairs(zoned_datetimes_whenever(), unique=True, sorted=True))
|
239
|
+
@settings(suppress_health_check={HealthCheck.filter_too_much})
|
240
|
+
def test_error_period_invalid(
|
241
|
+
self, *, datetimes: tuple[ZonedDateTime, ZonedDateTime]
|
242
|
+
) -> None:
|
243
|
+
start, end = datetimes
|
244
|
+
with raises(_PeriodInvalidError, match="Invalid period; got .* > .*"):
|
245
|
+
_ = ZonedDateTimePeriod(end, start)
|
246
|
+
|
247
|
+
@given(datetimes=pairs(plain_datetimes_whenever(), sorted=True))
|
248
|
+
def test_error_period_time_zone(
|
249
|
+
self, *, datetimes: tuple[PlainDateTime, PlainDateTime]
|
250
|
+
) -> None:
|
251
|
+
plain_start, plain_end = datetimes
|
252
|
+
with assume_does_not_raise(OverflowError, match="date value out of range"):
|
253
|
+
start = (plain_start - DAY).assume_tz(USCentral.key)
|
254
|
+
end = (plain_end + DAY).assume_tz(USEastern.key)
|
255
|
+
with raises(
|
256
|
+
_PeriodTimeZoneError,
|
257
|
+
match="Period must contain exactly one time zone; got .* and .*",
|
258
|
+
):
|
259
|
+
_ = ZonedDateTimePeriod(start, end)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from zoneinfo import ZoneInfo
|
4
|
+
|
5
|
+
from hypothesis import given
|
6
|
+
from hypothesis.strategies import sampled_from
|
7
|
+
|
8
|
+
from utilities.tzdata import HongKong, Tokyo, USCentral, USEastern
|
9
|
+
|
10
|
+
|
11
|
+
class TestTimeZones:
|
12
|
+
@given(time_zone=sampled_from([HongKong, Tokyo, USCentral, USEastern]))
|
13
|
+
def test_main(self, *, time_zone: ZoneInfo) -> None:
|
14
|
+
assert isinstance(time_zone, ZoneInfo)
|
@@ -10,7 +10,7 @@ from pytest import raises
|
|
10
10
|
|
11
11
|
from utilities.hypothesis import zoned_datetimes
|
12
12
|
from utilities.tzdata import HongKong, Tokyo
|
13
|
-
from utilities.tzlocal import
|
13
|
+
from utilities.tzlocal import LOCAL_TIME_ZONE, LOCAL_TIME_ZONE_NAME
|
14
14
|
from utilities.zoneinfo import (
|
15
15
|
UTC,
|
16
16
|
_EnsureTimeZoneInvalidTZInfoError,
|
@@ -45,8 +45,7 @@ class TestEnsureTimeZone:
|
|
45
45
|
|
46
46
|
def test_local(self) -> None:
|
47
47
|
result = ensure_time_zone("local")
|
48
|
-
|
49
|
-
assert result is expected
|
48
|
+
assert result is LOCAL_TIME_ZONE
|
50
49
|
|
51
50
|
@given(data=data(), time_zone=timezones())
|
52
51
|
def test_zoned_datetime(self, *, data: DataObject, time_zone: ZoneInfo) -> None:
|
@@ -78,8 +77,7 @@ class TestGetTimeZoneName:
|
|
78
77
|
|
79
78
|
def test_local(self) -> None:
|
80
79
|
result = get_time_zone_name("local")
|
81
|
-
|
82
|
-
assert result is expected
|
80
|
+
assert result == LOCAL_TIME_ZONE_NAME
|
83
81
|
|
84
82
|
|
85
83
|
class TestTimeZones:
|
@@ -5,25 +5,22 @@ from typing import TYPE_CHECKING, ParamSpec, TypeVar
|
|
5
5
|
|
6
6
|
from atools import memoize
|
7
7
|
|
8
|
-
from utilities.datetime import datetime_duration_to_timedelta
|
9
8
|
from utilities.types import Coroutine1
|
10
9
|
|
11
10
|
if TYPE_CHECKING:
|
12
|
-
|
13
|
-
|
14
|
-
from utilities.types import Duration
|
11
|
+
from whenever import TimeDelta
|
15
12
|
|
16
13
|
|
17
14
|
_P = ParamSpec("_P")
|
18
15
|
_R = TypeVar("_R")
|
19
16
|
_AsyncFunc = Callable[_P, Coroutine1[_R]]
|
20
|
-
type _Key = tuple[_AsyncFunc,
|
17
|
+
type _Key = tuple[_AsyncFunc, TimeDelta]
|
21
18
|
_MEMOIZED_FUNCS: dict[_Key, _AsyncFunc] = {}
|
22
19
|
|
23
20
|
|
24
21
|
async def call_memoized(
|
25
22
|
func: _AsyncFunc[_P, _R],
|
26
|
-
refresh:
|
23
|
+
refresh: TimeDelta | None = None,
|
27
24
|
/,
|
28
25
|
*args: _P.args,
|
29
26
|
**kwargs: _P.kwargs,
|
@@ -31,13 +28,14 @@ async def call_memoized(
|
|
31
28
|
"""Call an asynchronous function, with possible memoization."""
|
32
29
|
if refresh is None:
|
33
30
|
return await func(*args, **kwargs)
|
34
|
-
|
35
|
-
key: _Key = (func, timedelta)
|
31
|
+
key: _Key = (func, refresh)
|
36
32
|
memoized_func: _AsyncFunc[_P, _R]
|
37
33
|
try:
|
38
34
|
memoized_func = _MEMOIZED_FUNCS[key]
|
39
35
|
except KeyError:
|
40
|
-
memoized_func = _MEMOIZED_FUNCS[(key)] = memoize(duration=refresh)(
|
36
|
+
memoized_func = _MEMOIZED_FUNCS[(key)] = memoize(duration=refresh.in_seconds())(
|
37
|
+
func
|
38
|
+
)
|
41
39
|
return await memoized_func(*args, **kwargs)
|
42
40
|
|
43
41
|
|
@@ -8,10 +8,10 @@ from typing import TYPE_CHECKING, Any, TypeVar, override
|
|
8
8
|
import cachetools
|
9
9
|
from cachetools.func import ttl_cache
|
10
10
|
|
11
|
-
from utilities.datetime import datetime_duration_to_float
|
12
|
-
|
13
11
|
if TYPE_CHECKING:
|
14
|
-
from
|
12
|
+
from whenever import TimeDelta
|
13
|
+
|
14
|
+
from utilities.types import TCallable
|
15
15
|
|
16
16
|
_K = TypeVar("_K")
|
17
17
|
_T = TypeVar("_T")
|
@@ -25,15 +25,13 @@ class TTLCache(cachetools.TTLCache[_K, _V]):
|
|
25
25
|
self,
|
26
26
|
*,
|
27
27
|
max_size: int | None = None,
|
28
|
-
max_duration:
|
28
|
+
max_duration: TimeDelta | None = None,
|
29
29
|
timer: Callable[[], float] = monotonic,
|
30
30
|
get_size_of: Callable[[Any], int] | None = None,
|
31
31
|
) -> None:
|
32
32
|
super().__init__(
|
33
33
|
maxsize=inf if max_size is None else max_size,
|
34
|
-
ttl=inf
|
35
|
-
if max_duration is None
|
36
|
-
else datetime_duration_to_float(max_duration),
|
34
|
+
ttl=inf if max_duration is None else max_duration.in_seconds(),
|
37
35
|
timer=timer,
|
38
36
|
getsizeof=get_size_of,
|
39
37
|
)
|
@@ -54,7 +52,7 @@ class TTLSet(MutableSet[_T]):
|
|
54
52
|
/,
|
55
53
|
*,
|
56
54
|
max_size: int | None = None,
|
57
|
-
max_duration:
|
55
|
+
max_duration: TimeDelta | None = None,
|
58
56
|
timer: Callable[[], float] = monotonic,
|
59
57
|
get_size_of: Callable[[Any], int] | None = None,
|
60
58
|
) -> None:
|
@@ -103,14 +101,14 @@ class TTLSet(MutableSet[_T]):
|
|
103
101
|
def cache(
|
104
102
|
*,
|
105
103
|
max_size: int | None = None,
|
106
|
-
max_duration:
|
104
|
+
max_duration: TimeDelta | None = None,
|
107
105
|
timer: Callable[[], float] = monotonic,
|
108
106
|
typed_: bool = False,
|
109
107
|
) -> Callable[[TCallable], TCallable]:
|
110
108
|
"""Decorate a function with `max_size` and/or `ttl` settings."""
|
111
109
|
return ttl_cache(
|
112
110
|
maxsize=inf if max_size is None else max_size,
|
113
|
-
ttl=inf if max_duration is None else
|
111
|
+
ttl=inf if max_duration is None else max_duration.in_seconds(),
|
114
112
|
timer=timer,
|
115
113
|
typed=typed_,
|
116
114
|
)
|