dycw-utilities 0.131.12__tar.gz → 0.131.14__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.14}/PKG-INFO +1 -1
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/pyproject.toml +4 -2
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_atools.py +6 -5
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_cachetools.py +22 -9
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_hypothesis.py +0 -80
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_orjson.py +2 -1
- dycw_utilities-0.131.14/src/tests/test_period.py +259 -0
- dycw_utilities-0.131.14/src/tests/test_typed_settings.py +97 -0
- dycw_utilities-0.131.14/src/tests/test_tzdata.py +14 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_zoneinfo.py +3 -5
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/atools.py +7 -9
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/cachetools.py +8 -10
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/hypothesis.py +0 -217
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/orjson.py +1 -1
- dycw_utilities-0.131.14/src/utilities/period.py +154 -0
- dycw_utilities-0.131.14/src/utilities/typed_settings.py +61 -0
- dycw_utilities-0.131.14/src/utilities/tzdata.py +11 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/whenever2.py +4 -3
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/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.14}/.gitignore +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/LICENSE +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/README.md +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_aiolimiter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_inflect.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_whenever2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/aiolimiter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/click.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/git.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/http.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/math.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/os.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/random.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/re.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/string.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/text.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/types.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/version.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.131.12 → dycw_utilities-0.131.14}/src/utilities/zipfile.py +0 -0
@@ -61,6 +61,7 @@ dev = [
|
|
61
61
|
"streamlit >= 1.45.0, < 1.46",
|
62
62
|
"tenacity >= 8.5.0, < 9.0", # limited by luigi
|
63
63
|
"tomlkit >= 0.13.2, < 0.14",
|
64
|
+
"typed-settings >= 24.6.0, < 24.7",
|
64
65
|
"tzdata >= 2025.2, < 2025.3",
|
65
66
|
"uvicorn >= 0.34.1, < 0.35",
|
66
67
|
"vegafusion >= 2.0.2, < 2.1",
|
@@ -94,7 +95,7 @@ dependencies = [
|
|
94
95
|
name = "dycw-utilities"
|
95
96
|
readme = "README.md"
|
96
97
|
requires-python = ">= 3.12"
|
97
|
-
version = "0.131.
|
98
|
+
version = "0.131.14"
|
98
99
|
|
99
100
|
[project.optional-dependencies]
|
100
101
|
logging = [
|
@@ -121,7 +122,7 @@ test = [
|
|
121
122
|
# bump-my-version
|
122
123
|
[tool.bumpversion]
|
123
124
|
allow_dirty = true
|
124
|
-
current_version = "0.131.
|
125
|
+
current_version = "0.131.14"
|
125
126
|
|
126
127
|
[[tool.bumpversion.files]]
|
127
128
|
filename = "src/utilities/__init__.py"
|
@@ -319,6 +320,7 @@ select = [
|
|
319
320
|
"S101", # assert
|
320
321
|
"SLF001", # private-member-access
|
321
322
|
]
|
323
|
+
"src/tests/test_typed_settings.py" = ["I002"] # missing-required-import
|
322
324
|
"src/tests/test_typing_funcs/no_future.py" = ["I002"] # missing-required-import
|
323
325
|
|
324
326
|
[tool.ruff.lint.flake8-tidy-imports]
|
@@ -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:
|
@@ -43,7 +43,6 @@ from utilities.datetime import (
|
|
43
43
|
)
|
44
44
|
from utilities.functions import ensure_int
|
45
45
|
from utilities.hypothesis import (
|
46
|
-
MaybeSearchStrategy,
|
47
46
|
PlainDateTimesError,
|
48
47
|
Shape,
|
49
48
|
ZonedDateTimesError,
|
@@ -67,9 +66,6 @@ from utilities.hypothesis import (
|
|
67
66
|
int64s,
|
68
67
|
int_arrays,
|
69
68
|
lists_fixed_length,
|
70
|
-
min_and_max_datetimes,
|
71
|
-
min_and_maybe_max_datetimes,
|
72
|
-
min_and_maybe_max_sizes,
|
73
69
|
months,
|
74
70
|
namespace_mixins,
|
75
71
|
numbers,
|
@@ -706,82 +702,6 @@ class TestListsFixedLength:
|
|
706
702
|
assert sorted(result) == result
|
707
703
|
|
708
704
|
|
709
|
-
class TestMinAndMaxDateTimes:
|
710
|
-
@given(
|
711
|
-
data=data(),
|
712
|
-
min_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
|
713
|
-
max_value=zoned_datetimes() | none() | just(zoned_datetimes() | none()),
|
714
|
-
)
|
715
|
-
def test_main(
|
716
|
-
self,
|
717
|
-
*,
|
718
|
-
data: DataObject,
|
719
|
-
min_value: MaybeSearchStrategy[dt.datetime | None],
|
720
|
-
max_value: MaybeSearchStrategy[dt.datetime | None],
|
721
|
-
) -> None:
|
722
|
-
min_datetime, max_datetime = data.draw(
|
723
|
-
min_and_max_datetimes(min_value=min_value, max_value=max_value)
|
724
|
-
)
|
725
|
-
assert min_datetime <= max_datetime
|
726
|
-
if isinstance(min_value, dt.datetime):
|
727
|
-
assert min_datetime == min_value
|
728
|
-
if isinstance(max_value, dt.datetime):
|
729
|
-
assert max_datetime == max_value
|
730
|
-
|
731
|
-
|
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
705
|
class TestMonths:
|
786
706
|
@given(data=data())
|
787
707
|
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,97 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from operator import eq
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import TypeVar
|
6
|
+
|
7
|
+
from hypothesis import given
|
8
|
+
from hypothesis.strategies import DataObject, SearchStrategy, data, tuples
|
9
|
+
from pytest import mark, param
|
10
|
+
from typed_settings import FileLoader, TomlFormat, load_settings
|
11
|
+
from whenever import Date, DateDelta, PlainDateTime, Time, TimeDelta, ZonedDateTime
|
12
|
+
|
13
|
+
from utilities.hypothesis import (
|
14
|
+
date_deltas_whenever,
|
15
|
+
dates_whenever,
|
16
|
+
plain_datetimes_whenever,
|
17
|
+
temp_paths,
|
18
|
+
text_ascii,
|
19
|
+
time_deltas_whenever,
|
20
|
+
times_whenever,
|
21
|
+
zoned_datetimes_whenever,
|
22
|
+
)
|
23
|
+
from utilities.typed_settings import ExtendedTSConverter
|
24
|
+
|
25
|
+
app_names = text_ascii(min_size=1).map(str.lower)
|
26
|
+
|
27
|
+
|
28
|
+
_T = TypeVar("_T")
|
29
|
+
|
30
|
+
|
31
|
+
class TestExtendedTSConverter:
|
32
|
+
@given(data=data(), root=temp_paths(), appname=text_ascii(min_size=1))
|
33
|
+
@mark.parametrize(
|
34
|
+
("test_cls", "strategy", "serialize"),
|
35
|
+
[
|
36
|
+
param(Date, dates_whenever(), Date.format_common_iso),
|
37
|
+
param(
|
38
|
+
DateDelta,
|
39
|
+
date_deltas_whenever(parsable=True),
|
40
|
+
DateDelta.format_common_iso,
|
41
|
+
),
|
42
|
+
param(
|
43
|
+
PlainDateTime,
|
44
|
+
plain_datetimes_whenever(),
|
45
|
+
PlainDateTime.format_common_iso,
|
46
|
+
),
|
47
|
+
param(Time, times_whenever(), Time.format_common_iso),
|
48
|
+
param(TimeDelta, time_deltas_whenever(), TimeDelta.format_common_iso),
|
49
|
+
param(
|
50
|
+
ZonedDateTime,
|
51
|
+
zoned_datetimes_whenever(),
|
52
|
+
ZonedDateTime.format_common_iso,
|
53
|
+
),
|
54
|
+
],
|
55
|
+
)
|
56
|
+
def test_main(
|
57
|
+
self,
|
58
|
+
*,
|
59
|
+
data: DataObject,
|
60
|
+
root: Path,
|
61
|
+
appname: str,
|
62
|
+
test_cls: type[_T],
|
63
|
+
strategy: SearchStrategy[_T],
|
64
|
+
serialize: Callable[[_T], str],
|
65
|
+
) -> None:
|
66
|
+
default, value = data.draw(tuples(strategy, strategy))
|
67
|
+
self._run_test(test_cls, default, root, appname, serialize, value, eq)
|
68
|
+
|
69
|
+
def _run_test(
|
70
|
+
self,
|
71
|
+
test_cls: type[_T],
|
72
|
+
default: _T,
|
73
|
+
root: Path,
|
74
|
+
appname: str,
|
75
|
+
serialize: Callable[[_T], str],
|
76
|
+
value: _T,
|
77
|
+
equal: Callable[[_T, _T], bool],
|
78
|
+
/,
|
79
|
+
) -> None:
|
80
|
+
@dataclass(frozen=True, kw_only=True, slots=True)
|
81
|
+
class Settings:
|
82
|
+
value: test_cls = default # pyright: ignore[reportInvalidTypeForm]
|
83
|
+
|
84
|
+
settings_default = load_settings(
|
85
|
+
Settings, loaders=[], converter=ExtendedTSConverter()
|
86
|
+
)
|
87
|
+
assert settings_default.value == default
|
88
|
+
_ = hash(settings_default)
|
89
|
+
file = Path(root, "file.toml")
|
90
|
+
with file.open(mode="w") as fh:
|
91
|
+
_ = fh.write(f'[{appname}]\nvalue = "{serialize(value)}"')
|
92
|
+
settings_loaded = load_settings(
|
93
|
+
Settings,
|
94
|
+
loaders=[FileLoader(formats={"*.toml": TomlFormat(appname)}, files=[file])],
|
95
|
+
converter=ExtendedTSConverter(),
|
96
|
+
)
|
97
|
+
assert equal(settings_loaded.value, value)
|
@@ -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:
|