dycw-utilities 0.121.1__tar.gz → 0.122.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.121.1 → dycw_utilities-0.122.0}/PKG-INFO +1 -1
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/pyproject.toml +2 -2
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_asyncio.py +316 -142
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_fastapi.py +4 -11
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_redis.py +13 -13
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_slack_sdk.py +3 -20
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_sqlalchemy.py +1 -9
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/asyncio.py +132 -29
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/fastapi.py +4 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/redis.py +1 -1
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/sqlalchemy.py +1 -1
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/.gitignore +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/LICENSE +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/README.md +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_astor.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/astor.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/click.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/git.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/http.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/math.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/os.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/period.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/random.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/re.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/text.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/types.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/version.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.0}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.121.1 → dycw_utilities-0.122.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.122.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.122.0"
|
338
338
|
|
339
339
|
[[tool.bumpversion.files]]
|
340
340
|
filename = "src/utilities/__init__.py"
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
|
|
5
5
|
from functools import partial
|
6
6
|
from itertools import chain, count
|
7
7
|
from re import search
|
8
|
-
from typing import TYPE_CHECKING, Any, Self, cast, override
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Self, cast, override
|
9
9
|
|
10
10
|
from hypothesis import HealthCheck, Phase, given, settings
|
11
11
|
from hypothesis.strategies import (
|
@@ -18,13 +18,12 @@ from hypothesis.strategies import (
|
|
18
18
|
permutations,
|
19
19
|
sampled_from,
|
20
20
|
)
|
21
|
-
from pytest import LogCaptureFixture, mark, raises
|
21
|
+
from pytest import LogCaptureFixture, mark, param, raises
|
22
22
|
|
23
23
|
from utilities.asyncio import (
|
24
24
|
EnhancedTaskGroup,
|
25
25
|
InfiniteLooper,
|
26
26
|
InfiniteQueueLooper,
|
27
|
-
InfiniteQueueLooperError,
|
28
27
|
UniquePriorityQueue,
|
29
28
|
UniqueQueue,
|
30
29
|
_InfiniteLooperDefaultEventError,
|
@@ -135,15 +134,146 @@ class TestGetEvent:
|
|
135
134
|
|
136
135
|
|
137
136
|
class TestInfiniteLooper:
|
138
|
-
|
139
|
-
|
140
|
-
|
137
|
+
sleep_restart_cases: ClassVar[list[Any]] = [
|
138
|
+
param(60.0, "for 0:01:00"),
|
139
|
+
param(MINUTE, "for 0:01:00"),
|
140
|
+
param(("every", 60), "until next 0:01:00"),
|
141
|
+
param(("every", MINUTE), "until next 0:01:00"),
|
142
|
+
]
|
143
|
+
|
144
|
+
async def test_main_no_errors(self) -> None:
|
145
|
+
@dataclass(kw_only=True)
|
146
|
+
class Example(InfiniteLooper[None]):
|
147
|
+
counter: int = 0
|
148
|
+
|
149
|
+
@override
|
150
|
+
async def _initialize(self) -> None:
|
151
|
+
self.counter = 0
|
152
|
+
|
153
|
+
@override
|
154
|
+
async def _core(self) -> None:
|
155
|
+
self.counter += 1
|
156
|
+
|
157
|
+
async with timeout_dur(duration=1.0), Example(sleep_core=0.05) as looper:
|
158
|
+
pass
|
159
|
+
assert 15 <= looper.counter <= 25
|
160
|
+
|
161
|
+
async def test_main_with_errors(self) -> None:
|
162
|
+
class CustomError(Exception): ...
|
163
|
+
|
164
|
+
@dataclass(kw_only=True)
|
165
|
+
class Example(InfiniteLooper[None]):
|
166
|
+
initializations: int = 0
|
167
|
+
counter: int = 0
|
168
|
+
teardowns: int = 0
|
169
|
+
|
170
|
+
@override
|
171
|
+
async def _initialize(self) -> None:
|
172
|
+
self.initializations += 1
|
173
|
+
self.counter = 0
|
174
|
+
|
175
|
+
@override
|
176
|
+
async def _core(self) -> None:
|
177
|
+
self.counter += 1
|
178
|
+
if self.counter >= 5:
|
179
|
+
raise CustomError
|
141
180
|
|
142
|
-
|
181
|
+
@override
|
182
|
+
async def _teardown(self) -> None:
|
183
|
+
self.teardowns += 1
|
184
|
+
|
185
|
+
async with (
|
186
|
+
timeout_dur(duration=1.0),
|
187
|
+
Example(sleep_core=0.05, sleep_restart=0.05) as looper,
|
188
|
+
):
|
189
|
+
pass
|
190
|
+
assert 3 <= looper.initializations <= 5
|
191
|
+
assert 0 <= looper.counter <= 5
|
192
|
+
assert 3 <= looper.teardowns <= 5
|
193
|
+
|
194
|
+
async def test_cancelled_error(self) -> None:
|
195
|
+
@dataclass(kw_only=True)
|
196
|
+
class Example(InfiniteLooper[None]):
|
197
|
+
counter: int = 0
|
198
|
+
|
199
|
+
@override
|
200
|
+
async def _core(self) -> None:
|
201
|
+
self.counter += 1
|
202
|
+
if self.counter >= 5:
|
203
|
+
raise CancelledError
|
204
|
+
|
205
|
+
async with Example(sleep_core=0.05) as service:
|
206
|
+
pass
|
207
|
+
assert 5 <= service.counter <= 15
|
208
|
+
|
209
|
+
async def test_duration(self) -> None:
|
210
|
+
@dataclass(kw_only=True)
|
211
|
+
class Example(InfiniteLooper[None]):
|
212
|
+
counter: int = 0
|
213
|
+
|
214
|
+
@override
|
215
|
+
async def _initialize(self) -> None:
|
216
|
+
self.counter = 0
|
217
|
+
|
218
|
+
@override
|
219
|
+
async def _core(self) -> None:
|
220
|
+
self.counter += 1
|
221
|
+
|
222
|
+
async with Example(duration=1.0, sleep_core=0.05) as looper:
|
223
|
+
pass
|
224
|
+
assert 15 <= looper.counter <= 25
|
225
|
+
|
226
|
+
async def test_hashable(self) -> None:
|
227
|
+
@dataclass(kw_only=True, unsafe_hash=True)
|
228
|
+
class Example(InfiniteLooper[None]): ...
|
229
|
+
|
230
|
+
looper = Example(sleep_core=0.1)
|
231
|
+
_ = hash(looper)
|
232
|
+
|
233
|
+
async def test_nested_context_manager(self) -> None:
|
234
|
+
@dataclass(kw_only=True)
|
235
|
+
class Example(InfiniteLooper[None]):
|
236
|
+
running: bool = False
|
237
|
+
|
238
|
+
@override
|
239
|
+
async def _initialize(self) -> None:
|
240
|
+
self.running = True
|
241
|
+
|
242
|
+
@override
|
243
|
+
async def _teardown(self) -> None:
|
244
|
+
self.running = False
|
245
|
+
|
246
|
+
looper = Example()
|
247
|
+
for _ in range(2):
|
248
|
+
assert not looper.running
|
249
|
+
async with timeout_dur(duration=0.2), looper:
|
250
|
+
assert looper.running
|
251
|
+
async with timeout_dur(duration=0.1), looper:
|
252
|
+
assert looper.running
|
253
|
+
assert looper.running
|
254
|
+
assert not looper.running
|
255
|
+
|
256
|
+
def test_repr(self) -> None:
|
257
|
+
@dataclass(kw_only=True)
|
258
|
+
class Example(InfiniteLooper[None]):
|
259
|
+
counter: int = 0
|
260
|
+
|
261
|
+
looper = Example()
|
262
|
+
result = repr(looper)
|
263
|
+
expected = "TestInfiniteLooper.test_repr.<locals>.Example(counter=0)"
|
264
|
+
assert result == expected
|
265
|
+
|
266
|
+
@given(n=integers(10, 11))
|
267
|
+
async def test_setting_events(self, *, n: int) -> None:
|
268
|
+
class TrueError(Exception): ...
|
269
|
+
|
270
|
+
class FalseError(Exception): ...
|
143
271
|
|
144
272
|
@dataclass(kw_only=True)
|
145
273
|
class Example(InfiniteLooper[bool]):
|
146
274
|
counter: int = 0
|
275
|
+
true_counter: int = 0
|
276
|
+
false_counter: int = 0
|
147
277
|
|
148
278
|
@override
|
149
279
|
async def _initialize(self) -> None:
|
@@ -155,28 +285,29 @@ class TestInfiniteLooper:
|
|
155
285
|
if self.counter >= n:
|
156
286
|
self._set_event(event=n % 2 == 0)
|
157
287
|
|
288
|
+
@override
|
289
|
+
def _error_upon_core(self, error: Exception, /) -> None:
|
290
|
+
if isinstance(error, TrueError):
|
291
|
+
self.true_counter += 1
|
292
|
+
elif isinstance(error, FalseError):
|
293
|
+
self.false_counter += 1
|
294
|
+
|
158
295
|
@override
|
159
296
|
def _yield_events_and_exceptions(
|
160
297
|
self,
|
161
|
-
) -> Iterator[tuple[bool, MaybeType[
|
298
|
+
) -> Iterator[tuple[bool, MaybeType[Exception]]]:
|
162
299
|
yield (True, TrueError)
|
163
300
|
yield (False, FalseError)
|
164
301
|
|
165
|
-
|
302
|
+
async with timeout_dur(duration=1.0), Example(sleep_core=0.05) as looper:
|
303
|
+
...
|
166
304
|
match n % 2 == 0:
|
167
305
|
case True:
|
168
|
-
|
169
|
-
|
306
|
+
assert looper.true_counter >= 1, looper
|
307
|
+
assert looper.false_counter == 0
|
170
308
|
case False:
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
async def test_hashable(self) -> None:
|
175
|
-
@dataclass(kw_only=True, unsafe_hash=True)
|
176
|
-
class Example(InfiniteLooper[None]): ...
|
177
|
-
|
178
|
-
looper = Example(sleep_core=0.1)
|
179
|
-
_ = hash(looper)
|
309
|
+
assert looper.true_counter == 0
|
310
|
+
assert looper.false_counter >= 1
|
180
311
|
|
181
312
|
async def test_with_coroutine_self_set_event(self) -> None:
|
182
313
|
external: int = 0
|
@@ -208,10 +339,11 @@ class TestInfiniteLooper:
|
|
208
339
|
def _yield_coroutines(self) -> Iterator[Callable[[], Coroutine1[None]]]:
|
209
340
|
yield partial(inc_external, self)
|
210
341
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
342
|
+
async with (
|
343
|
+
timeout_dur(duration=1.0),
|
344
|
+
Example(sleep_core=0.05, sleep_restart=0.05) as looper,
|
345
|
+
):
|
346
|
+
...
|
215
347
|
assert 4 <= looper.initializations <= 6
|
216
348
|
assert 0 <= looper.counter <= 7
|
217
349
|
assert 16 <= external <= 21
|
@@ -242,33 +374,39 @@ class TestInfiniteLooper:
|
|
242
374
|
def _yield_coroutines(self) -> Iterator[Callable[[], Coroutine1[None]]]:
|
243
375
|
yield dummy
|
244
376
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
377
|
+
async with (
|
378
|
+
timeout_dur(duration=1.0),
|
379
|
+
Example(sleep_core=0.05, sleep_restart=0.05) as looper,
|
380
|
+
):
|
381
|
+
...
|
249
382
|
assert 3 <= looper.initializations <= 5
|
250
383
|
assert 0 <= looper.counter <= 5
|
251
384
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
class CustomError(Exception): ...
|
385
|
+
async def test_with_looper(self) -> None:
|
386
|
+
@dataclass(kw_only=True)
|
387
|
+
class Child(InfiniteLooper[None]):
|
388
|
+
counter: int = 0
|
257
389
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
390
|
+
@override
|
391
|
+
async def _initialize(self) -> None:
|
392
|
+
self.counter = 0
|
393
|
+
|
394
|
+
@override
|
395
|
+
async def _core(self) -> None:
|
396
|
+
self.counter += 1
|
263
397
|
|
264
398
|
@dataclass(kw_only=True)
|
265
|
-
class
|
266
|
-
initializations: int = 0
|
399
|
+
class Parent(InfiniteLooper[None]):
|
267
400
|
counter: int = 0
|
401
|
+
child: Child = field(init=False, repr=False)
|
402
|
+
|
403
|
+
@override
|
404
|
+
def __post_init__(self) -> None:
|
405
|
+
super().__post_init__()
|
406
|
+
self.child = Child(sleep_core=self.sleep_core)
|
268
407
|
|
269
408
|
@override
|
270
409
|
async def _initialize(self) -> None:
|
271
|
-
self.initializations += 1
|
272
410
|
self.counter = 0
|
273
411
|
|
274
412
|
@override
|
@@ -276,15 +414,13 @@ class TestInfiniteLooper:
|
|
276
414
|
self.counter += 1
|
277
415
|
|
278
416
|
@override
|
279
|
-
def
|
280
|
-
yield
|
417
|
+
def _yield_loopers(self) -> Iterator[InfiniteLooper]:
|
418
|
+
yield self.child
|
281
419
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
assert 3 <= looper.initializations <= 5
|
287
|
-
assert 1 <= looper.counter <= 6
|
420
|
+
async with timeout_dur(duration=1.0), Parent(sleep_core=0.05) as parent:
|
421
|
+
...
|
422
|
+
assert 15 <= parent.counter <= 25
|
423
|
+
assert 15 <= parent.child.counter <= 25
|
288
424
|
|
289
425
|
async def test_error_default_event(self) -> None:
|
290
426
|
@dataclass(kw_only=True)
|
@@ -298,15 +434,7 @@ class TestInfiniteLooper:
|
|
298
434
|
raise _InfiniteLooperDefaultEventError(looper=looper)
|
299
435
|
|
300
436
|
@given(logger=just("logger") | none())
|
301
|
-
@mark.parametrize(
|
302
|
-
("sleep_restart", "desc"),
|
303
|
-
[
|
304
|
-
(60.0, "for 0:01:00"),
|
305
|
-
(MINUTE, "for 0:01:00"),
|
306
|
-
(("every", 60), "until next 0:01:00"),
|
307
|
-
(("every", MINUTE), "until next 0:01:00"),
|
308
|
-
],
|
309
|
-
)
|
437
|
+
@mark.parametrize(("sleep_restart", "desc"), sleep_restart_cases)
|
310
438
|
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
311
439
|
async def test_error_upon_initialize(
|
312
440
|
self,
|
@@ -328,27 +456,20 @@ class TestInfiniteLooper:
|
|
328
456
|
async def _core(self) -> None:
|
329
457
|
raise NotImplementedError
|
330
458
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
459
|
+
async with (
|
460
|
+
timeout_dur(duration=1.0),
|
461
|
+
Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger),
|
462
|
+
):
|
463
|
+
...
|
335
464
|
if logger is not None:
|
336
465
|
message = caplog.messages[0]
|
337
466
|
expected = f"'Example' encountered 'CustomError()' whilst initializing; sleeping {desc}..."
|
338
467
|
assert message == expected
|
339
468
|
|
340
469
|
@given(logger=just("logger") | none())
|
341
|
-
@mark.parametrize(
|
342
|
-
("sleep_restart", "desc"),
|
343
|
-
[
|
344
|
-
(60.0, "for 0:01:00"),
|
345
|
-
(MINUTE, "for 0:01:00"),
|
346
|
-
(("every", 60), "until next 0:01:00"),
|
347
|
-
(("every", MINUTE), "until next 0:01:00"),
|
348
|
-
],
|
349
|
-
)
|
470
|
+
@mark.parametrize(("sleep_restart", "desc"), sleep_restart_cases)
|
350
471
|
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
351
|
-
async def
|
472
|
+
async def test_error_upon_core(
|
352
473
|
self,
|
353
474
|
*,
|
354
475
|
sleep_restart: DurationOrEveryDuration,
|
@@ -364,15 +485,110 @@ class TestInfiniteLooper:
|
|
364
485
|
async def _core(self) -> None:
|
365
486
|
raise CustomError
|
366
487
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
488
|
+
async with (
|
489
|
+
timeout_dur(duration=1.0),
|
490
|
+
Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger),
|
491
|
+
):
|
492
|
+
...
|
371
493
|
if logger is not None:
|
372
494
|
message = caplog.messages[0]
|
373
495
|
expected = f"'Example' encountered 'CustomError()'; sleeping {desc}..."
|
374
496
|
assert message == expected
|
375
497
|
|
498
|
+
@given(logger=just("logger") | none())
|
499
|
+
@mark.parametrize(("sleep_restart", "desc"), sleep_restart_cases)
|
500
|
+
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
501
|
+
async def test_error_upon_teardown(
|
502
|
+
self,
|
503
|
+
*,
|
504
|
+
sleep_restart: DurationOrEveryDuration,
|
505
|
+
desc: str,
|
506
|
+
logger: str | None,
|
507
|
+
caplog: LogCaptureFixture,
|
508
|
+
) -> None:
|
509
|
+
class Custom1Error(Exception): ...
|
510
|
+
|
511
|
+
class Custom2Error(Exception): ...
|
512
|
+
|
513
|
+
@dataclass(kw_only=True)
|
514
|
+
class Example(InfiniteLooper[None]):
|
515
|
+
counter: int = 0
|
516
|
+
|
517
|
+
@override
|
518
|
+
async def _core(self) -> None:
|
519
|
+
self.counter += 1
|
520
|
+
if self.counter >= 5:
|
521
|
+
self._set_event()
|
522
|
+
|
523
|
+
@override
|
524
|
+
async def _teardown(self) -> None:
|
525
|
+
raise Custom2Error
|
526
|
+
|
527
|
+
@override
|
528
|
+
def _yield_events_and_exceptions(
|
529
|
+
self,
|
530
|
+
) -> Iterator[tuple[None, MaybeType[Exception]]]:
|
531
|
+
yield (None, Custom1Error)
|
532
|
+
|
533
|
+
async with (
|
534
|
+
timeout_dur(duration=1.0),
|
535
|
+
Example(sleep_core=0.1, sleep_restart=sleep_restart, logger=logger),
|
536
|
+
):
|
537
|
+
...
|
538
|
+
if logger is not None:
|
539
|
+
expected = f"'Example' encountered 'Custom2Error()' whilst tearing down; sleeping {desc}..."
|
540
|
+
assert expected in caplog.messages
|
541
|
+
|
542
|
+
@given(logger=just("logger") | none())
|
543
|
+
@mark.parametrize(("sleep_restart", "desc"), sleep_restart_cases)
|
544
|
+
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
545
|
+
async def test_error_group_upon_others(
|
546
|
+
self,
|
547
|
+
*,
|
548
|
+
sleep_restart: DurationOrEveryDuration,
|
549
|
+
desc: str,
|
550
|
+
logger: str | None,
|
551
|
+
caplog: LogCaptureFixture,
|
552
|
+
) -> None:
|
553
|
+
class CustomError(Exception): ...
|
554
|
+
|
555
|
+
async def dummy() -> None:
|
556
|
+
for i in count():
|
557
|
+
if i >= 5:
|
558
|
+
raise CustomError
|
559
|
+
await sleep(0.05)
|
560
|
+
|
561
|
+
@dataclass(kw_only=True)
|
562
|
+
class Example(InfiniteLooper[None]):
|
563
|
+
initializations: int = 0
|
564
|
+
counter: int = 0
|
565
|
+
|
566
|
+
@override
|
567
|
+
async def _initialize(self) -> None:
|
568
|
+
self.initializations += 1
|
569
|
+
self.counter = 0
|
570
|
+
|
571
|
+
@override
|
572
|
+
async def _core(self) -> None:
|
573
|
+
self.counter += 1
|
574
|
+
|
575
|
+
@override
|
576
|
+
def _yield_coroutines(self) -> Iterator[Callable[[], Coroutine1[None]]]:
|
577
|
+
yield dummy
|
578
|
+
|
579
|
+
async with (
|
580
|
+
timeout_dur(duration=1.0),
|
581
|
+
Example(sleep_core=0.05, sleep_restart=sleep_restart, logger=logger),
|
582
|
+
):
|
583
|
+
...
|
584
|
+
if logger is not None:
|
585
|
+
message = caplog.messages[0]
|
586
|
+
expected = f"""\
|
587
|
+
'Example' encountered 1 error(s):
|
588
|
+
- Error #1/1: CustomError()
|
589
|
+
Sleeping {desc}..."""
|
590
|
+
assert message == expected
|
591
|
+
|
376
592
|
async def test_error_no_event_found(self) -> None:
|
377
593
|
@dataclass(kw_only=True)
|
378
594
|
class Example(InfiniteLooper[None]):
|
@@ -388,36 +604,31 @@ class TestInfiniteLooper:
|
|
388
604
|
if self.counter >= 10:
|
389
605
|
self._set_event(event=cast("Any", "invalid"))
|
390
606
|
|
391
|
-
looper = Example()
|
392
607
|
with raises(
|
393
608
|
_InfiniteLooperNoSuchEventError,
|
394
609
|
match="'Example' does not have an event 'invalid'",
|
395
610
|
):
|
396
|
-
|
611
|
+
async with Example():
|
612
|
+
...
|
397
613
|
|
398
614
|
|
399
615
|
class TestInfiniteQueueLooper:
|
400
616
|
async def test_main(self) -> None:
|
401
617
|
@dataclass(kw_only=True)
|
402
618
|
class Example(InfiniteQueueLooper[None, int]):
|
403
|
-
|
619
|
+
counter: int = 0
|
404
620
|
|
405
621
|
@override
|
406
622
|
async def _process_items(self, *items: int) -> None:
|
407
|
-
self.
|
623
|
+
self.counter += len(items)
|
408
624
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
for i in count():
|
625
|
+
async with timeout_dur(duration=1.0), Example(sleep_core=0.05) as looper:
|
626
|
+
await sleep(0.1)
|
627
|
+
for i in range(10):
|
413
628
|
looper.put_items_nowait(i)
|
414
629
|
await sleep(0.05)
|
415
630
|
|
416
|
-
|
417
|
-
async with EnhancedTaskGroup(timeout=1.0) as tg:
|
418
|
-
_ = tg.create_task(looper())
|
419
|
-
_ = tg.create_task(add_items())
|
420
|
-
assert 15 <= len(looper.output) <= 20
|
631
|
+
assert looper.counter == 10
|
421
632
|
|
422
633
|
@given(n=integers(1, 10))
|
423
634
|
def test_len_and_empty(self, *, n: int) -> None:
|
@@ -435,20 +646,6 @@ class TestInfiniteQueueLooper:
|
|
435
646
|
assert len(looper) == n
|
436
647
|
assert not looper.empty()
|
437
648
|
|
438
|
-
async def test_no_items(self) -> None:
|
439
|
-
@dataclass(kw_only=True)
|
440
|
-
class Example(InfiniteQueueLooper[None, int]):
|
441
|
-
output: set[int] = field(default_factory=set)
|
442
|
-
|
443
|
-
@override
|
444
|
-
async def _process_items(self, *items: int) -> None:
|
445
|
-
self.output.update(items)
|
446
|
-
|
447
|
-
looper = Example(sleep_core=0.05)
|
448
|
-
with raises(TimeoutError):
|
449
|
-
async with timeout_dur(duration=0.5):
|
450
|
-
_ = await looper()
|
451
|
-
|
452
649
|
async def test_run_until_empty(self) -> None:
|
453
650
|
@dataclass(kw_only=True)
|
454
651
|
class Example(InfiniteQueueLooper[None, int]):
|
@@ -458,27 +655,17 @@ class TestInfiniteQueueLooper:
|
|
458
655
|
async def _process_items(self, *items: int) -> None:
|
459
656
|
self.output.update(items)
|
460
657
|
|
461
|
-
looper = Example(sleep_core=0.
|
462
|
-
|
463
|
-
async
|
464
|
-
|
465
|
-
looper.put_items_nowait(i)
|
466
|
-
await sleep(0.01)
|
467
|
-
|
468
|
-
with raises(ExceptionGroup): # noqa: PT012
|
469
|
-
async with EnhancedTaskGroup(timeout=1.0) as tg:
|
470
|
-
_ = tg.create_task(looper())
|
471
|
-
_ = tg.create_task(add_items())
|
472
|
-
|
473
|
-
tasks = len(looper)
|
474
|
-
assert tasks >= 1
|
475
|
-
await sleep(0.1)
|
476
|
-
assert len(looper) == tasks
|
477
|
-
await looper.run_until_empty()
|
658
|
+
looper = Example(sleep_core=0.05)
|
659
|
+
looper.put_items_nowait(*range(10))
|
660
|
+
async with looper:
|
661
|
+
await looper.run_until_empty()
|
478
662
|
assert looper.empty()
|
479
663
|
|
480
664
|
@given(logger=just("logger") | none())
|
481
|
-
|
665
|
+
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
666
|
+
async def test_error_process_items(
|
667
|
+
self, *, logger: str | None, caplog: LogCaptureFixture
|
668
|
+
) -> None:
|
482
669
|
class CustomError(Exception): ...
|
483
670
|
|
484
671
|
@dataclass(kw_only=True)
|
@@ -489,28 +676,15 @@ class TestInfiniteQueueLooper:
|
|
489
676
|
async def _process_items(self, *items: int) -> None:
|
490
677
|
raise CustomError(*items)
|
491
678
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
async with timeout_dur(duration=0.5):
|
496
|
-
_ = await looper()
|
497
|
-
|
498
|
-
async def test_error_infinite_queue_looper(self) -> None:
|
499
|
-
class CustomError(Exception): ...
|
500
|
-
|
501
|
-
@dataclass(kw_only=True)
|
502
|
-
class Example(InfiniteQueueLooper[None, int]):
|
503
|
-
@override
|
504
|
-
async def _process_items(self, *items: int) -> None:
|
505
|
-
raise CustomError(*items)
|
506
|
-
|
507
|
-
looper = Example(sleep_core=0.1)
|
508
|
-
looper.put_items_nowait(1)
|
509
|
-
with raises(
|
510
|
-
InfiniteQueueLooperError,
|
511
|
-
match=r"'Example' encountered CustomError\(1\) whilst processing 1 item\(s\): \[1\]",
|
679
|
+
async with (
|
680
|
+
timeout_dur(duration=1.0),
|
681
|
+
Example(sleep_core=0.05, logger=logger) as looper,
|
512
682
|
):
|
513
|
-
|
683
|
+
looper.put_items_nowait(1)
|
684
|
+
if logger is not None:
|
685
|
+
message = caplog.messages[0]
|
686
|
+
expected = "'Example' encountered CustomError(1) whilst processing 1 item(s) [1]; sleeping for 0:01:00..."
|
687
|
+
assert message == expected
|
514
688
|
|
515
689
|
|
516
690
|
class TestPutAndGetItems:
|
@@ -3,10 +3,8 @@ from __future__ import annotations
|
|
3
3
|
from asyncio import sleep
|
4
4
|
from re import search
|
5
5
|
|
6
|
-
from pytest import raises
|
7
|
-
|
8
6
|
from tests.conftest import SKIPIF_CI
|
9
|
-
from utilities.asyncio import
|
7
|
+
from utilities.asyncio import timeout_dur
|
10
8
|
from utilities.fastapi import PingReceiver
|
11
9
|
|
12
10
|
|
@@ -14,19 +12,14 @@ class TestPingReceiver:
|
|
14
12
|
@SKIPIF_CI
|
15
13
|
async def test_main(self) -> None:
|
16
14
|
port = 5465
|
17
|
-
receiver = PingReceiver(port=port)
|
18
15
|
assert await PingReceiver.ping(port) is False
|
19
16
|
await sleep(0.1)
|
20
|
-
|
21
|
-
async def run_test() -> None:
|
17
|
+
async with timeout_dur(duration=1.0), PingReceiver(port=port):
|
22
18
|
await sleep(0.1)
|
23
19
|
result = await PingReceiver.ping(port)
|
24
20
|
assert isinstance(result, str)
|
25
21
|
assert search(
|
26
22
|
r"pong @ \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,6}", result
|
27
23
|
)
|
28
|
-
|
29
|
-
|
30
|
-
async with EnhancedTaskGroup(timeout=1.0) as tg:
|
31
|
-
_ = tg.create_task(receiver())
|
32
|
-
_ = tg.create_task(run_test())
|
24
|
+
await sleep(0.1)
|
25
|
+
assert await PingReceiver.ping(port) is False
|