dycw-utilities 0.133.0__tar.gz → 0.133.2__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.133.0 → dycw_utilities-0.133.2}/PKG-INFO +1 -1
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/pyproject.toml +4 -2
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_arq.py +24 -13
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_click.py +3 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_hypothesis.py +13 -1
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typed_settings.py +3 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_whenever.py +74 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/arq.py +66 -4
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/click.py +26 -1
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/hypothesis.py +36 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/typed_settings.py +2 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/whenever.py +129 -2
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/.gitignore +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/LICENSE +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/README.md +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_aiolimiter.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_inflect.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_objects/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_objects/objects.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_statsmodels.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/aiolimiter.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/git.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/http.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/math.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/os.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/period.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/random.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/re.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/string.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/text.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/types.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/version.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.133.0 → dycw_utilities-0.133.2}/src/utilities/zoneinfo.py +0 -0
@@ -30,6 +30,7 @@ dataclasses-test = ["orjson", "polars-lts-cpu"]
|
|
30
30
|
dev = [
|
31
31
|
"coloredlogs >= 15.0.1, < 15.1",
|
32
32
|
"coverage-conditional-plugin >= 0.9.0, < 0.10",
|
33
|
+
"dycw-pytest-only >= 2.1.1, < 2.2",
|
33
34
|
"pyright[nodejs] >= 1.1.401, < 1.2",
|
34
35
|
"pytest-cov >= 6.1.1, < 6.2",
|
35
36
|
]
|
@@ -103,7 +104,7 @@ dependencies = [
|
|
103
104
|
name = "dycw-utilities"
|
104
105
|
readme = "README.md"
|
105
106
|
requires-python = ">= 3.12"
|
106
|
-
version = "0.133.
|
107
|
+
version = "0.133.2"
|
107
108
|
|
108
109
|
[project.optional-dependencies]
|
109
110
|
logging = [
|
@@ -129,7 +130,7 @@ test = [
|
|
129
130
|
# bump-my-version
|
130
131
|
[tool.bumpversion]
|
131
132
|
allow_dirty = true
|
132
|
-
current_version = "0.133.
|
133
|
+
current_version = "0.133.2"
|
133
134
|
|
134
135
|
[[tool.bumpversion.files]]
|
135
136
|
filename = "src/utilities/__init__.py"
|
@@ -238,6 +239,7 @@ filterwarnings = [
|
|
238
239
|
"error",
|
239
240
|
"ignore:.*utcfromtimestamp.* is deprecated and scheduled for removal in a future version:DeprecationWarning", # luigi
|
240
241
|
"ignore:Exception ignored in.* <coroutine object .* at .*>:pytest.PytestUnraisableExceptionWarning",
|
242
|
+
"ignore:Exception in thread Thread-.*:pytest.PytestUnhandledThreadExceptionWarning",
|
241
243
|
"ignore:Implicitly cleaning up <TemporaryDirectory '.*'>:ResourceWarning",
|
242
244
|
"ignore:ResourceTracker called reentrantly for resource cleanup, which is unsupported:UserWarning",
|
243
245
|
"ignore:Task .* without outputs has no custom complete.* method:UserWarning", # luigi
|
@@ -3,45 +3,56 @@ from __future__ import annotations
|
|
3
3
|
from asyncio import sleep
|
4
4
|
from typing import TYPE_CHECKING, Any, cast
|
5
5
|
|
6
|
+
from arq.connections import ArqRedis
|
6
7
|
from hypothesis import given
|
7
8
|
from hypothesis.strategies import integers
|
8
9
|
|
9
|
-
from
|
10
|
+
from tests.conftest import SKIPIF_CI_AND_NOT_LINUX
|
11
|
+
from utilities.arq import Worker, cron_raw, job_enqueuer
|
10
12
|
from utilities.iterables import one
|
11
13
|
|
12
14
|
if TYPE_CHECKING:
|
13
15
|
from collections.abc import Sequence
|
14
16
|
|
15
|
-
from arq.cron import CronJob
|
16
17
|
from arq.typing import WorkerCoroutine
|
17
18
|
|
18
19
|
from utilities.types import CallableCoroutine1
|
19
20
|
|
20
21
|
|
21
|
-
class
|
22
|
+
class TestCronRaw:
|
22
23
|
@given(x=integers(), y=integers())
|
23
24
|
async def test_main(self, *, x: int, y: int) -> None:
|
24
25
|
async def func(x: int, y: int, /) -> int:
|
25
26
|
await sleep(0.01)
|
26
27
|
return x + y
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
func_use = cast("WorkerCoroutine", one(Example.functions))
|
32
|
-
result = await func_use({}, x, y)
|
29
|
+
job = cron_raw(func, args=(x, y))
|
30
|
+
result = await job.coroutine({})
|
33
31
|
assert result == (x + y)
|
34
32
|
|
33
|
+
|
34
|
+
class TestJobEnqueuer:
|
35
|
+
@given(x=integers(), y=integers())
|
36
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
37
|
+
async def test_main(self, *, x: int, y: int) -> None:
|
38
|
+
async def func(x: int, y: int, /) -> int:
|
39
|
+
await sleep(0.01)
|
40
|
+
return x + y
|
41
|
+
|
42
|
+
redis = ArqRedis(db=15)
|
43
|
+
_ = await job_enqueuer.settings(queue_name="test")(redis, func, x, y)
|
44
|
+
|
45
|
+
|
46
|
+
class TestWorker:
|
35
47
|
@given(x=integers(), y=integers())
|
36
|
-
async def
|
48
|
+
async def test_main(self, *, x: int, y: int) -> None:
|
37
49
|
async def func(x: int, y: int, /) -> int:
|
38
50
|
await sleep(0.01)
|
39
51
|
return x + y
|
40
52
|
|
41
53
|
class Example(Worker):
|
42
|
-
|
54
|
+
functions_raw: Sequence[CallableCoroutine1[Any]] = [func]
|
43
55
|
|
44
|
-
|
45
|
-
|
46
|
-
result = await cron_job.coroutine({})
|
56
|
+
func_use = cast("WorkerCoroutine", one(Example.functions))
|
57
|
+
result = await func_use({}, x, y)
|
47
58
|
assert result == (x + y)
|
@@ -34,6 +34,7 @@ from utilities.click import (
|
|
34
34
|
ExistingDirPath,
|
35
35
|
ExistingFilePath,
|
36
36
|
FilePath,
|
37
|
+
Freq,
|
37
38
|
FrozenSetChoices,
|
38
39
|
FrozenSetEnums,
|
39
40
|
FrozenSetStrs,
|
@@ -51,6 +52,7 @@ from utilities.hypothesis import (
|
|
51
52
|
date_deltas,
|
52
53
|
date_time_deltas,
|
53
54
|
dates,
|
55
|
+
freqs,
|
54
56
|
months,
|
55
57
|
pairs,
|
56
58
|
plain_datetimes,
|
@@ -209,6 +211,7 @@ class TestParameters:
|
|
209
211
|
attrgetter("name"),
|
210
212
|
True,
|
211
213
|
),
|
214
|
+
param(Freq(), "FREQ", freqs(), utilities.whenever.Freq.serialize, True),
|
212
215
|
param(
|
213
216
|
FrozenSetChoices(["a", "b", "c"]),
|
214
217
|
"FROZENSET[Choice(['a', 'b', 'c'])]",
|
@@ -41,6 +41,7 @@ from utilities.hypothesis import (
|
|
41
41
|
Shape,
|
42
42
|
_Draw2DefaultGeneratedSentinelError,
|
43
43
|
_Draw2InputResolvedToSentinelError,
|
44
|
+
_freq_units,
|
44
45
|
assume_does_not_raise,
|
45
46
|
bool_arrays,
|
46
47
|
date_deltas,
|
@@ -51,6 +52,7 @@ from utilities.hypothesis import (
|
|
51
52
|
float64s,
|
52
53
|
float_arrays,
|
53
54
|
floats_extra,
|
55
|
+
freqs,
|
54
56
|
git_repos,
|
55
57
|
hashables,
|
56
58
|
int32s,
|
@@ -107,6 +109,7 @@ from utilities.version import Version
|
|
107
109
|
from utilities.whenever import (
|
108
110
|
DATE_TWO_DIGIT_YEAR_MAX,
|
109
111
|
DATE_TWO_DIGIT_YEAR_MIN,
|
112
|
+
Freq,
|
110
113
|
Month,
|
111
114
|
to_days,
|
112
115
|
to_nanos,
|
@@ -118,7 +121,7 @@ if TYPE_CHECKING:
|
|
118
121
|
from zoneinfo import ZoneInfo
|
119
122
|
|
120
123
|
from utilities.tempfile import TemporaryDirectory
|
121
|
-
from utilities.types import Number
|
124
|
+
from utilities.types import DateTimeRoundUnit, Number
|
122
125
|
|
123
126
|
|
124
127
|
class TestAssumeDoesNotRaise:
|
@@ -486,6 +489,15 @@ class TestFloatsExtra:
|
|
486
489
|
assert x == round(x)
|
487
490
|
|
488
491
|
|
492
|
+
class TestFreqs:
|
493
|
+
@given(data=data(), unit=_freq_units() | none())
|
494
|
+
def test_main(self, *, data: DataObject, unit: DateTimeRoundUnit | None) -> None:
|
495
|
+
freq = data.draw(freqs(unit=unit))
|
496
|
+
assert isinstance(freq, Freq)
|
497
|
+
if unit is not None:
|
498
|
+
assert freq.unit == unit
|
499
|
+
|
500
|
+
|
489
501
|
class TestGitRepos:
|
490
502
|
@given(data=data())
|
491
503
|
@settings_with_reduced_examples()
|
@@ -23,6 +23,7 @@ from utilities.hypothesis import (
|
|
23
23
|
date_deltas,
|
24
24
|
date_time_deltas,
|
25
25
|
dates,
|
26
|
+
freqs,
|
26
27
|
plain_datetimes,
|
27
28
|
temp_paths,
|
28
29
|
text_ascii,
|
@@ -37,6 +38,7 @@ from utilities.typed_settings import (
|
|
37
38
|
LoadSettingsError,
|
38
39
|
load_settings,
|
39
40
|
)
|
41
|
+
from utilities.whenever import Freq
|
40
42
|
|
41
43
|
app_names = text_ascii(min_size=1).map(str.lower)
|
42
44
|
|
@@ -56,6 +58,7 @@ class TestExtendedTSConverter:
|
|
56
58
|
date_time_deltas(parsable=True),
|
57
59
|
DateTimeDelta.format_common_iso,
|
58
60
|
),
|
61
|
+
param(Freq, freqs(), Freq.serialize),
|
59
62
|
param(IPv4Address, ip_addresses(v=4), IPv4Address),
|
60
63
|
param(IPv6Address, ip_addresses(v=6), IPv6Address),
|
61
64
|
param(PlainDateTime, plain_datetimes(), PlainDateTime.format_common_iso),
|
@@ -30,12 +30,15 @@ from utilities.hypothesis import (
|
|
30
30
|
assume_does_not_raise,
|
31
31
|
date_deltas,
|
32
32
|
dates,
|
33
|
+
freqs,
|
33
34
|
months,
|
34
35
|
pairs,
|
35
36
|
sentinels,
|
36
37
|
zoned_datetimes,
|
37
38
|
)
|
38
39
|
from utilities.sentinel import Sentinel, sentinel
|
40
|
+
from utilities.types import DateTimeRoundUnit
|
41
|
+
from utilities.typing import get_literal_elements
|
39
42
|
from utilities.tzdata import HongKong, Tokyo
|
40
43
|
from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
|
41
44
|
from utilities.whenever import (
|
@@ -64,12 +67,16 @@ from utilities.whenever import (
|
|
64
67
|
ZERO_DAYS,
|
65
68
|
ZONED_DATE_TIME_MAX,
|
66
69
|
ZONED_DATE_TIME_MIN,
|
70
|
+
Freq,
|
67
71
|
MeanDateTimeError,
|
68
72
|
MinMaxDateError,
|
69
73
|
Month,
|
70
74
|
ToDaysError,
|
71
75
|
ToNanosError,
|
72
76
|
WheneverLogRecord,
|
77
|
+
_FreqDayIncrementError,
|
78
|
+
_FreqIncrementError,
|
79
|
+
_FreqParseError,
|
73
80
|
_MinMaxDateMaxDateError,
|
74
81
|
_MinMaxDateMinDateError,
|
75
82
|
_MinMaxDatePeriodError,
|
@@ -128,6 +135,73 @@ class TestFormatCompact:
|
|
128
135
|
assert parsed == expected
|
129
136
|
|
130
137
|
|
138
|
+
class TestFreq:
|
139
|
+
@given(freq=freqs())
|
140
|
+
def test_main(self, *, freq: Freq) -> None:
|
141
|
+
_ = get_now().round(unit=freq.unit, increment=freq.increment, mode="floor")
|
142
|
+
|
143
|
+
@given(unit=sampled_from(get_literal_elements(DateTimeRoundUnit)))
|
144
|
+
def test_abbreviate_and_expand(self, *, unit: DateTimeRoundUnit) -> None:
|
145
|
+
result = Freq._expand(Freq._abbreviate(unit))
|
146
|
+
assert result == unit
|
147
|
+
|
148
|
+
@given(freqs=pairs(freqs()))
|
149
|
+
def test_eq(self, *, freqs: tuple[Freq, Freq]) -> None:
|
150
|
+
x, y = freqs
|
151
|
+
result = x == y
|
152
|
+
assert isinstance(result, bool)
|
153
|
+
|
154
|
+
@given(freq=freqs())
|
155
|
+
def test_eq_non_freq(self, *, freq: Freq) -> None:
|
156
|
+
result = freq == 0
|
157
|
+
assert not result
|
158
|
+
|
159
|
+
@given(freq=freqs())
|
160
|
+
def test_hashable(self, *, freq: Freq) -> None:
|
161
|
+
_ = hash(freq)
|
162
|
+
|
163
|
+
@given(freq=freqs())
|
164
|
+
def test_repr(self, *, freq: Freq) -> None:
|
165
|
+
_ = repr(freq)
|
166
|
+
|
167
|
+
@given(freq=freqs())
|
168
|
+
def test_serialize_and_parse(self, *, freq: Freq) -> None:
|
169
|
+
result = Freq.parse(freq.serialize())
|
170
|
+
assert result == freq
|
171
|
+
|
172
|
+
def test_error_day(self) -> None:
|
173
|
+
with raises(
|
174
|
+
_FreqDayIncrementError,
|
175
|
+
match="Increment must be 1 for the 'day' unit; got 2",
|
176
|
+
):
|
177
|
+
_ = Freq(unit="day", increment=2)
|
178
|
+
|
179
|
+
def test_error_hour(self) -> None:
|
180
|
+
with raises(
|
181
|
+
_FreqIncrementError,
|
182
|
+
match="Increment must be a proper divisor of 24 for the 'hour' unit; got 5",
|
183
|
+
):
|
184
|
+
_ = Freq(unit="hour", increment=5)
|
185
|
+
|
186
|
+
def test_error_minute(self) -> None:
|
187
|
+
with raises(
|
188
|
+
_FreqIncrementError,
|
189
|
+
match="Increment must be a proper divisor of 60 for the 'minute' unit; got 7",
|
190
|
+
):
|
191
|
+
_ = Freq(unit="minute", increment=7)
|
192
|
+
|
193
|
+
def test_error_milliseond(self) -> None:
|
194
|
+
with raises(
|
195
|
+
_FreqIncrementError,
|
196
|
+
match="Increment must be a proper divisor of 1000 for the 'millisecond' unit; got 3",
|
197
|
+
):
|
198
|
+
_ = Freq(unit="millisecond", increment=3)
|
199
|
+
|
200
|
+
def test_error_parse(self) -> None:
|
201
|
+
with raises(_FreqParseError, match="Unable to parse frequency; got 's'"):
|
202
|
+
_ = Freq.parse("s")
|
203
|
+
|
204
|
+
|
131
205
|
class TestFromTimeStamp:
|
132
206
|
@given(
|
133
207
|
datetime=zoned_datetimes(time_zone=timezones()).map(lambda d: d.round("second"))
|
@@ -3,18 +3,21 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from functools import wraps
|
5
5
|
from itertools import chain
|
6
|
-
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast, override
|
6
|
+
from typing import TYPE_CHECKING, Any, ParamSpec, Self, TypeVar, cast, override
|
7
7
|
|
8
8
|
from arq.constants import default_queue_name, expires_extra_ms
|
9
9
|
from arq.cron import cron
|
10
10
|
|
11
|
+
from utilities.dataclasses import replace_non_sentinel
|
12
|
+
from utilities.sentinel import Sentinel, sentinel
|
13
|
+
|
11
14
|
if TYPE_CHECKING:
|
12
15
|
from collections.abc import Callable, Iterable, Sequence
|
13
|
-
from datetime import timezone
|
16
|
+
from datetime import datetime, timedelta, timezone
|
14
17
|
|
15
18
|
from arq.connections import ArqRedis, RedisSettings
|
16
19
|
from arq.cron import CronJob
|
17
|
-
from arq.jobs import Deserializer, Serializer
|
20
|
+
from arq.jobs import Deserializer, Job, Serializer
|
18
21
|
from arq.typing import (
|
19
22
|
OptionType,
|
20
23
|
SecondsTimedelta,
|
@@ -26,6 +29,7 @@ if TYPE_CHECKING:
|
|
26
29
|
|
27
30
|
from utilities.types import CallableCoroutine1, Coroutine1, StrMapping
|
28
31
|
|
32
|
+
|
29
33
|
_P = ParamSpec("_P")
|
30
34
|
_T = TypeVar("_T")
|
31
35
|
|
@@ -95,6 +99,64 @@ def _lift_cron(
|
|
95
99
|
##
|
96
100
|
|
97
101
|
|
102
|
+
@dataclass(kw_only=True, slots=True)
|
103
|
+
class _JobEnqueuer:
|
104
|
+
"""Enqueuer of jobs."""
|
105
|
+
|
106
|
+
job_id: str | None = None
|
107
|
+
queue_name: str | None = None
|
108
|
+
defer_until: datetime | None = None
|
109
|
+
defer_by: int | float | timedelta | None = None
|
110
|
+
expires: int | float | timedelta | None = None
|
111
|
+
job_try: int | None = None
|
112
|
+
|
113
|
+
async def __call__(
|
114
|
+
self,
|
115
|
+
redis: ArqRedis,
|
116
|
+
function: Callable[_P, Coroutine1[_T]],
|
117
|
+
*args: _P.args,
|
118
|
+
**kwargs: _P.kwargs,
|
119
|
+
) -> Job | None:
|
120
|
+
return await redis.enqueue_job( # skipif-ci-and-not-linux
|
121
|
+
function.__name__,
|
122
|
+
*args,
|
123
|
+
_job_id=self.job_id,
|
124
|
+
_queue_name=self.queue_name,
|
125
|
+
_defer_until=self.defer_until,
|
126
|
+
_defer_by=self.defer_by,
|
127
|
+
_expires=self.expires,
|
128
|
+
_job_try=self.job_try,
|
129
|
+
**kwargs,
|
130
|
+
)
|
131
|
+
|
132
|
+
def settings(
|
133
|
+
self,
|
134
|
+
*,
|
135
|
+
job_id: str | None | Sentinel = sentinel,
|
136
|
+
queue_name: str | None | Sentinel = sentinel,
|
137
|
+
defer_until: datetime | None | Sentinel = sentinel,
|
138
|
+
defer_by: float | timedelta | None | Sentinel = sentinel,
|
139
|
+
expires: float | timedelta | None | Sentinel = sentinel,
|
140
|
+
job_try: int | None | Sentinel = sentinel,
|
141
|
+
) -> Self:
|
142
|
+
"""Replace elements of the enqueuer."""
|
143
|
+
return replace_non_sentinel( # skipif-ci-and-not-linux
|
144
|
+
self,
|
145
|
+
job_id=job_id,
|
146
|
+
queue_name=queue_name,
|
147
|
+
defer_until=defer_until,
|
148
|
+
defer_by=defer_by,
|
149
|
+
expires=expires,
|
150
|
+
job_try=job_try,
|
151
|
+
)
|
152
|
+
|
153
|
+
|
154
|
+
job_enqueuer = _JobEnqueuer()
|
155
|
+
|
156
|
+
|
157
|
+
##
|
158
|
+
|
159
|
+
|
98
160
|
class _WorkerMeta(type):
|
99
161
|
@override
|
100
162
|
def __new__(
|
@@ -158,4 +220,4 @@ class Worker(metaclass=_WorkerMeta):
|
|
158
220
|
log_results: bool = True
|
159
221
|
|
160
222
|
|
161
|
-
__all__ = ["Worker", "cron"]
|
223
|
+
__all__ = ["Worker", "cron", "job_enqueuer"]
|
@@ -29,7 +29,7 @@ from utilities.types import (
|
|
29
29
|
TimeLike,
|
30
30
|
ZonedDateTimeLike,
|
31
31
|
)
|
32
|
-
from utilities.whenever import _MonthParseCommonISOError
|
32
|
+
from utilities.whenever import FreqLike, _FreqParseError, _MonthParseCommonISOError
|
33
33
|
|
34
34
|
if TYPE_CHECKING:
|
35
35
|
from collections.abc import Iterable, Sequence
|
@@ -177,6 +177,30 @@ class Enum(ParamType, Generic[TEnum]):
|
|
177
177
|
return _make_metavar(param, desc)
|
178
178
|
|
179
179
|
|
180
|
+
class Freq(ParamType):
|
181
|
+
"""An frequency-valued parameter."""
|
182
|
+
|
183
|
+
@override
|
184
|
+
def __repr__(self) -> str:
|
185
|
+
return "FREQ"
|
186
|
+
|
187
|
+
@override
|
188
|
+
def convert(
|
189
|
+
self, value: FreqLike, param: Parameter | None, ctx: Context | None
|
190
|
+
) -> utilities.whenever.Freq:
|
191
|
+
"""Convert a value into the `Freq` type."""
|
192
|
+
match value:
|
193
|
+
case utilities.whenever.Freq():
|
194
|
+
return value
|
195
|
+
case str():
|
196
|
+
try:
|
197
|
+
return utilities.whenever.Freq.parse(value)
|
198
|
+
except _FreqParseError as error:
|
199
|
+
self.fail(str(error), param, ctx)
|
200
|
+
case _ as never:
|
201
|
+
assert_never(never)
|
202
|
+
|
203
|
+
|
180
204
|
class IPv4Address(ParamType):
|
181
205
|
"""An IPv4 address-valued parameter."""
|
182
206
|
|
@@ -519,6 +543,7 @@ __all__ = [
|
|
519
543
|
"ExistingDirPath",
|
520
544
|
"ExistingFilePath",
|
521
545
|
"FilePath",
|
546
|
+
"Freq",
|
522
547
|
"FrozenSetChoices",
|
523
548
|
"FrozenSetEnums",
|
524
549
|
"FrozenSetParameter",
|
@@ -77,6 +77,8 @@ from utilities.pathlib import temp_cwd
|
|
77
77
|
from utilities.platform import IS_WINDOWS
|
78
78
|
from utilities.sentinel import Sentinel, sentinel
|
79
79
|
from utilities.tempfile import TEMP_DIR, TemporaryDirectory
|
80
|
+
from utilities.types import DateTimeRoundUnit
|
81
|
+
from utilities.typing import get_literal_elements
|
80
82
|
from utilities.version import Version
|
81
83
|
from utilities.whenever import (
|
82
84
|
DATE_DELTA_MAX,
|
@@ -100,6 +102,7 @@ from utilities.whenever import (
|
|
100
102
|
TIME_DELTA_MIN,
|
101
103
|
TIME_MAX,
|
102
104
|
TIME_MIN,
|
105
|
+
Freq,
|
103
106
|
Month,
|
104
107
|
to_date_time_delta,
|
105
108
|
to_days,
|
@@ -502,6 +505,38 @@ def floats_extra(
|
|
502
505
|
##
|
503
506
|
|
504
507
|
|
508
|
+
@composite
|
509
|
+
def freqs(
|
510
|
+
draw: DrawFn, /, *, unit: MaybeSearchStrategy[DateTimeRoundUnit | None] = None
|
511
|
+
) -> Freq:
|
512
|
+
unit_ = draw2(draw, unit, _freq_units())
|
513
|
+
match unit_:
|
514
|
+
case "day":
|
515
|
+
return Freq(unit=unit_)
|
516
|
+
case "hour":
|
517
|
+
return Freq(unit=unit_, increment=draw(_freq_increments(24)))
|
518
|
+
case "minute" | "second":
|
519
|
+
return Freq(unit=unit_, increment=draw(_freq_increments(60)))
|
520
|
+
case "millisecond" | "microsecond" | "nanosecond":
|
521
|
+
return Freq(unit=unit_, increment=draw(_freq_increments(1000)))
|
522
|
+
case _ as never:
|
523
|
+
assert_never(never)
|
524
|
+
|
525
|
+
|
526
|
+
@composite
|
527
|
+
def _freq_units(draw: DrawFn, /) -> DateTimeRoundUnit:
|
528
|
+
return draw(sampled_from(get_literal_elements(DateTimeRoundUnit)))
|
529
|
+
|
530
|
+
|
531
|
+
@composite
|
532
|
+
def _freq_increments(draw: DrawFn, n: int, /) -> int:
|
533
|
+
divisors = [i for i in range(1, n) if n % i == 0]
|
534
|
+
return draw(sampled_from(divisors))
|
535
|
+
|
536
|
+
|
537
|
+
##
|
538
|
+
|
539
|
+
|
505
540
|
@composite
|
506
541
|
def git_repos(draw: DrawFn, /) -> Path:
|
507
542
|
path = draw(temp_paths())
|
@@ -1264,6 +1299,7 @@ __all__ = [
|
|
1264
1299
|
"float64s",
|
1265
1300
|
"float_arrays",
|
1266
1301
|
"floats_extra",
|
1302
|
+
"freqs",
|
1267
1303
|
"git_repos",
|
1268
1304
|
"hashables",
|
1269
1305
|
"int32s",
|
@@ -21,6 +21,7 @@ from whenever import (
|
|
21
21
|
)
|
22
22
|
|
23
23
|
from utilities.iterables import always_iterable
|
24
|
+
from utilities.whenever import Freq
|
24
25
|
|
25
26
|
if TYPE_CHECKING:
|
26
27
|
from collections.abc import Callable
|
@@ -52,6 +53,7 @@ class ExtendedTSConverter(TSConverter):
|
|
52
53
|
(Date, Date.parse_common_iso),
|
53
54
|
(DateDelta, DateDelta.parse_common_iso),
|
54
55
|
(DateTimeDelta, DateTimeDelta.parse_common_iso),
|
56
|
+
(Freq, Freq.parse),
|
55
57
|
(IPv4Address, IPv4Address),
|
56
58
|
(IPv6Address, IPv6Address),
|
57
59
|
(PlainDateTime, PlainDateTime.parse_common_iso),
|