dycw-utilities 0.117.0__tar.gz → 0.118.0__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.117.0 → dycw_utilities-0.118.0}/PKG-INFO +1 -1
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/pyproject.toml +2 -2
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_asyncio.py +60 -398
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_fastapi.py +11 -3
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_redis.py +0 -75
- dycw_utilities-0.118.0/src/tests/test_slack_sdk.py +83 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sqlalchemy.py +0 -30
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/asyncio.py +40 -234
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/fastapi.py +3 -8
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/redis.py +1 -18
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/slack_sdk.py +2 -68
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sqlalchemy.py +1 -44
- dycw_utilities-0.117.0/src/tests/scripts/test_async_service/__init__.py +0 -45
- dycw_utilities-0.117.0/src/tests/scripts/test_async_service/__main__.py +0 -6
- dycw_utilities-0.117.0/src/tests/scripts/test_async_service/run.sh +0 -3
- dycw_utilities-0.117.0/src/tests/scripts/test_queue_processor/__init__.py +0 -51
- dycw_utilities-0.117.0/src/tests/scripts/test_queue_processor/__main__.py +0 -6
- dycw_utilities-0.117.0/src/tests/scripts/test_queue_processor/run.sh +0 -3
- dycw_utilities-0.117.0/src/tests/test_slack_sdk.py +0 -190
- dycw_utilities-0.117.0/src/tests/test_traceback_funcs/__init__.py +0 -1
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/.gitignore +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/LICENSE +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/README.md +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_astor.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.117.0/src/tests/scripts → dycw_utilities-0.118.0/src/tests/test_traceback_funcs}/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/astor.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/click.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/git.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/http.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/math.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/os.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/period.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/random.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/re.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/text.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/types.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/version.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.117.0 → dycw_utilities-0.118.0}/src/utilities/zoneinfo.py +0 -0
@@ -92,7 +92,7 @@ dependencies = [
|
|
92
92
|
name = "dycw-utilities"
|
93
93
|
readme = "README.md"
|
94
94
|
requires-python = ">= 3.12"
|
95
|
-
version = "0.
|
95
|
+
version = "0.118.0"
|
96
96
|
|
97
97
|
[project.optional-dependencies]
|
98
98
|
test = [
|
@@ -334,7 +334,7 @@ zzz-test-zoneinfo = [
|
|
334
334
|
# bump-my-version
|
335
335
|
[tool.bumpversion]
|
336
336
|
allow_dirty = true
|
337
|
-
current_version = "0.
|
337
|
+
current_version = "0.118.0"
|
338
338
|
|
339
339
|
[[tool.bumpversion.files]]
|
340
340
|
filename = "src/utilities/__init__.py"
|
@@ -1,23 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from asyncio import
|
4
|
-
CancelledError,
|
5
|
-
Event,
|
6
|
-
PriorityQueue,
|
7
|
-
Queue,
|
8
|
-
TaskGroup,
|
9
|
-
run,
|
10
|
-
sleep,
|
11
|
-
timeout,
|
12
|
-
)
|
13
|
-
from collections import Counter
|
3
|
+
from asyncio import CancelledError, Event, Queue, TaskGroup, run, sleep, timeout
|
14
4
|
from dataclasses import dataclass, field
|
15
5
|
from functools import partial
|
16
6
|
from itertools import chain, count
|
17
7
|
from re import search
|
18
8
|
from typing import TYPE_CHECKING, Self, override
|
19
9
|
|
20
|
-
from hypothesis import Phase, given, settings
|
10
|
+
from hypothesis import HealthCheck, Phase, given, settings
|
21
11
|
from hypothesis.strategies import (
|
22
12
|
DataObject,
|
23
13
|
data,
|
@@ -28,20 +18,17 @@ from hypothesis.strategies import (
|
|
28
18
|
permutations,
|
29
19
|
sampled_from,
|
30
20
|
)
|
31
|
-
from pytest import
|
21
|
+
from pytest import LogCaptureFixture, mark, raises
|
32
22
|
|
33
23
|
from utilities.asyncio import (
|
34
|
-
AsyncLoopingService,
|
35
|
-
AsyncService,
|
36
24
|
EnhancedTaskGroup,
|
37
|
-
ExceptionProcessor,
|
38
25
|
InfiniteLooper,
|
39
26
|
InfiniteLooperError,
|
40
27
|
InfiniteQueueLooper,
|
41
28
|
InfiniteQueueLooperError,
|
42
|
-
QueueProcessor,
|
43
29
|
UniquePriorityQueue,
|
44
30
|
UniqueQueue,
|
31
|
+
_DurationOrEvery,
|
45
32
|
get_event,
|
46
33
|
get_items,
|
47
34
|
get_items_nowait,
|
@@ -54,7 +41,12 @@ from utilities.asyncio import (
|
|
54
41
|
timeout_dur,
|
55
42
|
)
|
56
43
|
from utilities.dataclasses import replace_non_sentinel
|
57
|
-
from utilities.datetime import
|
44
|
+
from utilities.datetime import (
|
45
|
+
MILLISECOND,
|
46
|
+
MINUTE,
|
47
|
+
datetime_duration_to_timedelta,
|
48
|
+
get_now,
|
49
|
+
)
|
58
50
|
from utilities.hypothesis import sentinels, text_ascii
|
59
51
|
from utilities.iterables import one, unique_everseen
|
60
52
|
from utilities.pytest import skipif_windows
|
@@ -67,200 +59,6 @@ if TYPE_CHECKING:
|
|
67
59
|
from utilities.types import Coroutine1, Duration, MaybeCallableEvent, MaybeType
|
68
60
|
|
69
61
|
|
70
|
-
class TestAsyncLoopingService:
|
71
|
-
async def test_main(self) -> None:
|
72
|
-
@dataclass(kw_only=True)
|
73
|
-
class Example(AsyncLoopingService):
|
74
|
-
counter: int = 0
|
75
|
-
|
76
|
-
@override
|
77
|
-
async def _run(self) -> None:
|
78
|
-
self.counter += 1
|
79
|
-
|
80
|
-
async with Example(duration=1.0, sleep=0.1) as service:
|
81
|
-
pass
|
82
|
-
assert 5 <= service.counter <= 15
|
83
|
-
|
84
|
-
async def test_cancel(self) -> None:
|
85
|
-
@dataclass(kw_only=True)
|
86
|
-
class Example(AsyncLoopingService):
|
87
|
-
counter: int = 0
|
88
|
-
|
89
|
-
@override
|
90
|
-
async def _run(self) -> None:
|
91
|
-
self.counter += 1
|
92
|
-
if self.counter >= 10:
|
93
|
-
raise CancelledError
|
94
|
-
|
95
|
-
async with Example(sleep=0.1) as service:
|
96
|
-
pass
|
97
|
-
assert 5 <= service.counter <= 15
|
98
|
-
|
99
|
-
async def test_sleep_after_failure(self) -> None:
|
100
|
-
@dataclass(kw_only=True)
|
101
|
-
class Example(AsyncLoopingService):
|
102
|
-
counter: int = 0
|
103
|
-
errors: Counter[type[Exception]] = field(default_factory=Counter)
|
104
|
-
|
105
|
-
@override
|
106
|
-
async def _run(self) -> None:
|
107
|
-
self.counter += 1
|
108
|
-
if self.counter % 2 == 0:
|
109
|
-
raise ValueError
|
110
|
-
|
111
|
-
@override
|
112
|
-
async def _run_failure(self, error: Exception, /) -> None:
|
113
|
-
self.errors.update([type(error)])
|
114
|
-
|
115
|
-
async with Example(duration=1.0, sleep=0.1) as service:
|
116
|
-
pass
|
117
|
-
assert 5 <= service.counter <= 15
|
118
|
-
assert 3 <= service.errors[ValueError] <= 7
|
119
|
-
|
120
|
-
async def test_failure(self) -> None:
|
121
|
-
class CustomError(Exception): ...
|
122
|
-
|
123
|
-
@dataclass(kw_only=True)
|
124
|
-
class Example(AsyncLoopingService):
|
125
|
-
counter: int = 0
|
126
|
-
failed: bool = False
|
127
|
-
|
128
|
-
@override
|
129
|
-
async def _run(self) -> None:
|
130
|
-
self.counter += 1
|
131
|
-
if self.counter >= 5:
|
132
|
-
raise CustomError
|
133
|
-
|
134
|
-
with raises(CustomError):
|
135
|
-
async with Example(sleep=0.1):
|
136
|
-
pass
|
137
|
-
|
138
|
-
|
139
|
-
class TestAsyncService:
|
140
|
-
async def test_main(self) -> None:
|
141
|
-
@dataclass(kw_only=True)
|
142
|
-
class Example(AsyncService):
|
143
|
-
running: bool = False
|
144
|
-
|
145
|
-
@override
|
146
|
-
async def _start(self) -> None:
|
147
|
-
self.running = True
|
148
|
-
|
149
|
-
@override
|
150
|
-
async def stop(self) -> None:
|
151
|
-
self.running = False
|
152
|
-
await super().stop()
|
153
|
-
|
154
|
-
service = Example(duration=0.1)
|
155
|
-
for _ in range(2):
|
156
|
-
assert not service.running
|
157
|
-
async with service:
|
158
|
-
assert service.running
|
159
|
-
async with service:
|
160
|
-
assert service.running
|
161
|
-
assert service.running
|
162
|
-
assert not service.running
|
163
|
-
|
164
|
-
async def test_timeout(self) -> None:
|
165
|
-
@dataclass(kw_only=True)
|
166
|
-
class Example(AsyncService):
|
167
|
-
running: bool = False
|
168
|
-
|
169
|
-
@override
|
170
|
-
async def _start(self) -> None:
|
171
|
-
self.running = True
|
172
|
-
|
173
|
-
@override
|
174
|
-
async def stop(self) -> None:
|
175
|
-
self.running = False
|
176
|
-
await super().stop()
|
177
|
-
|
178
|
-
service = Example()
|
179
|
-
try:
|
180
|
-
async with timeout_dur(duration=0.05), service:
|
181
|
-
await sleep(0.1)
|
182
|
-
except TimeoutError:
|
183
|
-
assert not service.running
|
184
|
-
|
185
|
-
@mark.parametrize(
|
186
|
-
("duration", "expected"),
|
187
|
-
[
|
188
|
-
param(0.5, approx(5, abs=1)),
|
189
|
-
param(1.0, approx(10, abs=1)),
|
190
|
-
param(1.5, 10),
|
191
|
-
param(None, 10),
|
192
|
-
],
|
193
|
-
)
|
194
|
-
async def test_cancellation(
|
195
|
-
self, *, duration: Duration | None, expected: int
|
196
|
-
) -> None:
|
197
|
-
class Example(AsyncService):
|
198
|
-
counter: int = 0
|
199
|
-
|
200
|
-
@override
|
201
|
-
async def _start(self) -> None:
|
202
|
-
for _ in range(10):
|
203
|
-
self.counter += 1
|
204
|
-
await sleep(0.1)
|
205
|
-
raise CancelledError
|
206
|
-
|
207
|
-
async with Example(duration=duration) as service:
|
208
|
-
...
|
209
|
-
assert service.counter == expected
|
210
|
-
|
211
|
-
async def test_extra_context_managers(self) -> None:
|
212
|
-
@dataclass(kw_only=True)
|
213
|
-
class Inner(AsyncService):
|
214
|
-
duration: Duration | None = 0.1
|
215
|
-
running: bool = False
|
216
|
-
|
217
|
-
@override
|
218
|
-
async def _start(self) -> None:
|
219
|
-
self.running = True
|
220
|
-
|
221
|
-
@override
|
222
|
-
async def stop(self) -> None:
|
223
|
-
self.running = False
|
224
|
-
await super().stop()
|
225
|
-
|
226
|
-
@dataclass(kw_only=True)
|
227
|
-
class Outer(AsyncService):
|
228
|
-
duration: Duration | None = 0.1
|
229
|
-
running: bool = False
|
230
|
-
inner: Inner = field(default_factory=Inner, init=False, repr=False)
|
231
|
-
|
232
|
-
@override
|
233
|
-
async def _start(self) -> None:
|
234
|
-
self.running = True
|
235
|
-
_ = await self._stack.enter_async_context(self.inner)
|
236
|
-
|
237
|
-
@override
|
238
|
-
async def stop(self) -> None:
|
239
|
-
self.running = False
|
240
|
-
await super().stop()
|
241
|
-
|
242
|
-
outer = Outer()
|
243
|
-
for _ in range(2):
|
244
|
-
assert not outer.running
|
245
|
-
assert not outer.inner.running
|
246
|
-
async with outer:
|
247
|
-
assert outer.running
|
248
|
-
assert outer.inner.running
|
249
|
-
assert not outer.running
|
250
|
-
assert not outer.inner.running
|
251
|
-
|
252
|
-
def test_repr(self) -> None:
|
253
|
-
class Example(AsyncService):
|
254
|
-
@override
|
255
|
-
async def _start(self) -> None:
|
256
|
-
await sleep(0.01)
|
257
|
-
|
258
|
-
service = Example()
|
259
|
-
result = repr(service)
|
260
|
-
expected = "TestAsyncService.test_repr.<locals>.Example(duration=None)"
|
261
|
-
assert result == expected
|
262
|
-
|
263
|
-
|
264
62
|
class TestEnhancedTaskGroup:
|
265
63
|
async def test_max_tasks_disabled(self) -> None:
|
266
64
|
with Timer() as timer:
|
@@ -299,18 +97,6 @@ class TestEnhancedTaskGroup:
|
|
299
97
|
assert isinstance(error, CustomError)
|
300
98
|
|
301
99
|
|
302
|
-
class TestExceptionProcessor:
|
303
|
-
async def test_main(self) -> None:
|
304
|
-
processor = ExceptionProcessor()
|
305
|
-
|
306
|
-
class CustomError(Exception): ...
|
307
|
-
|
308
|
-
with raises(CustomError): # noqa: PT012
|
309
|
-
async with processor:
|
310
|
-
processor.enqueue(CustomError)
|
311
|
-
await sleep(0.1)
|
312
|
-
|
313
|
-
|
314
100
|
class TestGetEvent:
|
315
101
|
def test_event(self) -> None:
|
316
102
|
event = Event()
|
@@ -343,8 +129,8 @@ class TestGetEvent:
|
|
343
129
|
|
344
130
|
|
345
131
|
class TestInfiniteLooper:
|
346
|
-
@given(n=integers(10, 11))
|
347
|
-
async def test_main(self, *, n: int) -> None:
|
132
|
+
@given(n=integers(10, 11), sleep_core=sampled_from([0.1, ("every", 0.1)]))
|
133
|
+
async def test_main(self, *, n: int, sleep_core: _DurationOrEvery) -> None:
|
348
134
|
class TrueError(BaseException): ...
|
349
135
|
|
350
136
|
class FalseError(BaseException): ...
|
@@ -370,7 +156,7 @@ class TestInfiniteLooper:
|
|
370
156
|
yield (True, TrueError)
|
371
157
|
yield (False, FalseError)
|
372
158
|
|
373
|
-
looper = Example(sleep_core=
|
159
|
+
looper = Example(sleep_core=sleep_core)
|
374
160
|
match n % 2 == 0:
|
375
161
|
case True:
|
376
162
|
with raises(TrueError):
|
@@ -522,7 +308,24 @@ class TestInfiniteLooper:
|
|
522
308
|
assert 1 <= looper.counter <= 6
|
523
309
|
|
524
310
|
@given(logger=just("logger") | none())
|
525
|
-
|
311
|
+
@mark.parametrize(
|
312
|
+
("sleep_restart", "desc"),
|
313
|
+
[
|
314
|
+
(60.0, "for 0:01:00"),
|
315
|
+
(MINUTE, "for 0:01:00"),
|
316
|
+
(("every", 60), "until next 0:01:00"),
|
317
|
+
(("every", MINUTE), "until next 0:01:00"),
|
318
|
+
],
|
319
|
+
)
|
320
|
+
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
321
|
+
async def test_error_upon_initialize(
|
322
|
+
self,
|
323
|
+
*,
|
324
|
+
sleep_restart: _DurationOrEvery,
|
325
|
+
desc: str,
|
326
|
+
logger: str | None,
|
327
|
+
caplog: LogCaptureFixture,
|
328
|
+
) -> None:
|
526
329
|
class CustomError(Exception): ...
|
527
330
|
|
528
331
|
@dataclass(kw_only=True)
|
@@ -535,13 +338,34 @@ class TestInfiniteLooper:
|
|
535
338
|
async def _core(self) -> None:
|
536
339
|
raise NotImplementedError
|
537
340
|
|
538
|
-
looper = Example(sleep_core=0.1, logger=logger)
|
341
|
+
looper = Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger)
|
539
342
|
with raises(TimeoutError):
|
540
343
|
async with timeout_dur(duration=0.5):
|
541
344
|
_ = await looper()
|
345
|
+
if logger is not None:
|
346
|
+
message = caplog.messages[0]
|
347
|
+
expected = f"'Example' encountered 'CustomError()' whilst initializing; sleeping {desc}..."
|
348
|
+
assert message == expected
|
542
349
|
|
543
350
|
@given(logger=just("logger") | none())
|
544
|
-
|
351
|
+
@mark.parametrize(
|
352
|
+
("sleep_restart", "desc"),
|
353
|
+
[
|
354
|
+
(60.0, "for 0:01:00"),
|
355
|
+
(MINUTE, "for 0:01:00"),
|
356
|
+
(("every", 60), "until next 0:01:00"),
|
357
|
+
(("every", MINUTE), "until next 0:01:00"),
|
358
|
+
],
|
359
|
+
)
|
360
|
+
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
361
|
+
async def test_error_group_upon_coroutines(
|
362
|
+
self,
|
363
|
+
*,
|
364
|
+
sleep_restart: _DurationOrEvery,
|
365
|
+
desc: str,
|
366
|
+
logger: str | None,
|
367
|
+
caplog: LogCaptureFixture,
|
368
|
+
) -> None:
|
545
369
|
class CustomError(Exception): ...
|
546
370
|
|
547
371
|
@dataclass(kw_only=True)
|
@@ -556,10 +380,14 @@ class TestInfiniteLooper:
|
|
556
380
|
) -> Iterator[tuple[None, MaybeType[BaseException]]]:
|
557
381
|
yield (None, CustomError)
|
558
382
|
|
559
|
-
looper = Example(sleep_core=0.1, logger=logger)
|
383
|
+
looper = Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger)
|
560
384
|
with raises(TimeoutError):
|
561
385
|
async with timeout_dur(duration=0.5):
|
562
386
|
_ = await looper()
|
387
|
+
if logger is not None:
|
388
|
+
message = caplog.messages[0]
|
389
|
+
expected = f"'Example' encountered 'CustomError()'; sleeping {desc}..."
|
390
|
+
assert message == expected
|
563
391
|
|
564
392
|
async def test_error_no_event_found(self) -> None:
|
565
393
|
@dataclass(kw_only=True)
|
@@ -746,169 +574,6 @@ class TestPutAndGetItemsNoWait:
|
|
746
574
|
assert result == xs[:max_size]
|
747
575
|
|
748
576
|
|
749
|
-
class TestQueueProcessor:
|
750
|
-
async def test_one_processor_slow_tasks(self) -> None:
|
751
|
-
@dataclass(kw_only=True)
|
752
|
-
class Example(QueueProcessor[int]):
|
753
|
-
output: set[int] = field(default_factory=set)
|
754
|
-
|
755
|
-
@override
|
756
|
-
async def _process_item(self, item: int, /) -> None:
|
757
|
-
self.output.add(item)
|
758
|
-
|
759
|
-
async with Example() as processor:
|
760
|
-
|
761
|
-
async def add_tasks() -> None:
|
762
|
-
for i in range(10):
|
763
|
-
processor.enqueue(i)
|
764
|
-
await sleep(0.1)
|
765
|
-
|
766
|
-
async def run_until_empty() -> None:
|
767
|
-
await sleep(0.5)
|
768
|
-
await processor.run_until_empty()
|
769
|
-
|
770
|
-
async with TaskGroup() as tg:
|
771
|
-
_ = tg.create_task(add_tasks())
|
772
|
-
_ = tg.create_task(run_until_empty())
|
773
|
-
|
774
|
-
assert len(processor.output) == 10
|
775
|
-
|
776
|
-
async def test_one_processor_slow_run(self) -> None:
|
777
|
-
@dataclass(kw_only=True)
|
778
|
-
class Example(QueueProcessor[int]):
|
779
|
-
output: set[int] = field(default_factory=set)
|
780
|
-
|
781
|
-
@override
|
782
|
-
async def _process_item(self, item: int, /) -> None:
|
783
|
-
self.output.add(item)
|
784
|
-
await sleep(0.01)
|
785
|
-
|
786
|
-
async with Example() as processor:
|
787
|
-
processor.enqueue(*range(10))
|
788
|
-
await processor.run_until_empty()
|
789
|
-
assert len(processor.output) == 10
|
790
|
-
|
791
|
-
@given(n=integers(1, 10))
|
792
|
-
async def test_one_processor_continually_adding(self, *, n: int) -> None:
|
793
|
-
@dataclass(kw_only=True)
|
794
|
-
class Example(QueueProcessor[int]):
|
795
|
-
output: set[int] = field(default_factory=set)
|
796
|
-
|
797
|
-
@override
|
798
|
-
async def _process_item(self, item: int, /) -> None:
|
799
|
-
self.output.add(item)
|
800
|
-
|
801
|
-
async with Example() as processor:
|
802
|
-
for i in range(n):
|
803
|
-
processor.enqueue(i)
|
804
|
-
await sleep(0.01)
|
805
|
-
assert len(processor.output) == n
|
806
|
-
|
807
|
-
async def test_two_processors(self) -> None:
|
808
|
-
@dataclass(kw_only=True)
|
809
|
-
class First(QueueProcessor[int]):
|
810
|
-
second: Second
|
811
|
-
output: set[int] = field(default_factory=set)
|
812
|
-
|
813
|
-
@override
|
814
|
-
async def _process_item(self, item: int, /) -> None:
|
815
|
-
self.second.enqueue(item)
|
816
|
-
self.output.add(item)
|
817
|
-
await sleep(0.1)
|
818
|
-
|
819
|
-
@dataclass(kw_only=True)
|
820
|
-
class Second(QueueProcessor[int]):
|
821
|
-
output: set[int] = field(default_factory=set)
|
822
|
-
|
823
|
-
@override
|
824
|
-
async def _process_item(self, item: int, /) -> None:
|
825
|
-
self.output.add(item)
|
826
|
-
await sleep(0.01)
|
827
|
-
|
828
|
-
async with Second() as second, First(second=second) as first:
|
829
|
-
|
830
|
-
async def yield_tasks() -> None:
|
831
|
-
first.enqueue(*range(10))
|
832
|
-
await first.run_until_empty()
|
833
|
-
|
834
|
-
await yield_tasks()
|
835
|
-
assert len(first.output) == 10
|
836
|
-
assert len(second.output) == 10
|
837
|
-
|
838
|
-
@mark.parametrize("duration", [param(0.1), param(0.5), param(1.0), param(1.5)])
|
839
|
-
async def test_cancellation(self, *, duration: float) -> None:
|
840
|
-
@dataclass(kw_only=True)
|
841
|
-
class Example(QueueProcessor[int]):
|
842
|
-
output: set[int] = field(default_factory=set)
|
843
|
-
|
844
|
-
@override
|
845
|
-
async def _process_item(self, item: int, /) -> None:
|
846
|
-
self.output.add(item)
|
847
|
-
await sleep(0.1)
|
848
|
-
|
849
|
-
async with Example(duration=duration) as processor:
|
850
|
-
processor.enqueue(*range(10))
|
851
|
-
assert processor.output == set(range(10))
|
852
|
-
|
853
|
-
async def test_empty(self) -> None:
|
854
|
-
class Example(QueueProcessor[int]):
|
855
|
-
@override
|
856
|
-
async def _process_item(self, item: int, /) -> None:
|
857
|
-
_ = item
|
858
|
-
|
859
|
-
processor = Example()
|
860
|
-
assert processor.empty()
|
861
|
-
processor.enqueue(0)
|
862
|
-
assert not processor.empty()
|
863
|
-
|
864
|
-
@given(n=integers(0, 10))
|
865
|
-
async def test_get_items_nowait(self, *, n: int) -> None:
|
866
|
-
@dataclass(kw_only=True)
|
867
|
-
class Example(QueueProcessor[int]):
|
868
|
-
output: set[int] = field(default_factory=set)
|
869
|
-
|
870
|
-
@override
|
871
|
-
async def _process_item(self, _: int, /) -> None:
|
872
|
-
items = self._get_items_nowait()
|
873
|
-
self.output.add(len(items))
|
874
|
-
|
875
|
-
processor = Example()
|
876
|
-
processor.enqueue(*range(n + 1))
|
877
|
-
await processor._run()
|
878
|
-
result = one(processor.output)
|
879
|
-
assert result == n
|
880
|
-
|
881
|
-
@given(n=integers(0, 10))
|
882
|
-
async def test_len(self, *, n: int) -> None:
|
883
|
-
class Example(QueueProcessor[int]):
|
884
|
-
@override
|
885
|
-
async def _process_item(self, item: int) -> None:
|
886
|
-
_ = item
|
887
|
-
|
888
|
-
processor = Example()
|
889
|
-
assert len(processor) == 0
|
890
|
-
processor.enqueue(*range(n))
|
891
|
-
assert len(processor) == n
|
892
|
-
|
893
|
-
@given(data=data(), texts=lists(text_ascii(min_size=1), min_size=1))
|
894
|
-
async def test_priority_queue(self, *, data: DataObject, texts: list[str]) -> None:
|
895
|
-
@dataclass(kw_only=True)
|
896
|
-
class Example(QueueProcessor[tuple[int, str]]):
|
897
|
-
output: set[str] = field(default_factory=set)
|
898
|
-
|
899
|
-
@override
|
900
|
-
async def _process_item(self, item: tuple[int, str]) -> None:
|
901
|
-
_, text = item
|
902
|
-
self.output.add(text)
|
903
|
-
|
904
|
-
processor = Example(queue_type=PriorityQueue)
|
905
|
-
items = data.draw(permutations(list(enumerate(texts))))
|
906
|
-
processor.enqueue(*items)
|
907
|
-
await processor._run()
|
908
|
-
result = one(processor.output)
|
909
|
-
assert result == texts[0]
|
910
|
-
|
911
|
-
|
912
577
|
class TestUniquePriorityQueue:
|
913
578
|
@given(data=data(), texts=lists(text_ascii(min_size=1), min_size=1, unique=True))
|
914
579
|
async def test_main(self, *, data: DataObject, texts: list[str]) -> None:
|
@@ -955,10 +620,7 @@ class TestSleepDur:
|
|
955
620
|
|
956
621
|
class TestSleepUntil:
|
957
622
|
async def test_main(self) -> None:
|
958
|
-
|
959
|
-
with Timer() as timer:
|
960
|
-
await sleep_until(now + 10 * MILLISECOND)
|
961
|
-
assert timer >= datetime_duration_to_timedelta(5 * MILLISECOND)
|
623
|
+
await sleep_until(get_now() + 10 * MILLISECOND)
|
962
624
|
|
963
625
|
|
964
626
|
class TestSleepUntilRounded:
|
@@ -3,7 +3,10 @@ from __future__ import annotations
|
|
3
3
|
from asyncio import sleep
|
4
4
|
from re import search
|
5
5
|
|
6
|
+
from pytest import raises
|
7
|
+
|
6
8
|
from tests.conftest import SKIPIF_CI
|
9
|
+
from utilities.asyncio import EnhancedTaskGroup
|
7
10
|
from utilities.fastapi import PingReceiver
|
8
11
|
|
9
12
|
|
@@ -11,14 +14,19 @@ class TestPingReceiver:
|
|
11
14
|
@SKIPIF_CI
|
12
15
|
async def test_main(self) -> None:
|
13
16
|
port = 5465
|
17
|
+
receiver = PingReceiver(port=port)
|
14
18
|
assert await PingReceiver.ping(port) is False
|
15
19
|
await sleep(0.1)
|
16
|
-
|
20
|
+
|
21
|
+
async def run_test() -> None:
|
17
22
|
await sleep(0.1)
|
18
23
|
result = await PingReceiver.ping(port)
|
19
24
|
assert isinstance(result, str)
|
20
25
|
assert search(
|
21
26
|
r"pong @ \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,6}", result
|
22
27
|
)
|
23
|
-
|
24
|
-
|
28
|
+
|
29
|
+
with raises(ExceptionGroup): # noqa: PT012
|
30
|
+
async with EnhancedTaskGroup(timeout=1.0) as tg:
|
31
|
+
_ = tg.create_task(receiver())
|
32
|
+
_ = tg.create_task(run_test())
|
@@ -29,7 +29,6 @@ from utilities.hypothesis import (
|
|
29
29
|
)
|
30
30
|
from utilities.orjson import deserialize, serialize
|
31
31
|
from utilities.redis import (
|
32
|
-
Publisher,
|
33
32
|
PublisherIQL,
|
34
33
|
PublisherIQLError,
|
35
34
|
publish,
|
@@ -119,80 +118,6 @@ class TestPublishAndSubscribe:
|
|
119
118
|
_ = task.cancel()
|
120
119
|
|
121
120
|
|
122
|
-
class TestPublisher:
|
123
|
-
@given(
|
124
|
-
data=data(),
|
125
|
-
channel=text_ascii(min_size=1).map(
|
126
|
-
lambda c: f"{get_class_name(TestPublisher)}_obj_ser_{c}"
|
127
|
-
),
|
128
|
-
obj=make_objects(),
|
129
|
-
)
|
130
|
-
@mark.flaky
|
131
|
-
@settings(
|
132
|
-
max_examples=1,
|
133
|
-
phases={Phase.generate},
|
134
|
-
suppress_health_check={HealthCheck.function_scoped_fixture},
|
135
|
-
)
|
136
|
-
@SKIPIF_CI_AND_NOT_LINUX
|
137
|
-
async def test_main(
|
138
|
-
self, *, capsys: CaptureFixture, data: DataObject, channel: str, obj: Any
|
139
|
-
) -> None:
|
140
|
-
async with yield_test_redis(data) as test:
|
141
|
-
|
142
|
-
async def listener() -> None:
|
143
|
-
async for msg in subscribe(
|
144
|
-
test.redis.pubsub(), channel, deserializer=deserialize
|
145
|
-
):
|
146
|
-
print(msg) # noqa: T201
|
147
|
-
|
148
|
-
task = create_task(listener())
|
149
|
-
await sleep(0.1)
|
150
|
-
|
151
|
-
async with Publisher(redis=test.redis, serializer=serialize) as publisher:
|
152
|
-
publisher.enqueue((channel, obj))
|
153
|
-
await sleep(0.1)
|
154
|
-
|
155
|
-
try:
|
156
|
-
out = capsys.readouterr().out
|
157
|
-
expected = f"{obj}\n"
|
158
|
-
assert out == expected
|
159
|
-
finally:
|
160
|
-
_ = task.cancel()
|
161
|
-
|
162
|
-
@given(
|
163
|
-
data=data(),
|
164
|
-
channel=text_ascii(min_size=1).map(
|
165
|
-
lambda c: f"{get_class_name(TestPublisher)}_text_no_ser_{c}"
|
166
|
-
),
|
167
|
-
text=text_ascii(min_size=1),
|
168
|
-
)
|
169
|
-
@settings(
|
170
|
-
max_examples=1,
|
171
|
-
phases={Phase.generate},
|
172
|
-
suppress_health_check={HealthCheck.function_scoped_fixture},
|
173
|
-
)
|
174
|
-
@SKIPIF_CI_AND_NOT_LINUX
|
175
|
-
async def test_text_without_serialize(
|
176
|
-
self, *, capsys: CaptureFixture, data: DataObject, channel: str, text: str
|
177
|
-
) -> None:
|
178
|
-
async with yield_test_redis(data) as test:
|
179
|
-
|
180
|
-
async def listener() -> None:
|
181
|
-
async for msg in subscribe(test.redis.pubsub(), channel):
|
182
|
-
print(msg) # noqa: T201
|
183
|
-
|
184
|
-
task = create_task(listener())
|
185
|
-
await sleep(0.1)
|
186
|
-
_ = await publish(test.redis, channel, text)
|
187
|
-
await sleep(0.1)
|
188
|
-
try:
|
189
|
-
out = capsys.readouterr().out
|
190
|
-
expected = f"{text.encode()}\n"
|
191
|
-
assert out == expected
|
192
|
-
finally:
|
193
|
-
_ = task.cancel()
|
194
|
-
|
195
|
-
|
196
121
|
class TestPublisherIQL:
|
197
122
|
@given(
|
198
123
|
data=data(),
|