dycw-utilities 0.132.4__tar.gz → 0.133.1__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.132.4 → dycw_utilities-0.133.1}/PKG-INFO +1 -1
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/pyproject.toml +6 -3
- dycw_utilities-0.133.1/src/tests/test_arq.py +47 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_click.py +3 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_hypothesis.py +13 -1
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_typed_settings.py +3 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_whenever.py +74 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/__init__.py +1 -1
- dycw_utilities-0.133.1/src/utilities/arq.py +161 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/click.py +26 -1
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/hypothesis.py +36 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/typed_settings.py +2 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/types.py +6 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/whenever.py +129 -2
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/.gitignore +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/LICENSE +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/README.md +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_aiolimiter.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_inflect.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_objects/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_objects/objects.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_statsmodels.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/aiolimiter.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/git.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/http.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/math.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/os.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/period.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/random.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/re.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/string.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/text.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/version.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.132.4 → dycw_utilities-0.133.1}/src/utilities/zoneinfo.py +0 -0
@@ -14,6 +14,7 @@ altair-test = [
|
|
14
14
|
"img2pdf",
|
15
15
|
"vl-convert-python",
|
16
16
|
]
|
17
|
+
arq = ["arq >= 0.26.3, < 0.27"]
|
17
18
|
atools = ["atools >= 0.14.2, < 0.15"]
|
18
19
|
cachetools = ["cachetools >= 5.5.2, < 5.6"]
|
19
20
|
click = ["click >= 8.2.1, < 8.3"]
|
@@ -29,6 +30,7 @@ dataclasses-test = ["orjson", "polars-lts-cpu"]
|
|
29
30
|
dev = [
|
30
31
|
"coloredlogs >= 15.0.1, < 15.1",
|
31
32
|
"coverage-conditional-plugin >= 0.9.0, < 0.10",
|
33
|
+
"dycw-pytest-only >= 2.1.1, < 2.2",
|
32
34
|
"pyright[nodejs] >= 1.1.401, < 1.2",
|
33
35
|
"pytest-cov >= 6.1.1, < 6.2",
|
34
36
|
]
|
@@ -74,7 +76,7 @@ pytest-regressions = ["pytest-regressions >= 2.8.0, < 2.9"]
|
|
74
76
|
pytest-regressions-test = ["orjson", "polars-lts-cpu"]
|
75
77
|
pytest-test = ["orjson", "pytest-rng", "pytest-rerunfailures"]
|
76
78
|
python-dotenv = ["python-dotenv >= 1.1.0, < 1.2"]
|
77
|
-
redis = ["redis >=
|
79
|
+
redis = ["redis >= 5.3.0, < 6.0", "orjson"]
|
78
80
|
redis-test = ["pytest-rerunfailures"]
|
79
81
|
reprlib-test = ["rich"]
|
80
82
|
scipy = ["scipy >= 1.15.3, < 1.16"]
|
@@ -102,7 +104,7 @@ dependencies = [
|
|
102
104
|
name = "dycw-utilities"
|
103
105
|
readme = "README.md"
|
104
106
|
requires-python = ">= 3.12"
|
105
|
-
version = "0.
|
107
|
+
version = "0.133.1"
|
106
108
|
|
107
109
|
[project.optional-dependencies]
|
108
110
|
logging = [
|
@@ -128,7 +130,7 @@ test = [
|
|
128
130
|
# bump-my-version
|
129
131
|
[tool.bumpversion]
|
130
132
|
allow_dirty = true
|
131
|
-
current_version = "0.
|
133
|
+
current_version = "0.133.1"
|
132
134
|
|
133
135
|
[[tool.bumpversion.files]]
|
134
136
|
filename = "src/utilities/__init__.py"
|
@@ -237,6 +239,7 @@ filterwarnings = [
|
|
237
239
|
"error",
|
238
240
|
"ignore:.*utcfromtimestamp.* is deprecated and scheduled for removal in a future version:DeprecationWarning", # luigi
|
239
241
|
"ignore:Exception ignored in.* <coroutine object .* at .*>:pytest.PytestUnraisableExceptionWarning",
|
242
|
+
"ignore:Exception in thread Thread-.*:pytest.PytestUnhandledThreadExceptionWarning",
|
240
243
|
"ignore:Implicitly cleaning up <TemporaryDirectory '.*'>:ResourceWarning",
|
241
244
|
"ignore:ResourceTracker called reentrantly for resource cleanup, which is unsupported:UserWarning",
|
242
245
|
"ignore:Task .* without outputs has no custom complete.* method:UserWarning", # luigi
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from asyncio import sleep
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
5
|
+
|
6
|
+
from hypothesis import given
|
7
|
+
from hypothesis.strategies import integers
|
8
|
+
|
9
|
+
from utilities.arq import Worker, cron_raw
|
10
|
+
from utilities.iterables import one
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from collections.abc import Sequence
|
14
|
+
|
15
|
+
from arq.cron import CronJob
|
16
|
+
from arq.typing import WorkerCoroutine
|
17
|
+
|
18
|
+
from utilities.types import CallableCoroutine1
|
19
|
+
|
20
|
+
|
21
|
+
class TestWorker:
|
22
|
+
@given(x=integers(), y=integers())
|
23
|
+
async def test_main(self, *, x: int, y: int) -> None:
|
24
|
+
async def func(x: int, y: int, /) -> int:
|
25
|
+
await sleep(0.01)
|
26
|
+
return x + y
|
27
|
+
|
28
|
+
class Example(Worker):
|
29
|
+
functions_raw: Sequence[CallableCoroutine1[Any]] = [func]
|
30
|
+
|
31
|
+
func_use = cast("WorkerCoroutine", one(Example.functions))
|
32
|
+
result = await func_use({}, x, y)
|
33
|
+
assert result == (x + y)
|
34
|
+
|
35
|
+
@given(x=integers(), y=integers())
|
36
|
+
async def test_cron(self, *, x: int, y: int) -> None:
|
37
|
+
async def func(x: int, y: int, /) -> int:
|
38
|
+
await sleep(0.01)
|
39
|
+
return x + y
|
40
|
+
|
41
|
+
class Example(Worker):
|
42
|
+
cron_jobs: Sequence[CronJob] | None = [cron_raw(func, args=(x, y))]
|
43
|
+
|
44
|
+
assert Example.cron_jobs is not None
|
45
|
+
cron_job = one(Example.cron_jobs)
|
46
|
+
result = await cron_job.coroutine({})
|
47
|
+
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"))
|
@@ -0,0 +1,161 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from functools import wraps
|
5
|
+
from itertools import chain
|
6
|
+
from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast, override
|
7
|
+
|
8
|
+
from arq.constants import default_queue_name, expires_extra_ms
|
9
|
+
from arq.cron import cron
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from collections.abc import Callable, Iterable, Sequence
|
13
|
+
from datetime import timezone
|
14
|
+
|
15
|
+
from arq.connections import ArqRedis, RedisSettings
|
16
|
+
from arq.cron import CronJob
|
17
|
+
from arq.jobs import Deserializer, Serializer
|
18
|
+
from arq.typing import (
|
19
|
+
OptionType,
|
20
|
+
SecondsTimedelta,
|
21
|
+
StartupShutdown,
|
22
|
+
WeekdayOptionType,
|
23
|
+
WorkerCoroutine,
|
24
|
+
)
|
25
|
+
from arq.worker import Function
|
26
|
+
|
27
|
+
from utilities.types import CallableCoroutine1, Coroutine1, StrMapping
|
28
|
+
|
29
|
+
_P = ParamSpec("_P")
|
30
|
+
_T = TypeVar("_T")
|
31
|
+
|
32
|
+
|
33
|
+
##
|
34
|
+
|
35
|
+
|
36
|
+
def cron_raw(
|
37
|
+
coroutine: CallableCoroutine1[Any],
|
38
|
+
/,
|
39
|
+
*,
|
40
|
+
name: str | None = None,
|
41
|
+
month: OptionType = None,
|
42
|
+
day: OptionType = None,
|
43
|
+
weekday: WeekdayOptionType = None,
|
44
|
+
hour: OptionType = None,
|
45
|
+
minute: OptionType = None,
|
46
|
+
second: OptionType = 0,
|
47
|
+
microsecond: int = 123_456,
|
48
|
+
run_at_startup: bool = False,
|
49
|
+
unique: bool = True,
|
50
|
+
job_id: str | None = None,
|
51
|
+
timeout: SecondsTimedelta | None = None,
|
52
|
+
keep_result: float | None = 0,
|
53
|
+
keep_result_forever: bool | None = False,
|
54
|
+
max_tries: int | None = 1,
|
55
|
+
args: Iterable[Any] | None = None,
|
56
|
+
kwargs: StrMapping | None = None,
|
57
|
+
) -> CronJob:
|
58
|
+
"""Create a cron job with a raw coroutine function."""
|
59
|
+
lifted = _lift_cron(
|
60
|
+
coroutine, *(() if args is None else args), **({} if kwargs is None else kwargs)
|
61
|
+
)
|
62
|
+
return cron(
|
63
|
+
lifted,
|
64
|
+
name=name,
|
65
|
+
month=month,
|
66
|
+
day=day,
|
67
|
+
weekday=weekday,
|
68
|
+
hour=hour,
|
69
|
+
minute=minute,
|
70
|
+
second=second,
|
71
|
+
microsecond=microsecond,
|
72
|
+
run_at_startup=run_at_startup,
|
73
|
+
unique=unique,
|
74
|
+
job_id=job_id,
|
75
|
+
timeout=timeout,
|
76
|
+
keep_result=keep_result,
|
77
|
+
keep_result_forever=keep_result_forever,
|
78
|
+
max_tries=max_tries,
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
def _lift_cron(
|
83
|
+
func: Callable[_P, Coroutine1[_T]], *args: _P.args, **kwargs: _P.kwargs
|
84
|
+
) -> WorkerCoroutine:
|
85
|
+
"""Lift a coroutine function & call arg/kwargs for `cron`."""
|
86
|
+
|
87
|
+
@wraps(func)
|
88
|
+
async def wrapped(ctx: StrMapping, /) -> _T:
|
89
|
+
_ = ctx
|
90
|
+
return await func(*args, **kwargs)
|
91
|
+
|
92
|
+
return cast("Any", wrapped)
|
93
|
+
|
94
|
+
|
95
|
+
##
|
96
|
+
|
97
|
+
|
98
|
+
class _WorkerMeta(type):
|
99
|
+
@override
|
100
|
+
def __new__(
|
101
|
+
mcs: type[_WorkerMeta],
|
102
|
+
name: str,
|
103
|
+
bases: tuple[type, ...],
|
104
|
+
namespace: dict[str, Any],
|
105
|
+
/,
|
106
|
+
) -> type[Worker]:
|
107
|
+
cls = cast("type[Worker]", super().__new__(mcs, name, bases, namespace))
|
108
|
+
cls.functions = tuple(chain(cls.functions, map(cls._lift, cls.functions_raw)))
|
109
|
+
return cls
|
110
|
+
|
111
|
+
@classmethod
|
112
|
+
def _lift(cls, func: Callable[_P, Coroutine1[_T]]) -> WorkerCoroutine:
|
113
|
+
"""Lift a coroutine function to accept the required `ctx` argument."""
|
114
|
+
|
115
|
+
@wraps(func)
|
116
|
+
async def wrapped(ctx: StrMapping, *args: _P.args, **kwargs: _P.kwargs) -> _T:
|
117
|
+
_ = ctx
|
118
|
+
return await func(*args, **kwargs)
|
119
|
+
|
120
|
+
return cast("Any", wrapped)
|
121
|
+
|
122
|
+
|
123
|
+
@dataclass(kw_only=True)
|
124
|
+
class Worker(metaclass=_WorkerMeta):
|
125
|
+
"""Base class for all workers."""
|
126
|
+
|
127
|
+
functions: Sequence[Function | WorkerCoroutine] = ()
|
128
|
+
functions_raw: Sequence[CallableCoroutine1[Any]] = ()
|
129
|
+
queue_name: str | None = default_queue_name
|
130
|
+
cron_jobs: Sequence[CronJob] | None = None
|
131
|
+
redis_settings: RedisSettings | None = None
|
132
|
+
redis_pool: ArqRedis | None = None
|
133
|
+
burst: bool = False
|
134
|
+
on_startup: StartupShutdown | None = None
|
135
|
+
on_shutdown: StartupShutdown | None = None
|
136
|
+
on_job_start: StartupShutdown | None = None
|
137
|
+
on_job_end: StartupShutdown | None = None
|
138
|
+
after_job_end: StartupShutdown | None = None
|
139
|
+
handle_signals: bool = True
|
140
|
+
job_completion_wait: int = 0
|
141
|
+
max_jobs: int = 10
|
142
|
+
job_timeout: SecondsTimedelta = 300
|
143
|
+
keep_result: SecondsTimedelta = 3600
|
144
|
+
keep_result_forever: bool = False
|
145
|
+
poll_delay: SecondsTimedelta = 0.5
|
146
|
+
queue_read_limit: int | None = None
|
147
|
+
max_tries: int = 5
|
148
|
+
health_check_interval: SecondsTimedelta = 3600
|
149
|
+
health_check_key: str | None = None
|
150
|
+
ctx: dict[Any, Any] | None = None
|
151
|
+
retry_jobs: bool = True
|
152
|
+
allow_abort_jobs: bool = False
|
153
|
+
max_burst_jobs: int = -1
|
154
|
+
job_serializer: Serializer | None = None
|
155
|
+
job_deserializer: Deserializer | None = None
|
156
|
+
expires_extra_ms: int = expires_extra_ms
|
157
|
+
timezone: timezone | None = None
|
158
|
+
log_results: bool = True
|
159
|
+
|
160
|
+
|
161
|
+
__all__ = ["Worker", "cron"]
|
@@ -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),
|
@@ -72,12 +72,16 @@ type Coroutine1[_T] = Coroutine[Any, Any, _T]
|
|
72
72
|
type MaybeAwaitable[_T] = _T | Awaitable[_T]
|
73
73
|
type MaybeCallableEvent = MaybeCallable[Event]
|
74
74
|
type MaybeCoroutine1[_T] = _T | Coroutine1[_T]
|
75
|
+
type CallableCoroutine1[_T] = Callable[..., Coroutine1[_T]]
|
75
76
|
|
76
77
|
|
77
78
|
# callable
|
78
79
|
TCallable = TypeVar("TCallable", bound=Callable[..., Any])
|
79
80
|
TCallable1 = TypeVar("TCallable1", bound=Callable[..., Any])
|
80
81
|
TCallable2 = TypeVar("TCallable2", bound=Callable[..., Any])
|
82
|
+
TCallableCoroutine1 = TypeVar(
|
83
|
+
"TCallableCoroutine1", bound=Callable[..., Coroutine1[Any]]
|
84
|
+
)
|
81
85
|
TCallableMaybeCoroutine1None = TypeVar(
|
82
86
|
"TCallableMaybeCoroutine1None", bound=Callable[..., MaybeCoroutine1[None]]
|
83
87
|
)
|
@@ -292,6 +296,7 @@ type TimeZoneLike = (
|
|
292
296
|
|
293
297
|
|
294
298
|
__all__ = [
|
299
|
+
"CallableCoroutine1",
|
295
300
|
"Coroutine1",
|
296
301
|
"Dataclass",
|
297
302
|
"DateDeltaLike",
|
@@ -346,6 +351,7 @@ __all__ = [
|
|
346
351
|
"TCallable",
|
347
352
|
"TCallable1",
|
348
353
|
"TCallable2",
|
354
|
+
"TCallableCoroutine1",
|
349
355
|
"TCallableMaybeCoroutine1None",
|
350
356
|
"TDataclass",
|
351
357
|
"TEnum",
|