dycw-utilities 0.126.9__tar.gz → 0.126.11__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.126.9 → dycw_utilities-0.126.11}/PKG-INFO +3 -3
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/pyproject.toml +6 -5
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_asyncio.py +83 -55
- dycw_utilities-0.126.11/src/tests/test_asyncio_classes/loopers.py +310 -0
- dycw_utilities-0.126.11/src/tests/test_asyncio_classes/redis.py +28 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_hypothesis.py +0 -8
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_orjson.py +1 -1
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pottery.py +3 -7
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_redis.py +244 -198
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_sqlalchemy.py +135 -156
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_sqlalchemy_polars.py +38 -56
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_text.py +7 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/hypothesis.py +1 -19
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/redis.py +115 -5
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/sqlalchemy.py +65 -1
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/text.py +17 -0
- dycw_utilities-0.126.9/src/tests/test_asyncio_classes/loopers.py +0 -168
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/.gitignore +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/LICENSE +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/README.md +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/click.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/git.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/http.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/math.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/os.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/period.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/random.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/re.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/string.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/types.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/version.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.126.9 → dycw_utilities-0.126.11}/src/utilities/zoneinfo.py +0 -0
@@ -1,13 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: dycw-utilities
|
3
|
-
Version: 0.126.
|
3
|
+
Version: 0.126.11
|
4
4
|
Author-email: Derek Wan <d.wan@icloud.com>
|
5
5
|
License-File: LICENSE
|
6
6
|
Requires-Python: >=3.12
|
7
7
|
Requires-Dist: typing-extensions<4.14,>=4.13.1
|
8
8
|
Provides-Extra: test
|
9
9
|
Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
|
10
|
-
Requires-Dist: hypothesis<6.
|
10
|
+
Requires-Dist: hypothesis<6.134,>=6.133.0; extra == 'test'
|
11
11
|
Requires-Dist: pytest-asyncio<1.1,>=1.0.0; extra == 'test'
|
12
12
|
Requires-Dist: pytest-cov<6.2,>=6.1.1; extra == 'test'
|
13
13
|
Requires-Dist: pytest-instafail<0.6,>=0.5.0; extra == 'test'
|
@@ -77,7 +77,7 @@ Provides-Extra: zzz-test-hypothesis
|
|
77
77
|
Requires-Dist: aiosqlite<0.22,>=0.21.0; extra == 'zzz-test-hypothesis'
|
78
78
|
Requires-Dist: asyncpg<0.31,>=0.30.0; extra == 'zzz-test-hypothesis'
|
79
79
|
Requires-Dist: greenlet<3.3,>=3.2.0; extra == 'zzz-test-hypothesis'
|
80
|
-
Requires-Dist: hypothesis<6.
|
80
|
+
Requires-Dist: hypothesis<6.134,>=6.133.0; extra == 'zzz-test-hypothesis'
|
81
81
|
Requires-Dist: luigi<3.7,>=3.6.0; extra == 'zzz-test-hypothesis'
|
82
82
|
Requires-Dist: numpy<2.3,>=2.2.6; extra == 'zzz-test-hypothesis'
|
83
83
|
Requires-Dist: pathvalidate<3.3,>=3.2.3; extra == 'zzz-test-hypothesis'
|
@@ -25,7 +25,7 @@ dev = [
|
|
25
25
|
"fpdf2 >= 2.8.3, < 2.9",
|
26
26
|
"greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
|
27
27
|
"httpx >= 0.28.1, < 0.29", # for fastapi
|
28
|
-
"hypothesis >= 6.
|
28
|
+
"hypothesis >= 6.133.0, < 6.134",
|
29
29
|
"img2pdf >= 0.6.0, < 0.7",
|
30
30
|
"lightweight-charts >= 2.1, < 2.2",
|
31
31
|
"loguru >= 0.7.3, < 0.8",
|
@@ -94,12 +94,12 @@ dependencies = [
|
|
94
94
|
name = "dycw-utilities"
|
95
95
|
readme = "README.md"
|
96
96
|
requires-python = ">= 3.12"
|
97
|
-
version = "0.126.
|
97
|
+
version = "0.126.11"
|
98
98
|
|
99
99
|
[project.optional-dependencies]
|
100
100
|
test = [
|
101
101
|
"dycw-pytest-only >= 2.1.1, < 2.2",
|
102
|
-
"hypothesis >= 6.
|
102
|
+
"hypothesis >= 6.133.0, < 6.134",
|
103
103
|
"pytest >= 8.3.5, < 8.4",
|
104
104
|
"pytest-asyncio >= 1.0.0, < 1.1",
|
105
105
|
"pytest-cov >= 6.1.1, < 6.2",
|
@@ -174,7 +174,7 @@ zzz-test-hypothesis = [
|
|
174
174
|
"aiosqlite >= 0.21.0, < 0.22",
|
175
175
|
"asyncpg >= 0.30.0, < 0.31", # for sqlalchemy async
|
176
176
|
"greenlet >= 3.2.0, < 3.3", # for sqlalchemy async
|
177
|
-
"hypothesis >= 6.
|
177
|
+
"hypothesis >= 6.133.0, < 6.134",
|
178
178
|
"luigi >= 3.6.0, < 3.7",
|
179
179
|
"numpy >= 2.2.6, < 2.3",
|
180
180
|
"pathvalidate >= 3.2.3, < 3.3",
|
@@ -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.126.
|
337
|
+
current_version = "0.126.11"
|
338
338
|
|
339
339
|
[[tool.bumpversion.files]]
|
340
340
|
filename = "src/utilities/__init__.py"
|
@@ -436,6 +436,7 @@ addopts = [
|
|
436
436
|
"--strict-markers",
|
437
437
|
"--timeout=300",
|
438
438
|
]
|
439
|
+
asyncio_default_fixture_loop_scope = "function"
|
439
440
|
asyncio_mode = "auto"
|
440
441
|
filterwarnings = [
|
441
442
|
"error",
|
@@ -21,16 +21,20 @@ from hypothesis.strategies import (
|
|
21
21
|
permutations,
|
22
22
|
sampled_from,
|
23
23
|
)
|
24
|
-
from pytest import LogCaptureFixture,
|
24
|
+
from pytest import LogCaptureFixture, mark, param, raises
|
25
25
|
|
26
26
|
from tests.test_asyncio_classes.loopers import (
|
27
|
+
_REL,
|
27
28
|
CountingLooper,
|
28
29
|
CountingLooperError,
|
29
30
|
LooperWithCounterMixin,
|
31
|
+
LooperWithCounterMixins,
|
30
32
|
MultipleSubLoopers,
|
31
33
|
Outer2CountingLooper,
|
32
34
|
OuterCountingLooper,
|
33
35
|
QueueLooper,
|
36
|
+
assert_looper_full,
|
37
|
+
assert_looper_stats,
|
34
38
|
)
|
35
39
|
from utilities.asyncio import (
|
36
40
|
EnhancedQueue,
|
@@ -864,6 +868,7 @@ class TestInfiniteQueueLooper:
|
|
864
868
|
assert looper.empty()
|
865
869
|
|
866
870
|
@given(logger=just("logger") | none())
|
871
|
+
@mark.flaky
|
867
872
|
@settings(suppress_health_check={HealthCheck.function_scoped_fixture})
|
868
873
|
async def test_error_process_items(
|
869
874
|
self, *, logger: str | None, caplog: LogCaptureFixture
|
@@ -1007,6 +1012,70 @@ class TestLooper:
|
|
1007
1012
|
...
|
1008
1013
|
self._assert_stats_half(looper._counter, stops=1)
|
1009
1014
|
|
1015
|
+
@mark.parametrize("counter_auto_start", [param(True), param(False)])
|
1016
|
+
async def test_mixin_in_task_group(self, *, counter_auto_start: bool) -> None:
|
1017
|
+
looper = LooperWithCounterMixin(
|
1018
|
+
auto_start=True, timeout=1.0, counter_auto_start=counter_auto_start
|
1019
|
+
)
|
1020
|
+
with raises(ExceptionGroup) as exc_info:
|
1021
|
+
async with EnhancedTaskGroup(timeout=looper.timeout) as tg:
|
1022
|
+
_ = tg.create_task_context(looper)
|
1023
|
+
error = one(exc_info.value.exceptions)
|
1024
|
+
assert isinstance(error, TimeoutError)
|
1025
|
+
self._assert_stats_half(looper._counter)
|
1026
|
+
|
1027
|
+
@mark.parametrize("counter1_auto_start", [param(True), param(False)])
|
1028
|
+
@mark.parametrize("counter2_auto_start", [param(True), param(False)])
|
1029
|
+
async def test_mixins(
|
1030
|
+
self, *, counter1_auto_start: bool, counter2_auto_start: bool
|
1031
|
+
) -> None:
|
1032
|
+
looper = LooperWithCounterMixins(
|
1033
|
+
auto_start=True,
|
1034
|
+
timeout=1.0,
|
1035
|
+
counter1_auto_start=counter1_auto_start,
|
1036
|
+
counter2_auto_start=counter2_auto_start,
|
1037
|
+
)
|
1038
|
+
match counter1_auto_start, counter2_auto_start:
|
1039
|
+
case _, True:
|
1040
|
+
with raises(TimeoutError):
|
1041
|
+
async with timeout(1.0), looper:
|
1042
|
+
...
|
1043
|
+
assert_looper_full(looper)
|
1044
|
+
self._assert_stats_no_runs(looper._counter1)
|
1045
|
+
self._assert_stats_third(looper._counter2)
|
1046
|
+
case True, False:
|
1047
|
+
with raises(TimeoutError):
|
1048
|
+
async with timeout(1.0), looper:
|
1049
|
+
...
|
1050
|
+
assert_looper_full(looper)
|
1051
|
+
self._assert_stats_half(looper._counter1)
|
1052
|
+
self._assert_stats_third(looper._counter2)
|
1053
|
+
case False, False:
|
1054
|
+
async with looper:
|
1055
|
+
...
|
1056
|
+
assert_looper_full(looper, stops=1)
|
1057
|
+
self._assert_stats_half(looper._counter1, stops=1)
|
1058
|
+
self._assert_stats_third(looper._counter2, stops=1)
|
1059
|
+
|
1060
|
+
@mark.parametrize("counter_auto_start", [param(True), param(False)])
|
1061
|
+
async def test_mixins_in_task_group(self, *, counter_auto_start: bool) -> None:
|
1062
|
+
looper1 = LooperWithCounterMixin(
|
1063
|
+
auto_start=True, timeout=1.0, counter_auto_start=counter_auto_start
|
1064
|
+
)
|
1065
|
+
looper2 = LooperWithCounterMixin(
|
1066
|
+
auto_start=True, timeout=1.0, counter_auto_start=counter_auto_start
|
1067
|
+
)
|
1068
|
+
with raises(ExceptionGroup) as exc_info: # noqa: PT012
|
1069
|
+
async with EnhancedTaskGroup(timeout=1.0) as tg:
|
1070
|
+
_ = tg.create_task_context(looper1)
|
1071
|
+
_ = tg.create_task_context(looper2)
|
1072
|
+
errors = exc_info.value.exceptions
|
1073
|
+
assert 1 <= len(errors) <= 2
|
1074
|
+
for error in errors:
|
1075
|
+
assert isinstance(error, TimeoutError)
|
1076
|
+
self._assert_stats_half(looper1._counter)
|
1077
|
+
self._assert_stats_half(looper2._counter)
|
1078
|
+
|
1010
1079
|
def test_replace(self) -> None:
|
1011
1080
|
looper = CountingLooper().replace(freq=10.0)
|
1012
1081
|
assert looper.freq == 10.0
|
@@ -1024,7 +1093,7 @@ class TestLooper:
|
|
1024
1093
|
looper = Example(auto_start=True, timeout=1.0)
|
1025
1094
|
async with looper:
|
1026
1095
|
...
|
1027
|
-
|
1096
|
+
assert_looper_stats(
|
1028
1097
|
looper,
|
1029
1098
|
entries=1,
|
1030
1099
|
core_successes=79,
|
@@ -1058,7 +1127,7 @@ class TestLooper:
|
|
1058
1127
|
looper = Example(auto_start=True, timeout=1.0)
|
1059
1128
|
async with looper:
|
1060
1129
|
...
|
1061
|
-
|
1130
|
+
assert_looper_stats(
|
1062
1131
|
looper,
|
1063
1132
|
entries=1,
|
1064
1133
|
core_successes=14,
|
@@ -1098,7 +1167,7 @@ class TestLooper:
|
|
1098
1167
|
looper.put_right_nowait(i)
|
1099
1168
|
async with looper:
|
1100
1169
|
...
|
1101
|
-
|
1170
|
+
assert_looper_stats(
|
1102
1171
|
looper,
|
1103
1172
|
entries=1,
|
1104
1173
|
core_successes=25,
|
@@ -1301,47 +1370,6 @@ class TestLooper:
|
|
1301
1370
|
assert not looper.auto_start
|
1302
1371
|
assert looper.with_auto_start.auto_start
|
1303
1372
|
|
1304
|
-
def _assert_stats(
|
1305
|
-
self,
|
1306
|
-
looper: Looper[Any],
|
1307
|
-
/,
|
1308
|
-
*,
|
1309
|
-
entries: int = 0,
|
1310
|
-
core_successes: int = 0,
|
1311
|
-
core_failures: int = 0,
|
1312
|
-
initialization_successes: int = 0,
|
1313
|
-
initialization_failures: int = 0,
|
1314
|
-
tear_down_successes: int = 0,
|
1315
|
-
tear_down_failures: int = 0,
|
1316
|
-
restart_successes: int = 0,
|
1317
|
-
restart_failures: int = 0,
|
1318
|
-
stops: int = 0,
|
1319
|
-
rel: float = 0.5,
|
1320
|
-
) -> None:
|
1321
|
-
stats = looper.stats
|
1322
|
-
assert stats.entries == entries
|
1323
|
-
assert stats.core_attempts == (stats.core_successes + stats.core_failures)
|
1324
|
-
assert stats.core_successes == approx(core_successes, rel=rel)
|
1325
|
-
assert stats.core_failures == approx(core_failures, rel=rel)
|
1326
|
-
assert stats.initialization_attempts == (
|
1327
|
-
stats.initialization_successes + stats.initialization_failures
|
1328
|
-
)
|
1329
|
-
assert stats.initialization_successes == approx(
|
1330
|
-
initialization_successes, rel=rel
|
1331
|
-
)
|
1332
|
-
assert stats.initialization_failures == approx(initialization_failures, rel=rel)
|
1333
|
-
assert stats.tear_down_attempts == (
|
1334
|
-
stats.tear_down_successes + stats.tear_down_failures
|
1335
|
-
)
|
1336
|
-
assert stats.tear_down_successes == approx(tear_down_successes, rel=rel)
|
1337
|
-
assert stats.tear_down_failures == approx(tear_down_failures, rel=rel)
|
1338
|
-
assert stats.restart_attempts == (
|
1339
|
-
stats.restart_successes + stats.restart_failures
|
1340
|
-
)
|
1341
|
-
assert stats.restart_successes == approx(restart_successes, rel=rel)
|
1342
|
-
assert stats.restart_failures == approx(restart_failures, rel=rel)
|
1343
|
-
assert stats.stops == stops
|
1344
|
-
|
1345
1373
|
def _assert_stats_no_runs(
|
1346
1374
|
self,
|
1347
1375
|
looper: Looper[Any],
|
@@ -1349,14 +1377,14 @@ class TestLooper:
|
|
1349
1377
|
*,
|
1350
1378
|
entries: int = 0,
|
1351
1379
|
stops: int = 0,
|
1352
|
-
rel: float =
|
1380
|
+
rel: float = _REL,
|
1353
1381
|
) -> None:
|
1354
|
-
|
1382
|
+
assert_looper_stats(looper, entries=entries, stops=stops, rel=rel)
|
1355
1383
|
|
1356
1384
|
def _assert_stats_full(
|
1357
|
-
self, looper: Looper[Any], /, *, stops: int = 0, rel: float =
|
1385
|
+
self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
|
1358
1386
|
) -> None:
|
1359
|
-
|
1387
|
+
assert_looper_stats(
|
1360
1388
|
looper,
|
1361
1389
|
entries=1,
|
1362
1390
|
core_successes=45,
|
@@ -1369,9 +1397,9 @@ class TestLooper:
|
|
1369
1397
|
)
|
1370
1398
|
|
1371
1399
|
def _assert_stats_half(
|
1372
|
-
self, looper: Looper[Any], /, *, stops: int = 0, rel: float =
|
1400
|
+
self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
|
1373
1401
|
) -> None:
|
1374
|
-
|
1402
|
+
assert_looper_stats(
|
1375
1403
|
looper,
|
1376
1404
|
entries=1,
|
1377
1405
|
core_successes=56,
|
@@ -1384,9 +1412,9 @@ class TestLooper:
|
|
1384
1412
|
)
|
1385
1413
|
|
1386
1414
|
def _assert_stats_third(
|
1387
|
-
self, looper: Looper[Any], /, *, stops: int = 0, rel: float =
|
1415
|
+
self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
|
1388
1416
|
) -> None:
|
1389
|
-
|
1417
|
+
assert_looper_stats(
|
1390
1418
|
looper,
|
1391
1419
|
entries=1,
|
1392
1420
|
core_successes=49,
|
@@ -1399,9 +1427,9 @@ class TestLooper:
|
|
1399
1427
|
)
|
1400
1428
|
|
1401
1429
|
def _assert_stats_quarter(
|
1402
|
-
self, looper: Looper[Any], /, *, stops: int = 0, rel: float =
|
1430
|
+
self, looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
|
1403
1431
|
) -> None:
|
1404
|
-
|
1432
|
+
assert_looper_stats(
|
1405
1433
|
looper,
|
1406
1434
|
entries=1,
|
1407
1435
|
core_successes=35,
|
@@ -0,0 +1,310 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from typing import TYPE_CHECKING, Any, Literal, override
|
5
|
+
|
6
|
+
from pytest import approx
|
7
|
+
|
8
|
+
from utilities.asyncio import Looper
|
9
|
+
from utilities.contextlib import suppress_super_object_attribute_error
|
10
|
+
from utilities.datetime import MILLISECOND
|
11
|
+
|
12
|
+
if TYPE_CHECKING:
|
13
|
+
from collections.abc import Iterator
|
14
|
+
|
15
|
+
from utilities.types import Duration
|
16
|
+
|
17
|
+
_FREQ: Duration = 10 * MILLISECOND
|
18
|
+
_BACKOFF: Duration = 100 * MILLISECOND
|
19
|
+
_REL: float = 0.75
|
20
|
+
|
21
|
+
|
22
|
+
# assert
|
23
|
+
|
24
|
+
|
25
|
+
def assert_looper_stats(
|
26
|
+
looper: Looper[Any],
|
27
|
+
/,
|
28
|
+
*,
|
29
|
+
entries: int = 0,
|
30
|
+
core_successes: int | tuple[Literal[">="], int] = 0,
|
31
|
+
core_failures: int = 0,
|
32
|
+
initialization_successes: int = 0,
|
33
|
+
initialization_failures: int = 0,
|
34
|
+
tear_down_successes: int = 0,
|
35
|
+
tear_down_failures: int = 0,
|
36
|
+
restart_successes: int = 0,
|
37
|
+
restart_failures: int = 0,
|
38
|
+
stops: int = 0,
|
39
|
+
rel: float = _REL,
|
40
|
+
) -> None:
|
41
|
+
stats = looper.stats
|
42
|
+
assert stats.entries == entries, f"{stats=}, {entries=}"
|
43
|
+
assert stats.core_attempts == (stats.core_successes + stats.core_failures), (
|
44
|
+
f"{stats=}"
|
45
|
+
)
|
46
|
+
match core_successes:
|
47
|
+
case int():
|
48
|
+
assert stats.core_successes == approx(core_successes, rel=rel), (
|
49
|
+
f"{stats=}, {core_successes=}"
|
50
|
+
)
|
51
|
+
case ">=", int() as min_successes:
|
52
|
+
assert stats.core_successes >= min_successes, f"{stats=}, {min_successes=}"
|
53
|
+
assert stats.core_failures == approx(core_failures, rel=rel), (
|
54
|
+
f"{stats=}, {core_failures=}"
|
55
|
+
)
|
56
|
+
assert stats.initialization_attempts == (
|
57
|
+
stats.initialization_successes + stats.initialization_failures
|
58
|
+
), f"{stats=}"
|
59
|
+
assert stats.initialization_successes == approx(
|
60
|
+
initialization_successes, rel=rel
|
61
|
+
), f"{stats=}, {initialization_successes=}"
|
62
|
+
assert stats.initialization_failures == approx(initialization_failures, rel=rel), (
|
63
|
+
f"{stats=}, {initialization_failures=}"
|
64
|
+
)
|
65
|
+
assert stats.tear_down_attempts == (
|
66
|
+
stats.tear_down_successes + stats.tear_down_failures
|
67
|
+
), f"{stats=}"
|
68
|
+
assert stats.tear_down_successes == approx(tear_down_successes, rel=rel), (
|
69
|
+
f"{stats=}, {tear_down_successes=}"
|
70
|
+
)
|
71
|
+
assert stats.tear_down_failures == approx(tear_down_failures, rel=rel), (
|
72
|
+
f"{stats=}, {initialization_failures=}"
|
73
|
+
)
|
74
|
+
assert stats.restart_attempts == (
|
75
|
+
stats.restart_successes + stats.restart_failures
|
76
|
+
), f"{stats=}"
|
77
|
+
assert stats.restart_successes == approx(restart_successes, rel=rel), (
|
78
|
+
f"{stats=}, {restart_successes=}"
|
79
|
+
)
|
80
|
+
assert stats.restart_failures == approx(restart_failures, rel=rel), (
|
81
|
+
f"{stats=}, {restart_failures=}"
|
82
|
+
)
|
83
|
+
assert stats.stops == stops, f"{stats=}, {stops=}"
|
84
|
+
|
85
|
+
|
86
|
+
def assert_looper_full(
|
87
|
+
looper: Looper[Any], /, *, stops: int = 0, rel: float = _REL
|
88
|
+
) -> None:
|
89
|
+
assert_looper_stats(
|
90
|
+
looper,
|
91
|
+
entries=1,
|
92
|
+
core_successes=99,
|
93
|
+
initialization_successes=1,
|
94
|
+
stops=stops,
|
95
|
+
rel=rel,
|
96
|
+
)
|
97
|
+
|
98
|
+
|
99
|
+
# counting looper
|
100
|
+
|
101
|
+
|
102
|
+
@dataclass(kw_only=True)
|
103
|
+
class CountingLooper(Looper[Any]):
|
104
|
+
freq: Duration = field(default=_FREQ, repr=False)
|
105
|
+
backoff: Duration = field(default=_BACKOFF, repr=False)
|
106
|
+
_debug: bool = field(default=True, repr=False)
|
107
|
+
count: int = 0
|
108
|
+
max_count: int = 10
|
109
|
+
|
110
|
+
@override
|
111
|
+
async def _initialize_core(self) -> None:
|
112
|
+
await super()._initialize_core()
|
113
|
+
self.count = 0
|
114
|
+
|
115
|
+
@override
|
116
|
+
async def core(self) -> None:
|
117
|
+
await super().core()
|
118
|
+
self.count += 1
|
119
|
+
if self.count >= self.max_count:
|
120
|
+
raise CountingLooperError
|
121
|
+
|
122
|
+
|
123
|
+
class CountingLooperError(Exception): ...
|
124
|
+
|
125
|
+
|
126
|
+
# one sub looper
|
127
|
+
|
128
|
+
|
129
|
+
@dataclass(kw_only=True)
|
130
|
+
class OuterCountingLooper(CountingLooper):
|
131
|
+
inner: CountingLooper = field(init=False, repr=False)
|
132
|
+
inner_auto_start: bool = False
|
133
|
+
|
134
|
+
@override
|
135
|
+
def __post_init__(self) -> None:
|
136
|
+
super().__post_init__()
|
137
|
+
self.inner = CountingLooper(
|
138
|
+
auto_start=self.inner_auto_start,
|
139
|
+
freq=self.freq / 2,
|
140
|
+
backoff=self.backoff / 2,
|
141
|
+
max_count=round(self.max_count / 2),
|
142
|
+
)
|
143
|
+
|
144
|
+
@override
|
145
|
+
def _yield_sub_loopers(self) -> Iterator[Looper]:
|
146
|
+
yield from super()._yield_sub_loopers()
|
147
|
+
yield self.inner
|
148
|
+
|
149
|
+
|
150
|
+
# two sub loopers
|
151
|
+
|
152
|
+
|
153
|
+
@dataclass(kw_only=True)
|
154
|
+
class MultipleSubLoopers(CountingLooper):
|
155
|
+
inner1: CountingLooper = field(init=False, repr=False)
|
156
|
+
inner2: CountingLooper = field(init=False, repr=False)
|
157
|
+
inner1_auto_start: bool = False
|
158
|
+
inner2_auto_start: bool = False
|
159
|
+
|
160
|
+
@override
|
161
|
+
def __post_init__(self) -> None:
|
162
|
+
super().__post_init__()
|
163
|
+
self.inner1 = CountingLooper(
|
164
|
+
auto_start=self.inner1_auto_start,
|
165
|
+
freq=self.freq / 2,
|
166
|
+
backoff=self.backoff / 2,
|
167
|
+
max_count=round(self.max_count / 2),
|
168
|
+
)
|
169
|
+
self.inner2 = CountingLooper(
|
170
|
+
auto_start=self.inner2_auto_start,
|
171
|
+
freq=self.freq / 3,
|
172
|
+
backoff=self.backoff / 3,
|
173
|
+
max_count=round(self.max_count / 3),
|
174
|
+
)
|
175
|
+
|
176
|
+
@override
|
177
|
+
def _yield_sub_loopers(self) -> Iterator[Looper]:
|
178
|
+
yield from super()._yield_sub_loopers()
|
179
|
+
yield self.inner1
|
180
|
+
yield self.inner2
|
181
|
+
|
182
|
+
|
183
|
+
# nested sub loopers
|
184
|
+
|
185
|
+
|
186
|
+
@dataclass(kw_only=True)
|
187
|
+
class Outer2CountingLooper(CountingLooper):
|
188
|
+
middle: OuterCountingLooper = field(init=False, repr=False)
|
189
|
+
middle_auto_start: bool = False
|
190
|
+
inner_auto_start: bool = False
|
191
|
+
|
192
|
+
@override
|
193
|
+
def __post_init__(self) -> None:
|
194
|
+
super().__post_init__()
|
195
|
+
self.middle = OuterCountingLooper(
|
196
|
+
auto_start=self.middle_auto_start,
|
197
|
+
freq=self.freq / 2,
|
198
|
+
backoff=self.backoff / 2,
|
199
|
+
max_count=round(self.max_count / 2),
|
200
|
+
inner_auto_start=self.inner_auto_start,
|
201
|
+
)
|
202
|
+
|
203
|
+
@override
|
204
|
+
def _yield_sub_loopers(self) -> Iterator[Looper]:
|
205
|
+
yield from super()._yield_sub_loopers()
|
206
|
+
yield self.middle
|
207
|
+
|
208
|
+
|
209
|
+
# one mixin
|
210
|
+
|
211
|
+
|
212
|
+
@dataclass(kw_only=True)
|
213
|
+
class CounterMixin:
|
214
|
+
freq: Duration = field(default=_FREQ, repr=False)
|
215
|
+
backoff: Duration = field(default=_BACKOFF, repr=False)
|
216
|
+
_debug: bool = field(default=True, repr=False)
|
217
|
+
count: int = 0
|
218
|
+
max_count: int = 10
|
219
|
+
counter_auto_start: bool = False
|
220
|
+
_counter: CountingLooper = field(init=False, repr=False)
|
221
|
+
|
222
|
+
def __post_init__(self) -> None:
|
223
|
+
with suppress_super_object_attribute_error():
|
224
|
+
super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
|
225
|
+
self._counter = CountingLooper(
|
226
|
+
auto_start=self.counter_auto_start,
|
227
|
+
freq=self.freq / 2,
|
228
|
+
backoff=self.backoff / 2,
|
229
|
+
max_count=round(self.max_count / 2),
|
230
|
+
)
|
231
|
+
|
232
|
+
def _yield_sub_loopers(self) -> Iterator[Looper[Any]]:
|
233
|
+
with suppress_super_object_attribute_error():
|
234
|
+
yield from super()._yield_sub_loopers() # pyright: ignore[reportAttributeAccessIssue]
|
235
|
+
yield self._counter
|
236
|
+
|
237
|
+
|
238
|
+
@dataclass(kw_only=True)
|
239
|
+
class LooperWithCounterMixin(CounterMixin, Looper): ...
|
240
|
+
|
241
|
+
|
242
|
+
# two mixins
|
243
|
+
|
244
|
+
|
245
|
+
@dataclass(kw_only=True)
|
246
|
+
class CounterMixin1:
|
247
|
+
freq: Duration = field(default=_FREQ, repr=False)
|
248
|
+
backoff: Duration = field(default=_BACKOFF, repr=False)
|
249
|
+
_debug: bool = field(default=True, repr=False)
|
250
|
+
count: int = 0
|
251
|
+
max_count: int = 10
|
252
|
+
counter1_auto_start: bool = False
|
253
|
+
_counter1: CountingLooper = field(init=False, repr=False)
|
254
|
+
|
255
|
+
def __post_init__(self) -> None:
|
256
|
+
with suppress_super_object_attribute_error():
|
257
|
+
super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
|
258
|
+
self._counter1 = CountingLooper(
|
259
|
+
auto_start=self.counter1_auto_start,
|
260
|
+
freq=self.freq / 2,
|
261
|
+
backoff=self.backoff / 2,
|
262
|
+
max_count=round(self.max_count / 2),
|
263
|
+
)
|
264
|
+
|
265
|
+
def _yield_sub_loopers(self) -> Iterator[Looper[Any]]:
|
266
|
+
with suppress_super_object_attribute_error():
|
267
|
+
yield from super()._yield_sub_loopers() # pyright: ignore[reportAttributeAccessIssue]
|
268
|
+
yield self._counter1
|
269
|
+
|
270
|
+
|
271
|
+
@dataclass(kw_only=True)
|
272
|
+
class CounterMixin2:
|
273
|
+
freq: Duration = field(default=_FREQ, repr=False)
|
274
|
+
backoff: Duration = field(default=_BACKOFF, repr=False)
|
275
|
+
_debug: bool = field(default=True, repr=False)
|
276
|
+
count: int = 0
|
277
|
+
max_count: int = 10
|
278
|
+
counter2_auto_start: bool = False
|
279
|
+
_counter2: CountingLooper = field(init=False, repr=False)
|
280
|
+
|
281
|
+
def __post_init__(self) -> None:
|
282
|
+
with suppress_super_object_attribute_error():
|
283
|
+
super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
|
284
|
+
self._counter2 = CountingLooper(
|
285
|
+
auto_start=self.counter2_auto_start,
|
286
|
+
freq=self.freq / 3,
|
287
|
+
backoff=self.backoff / 3,
|
288
|
+
max_count=round(self.max_count / 3),
|
289
|
+
)
|
290
|
+
|
291
|
+
def _yield_sub_loopers(self) -> Iterator[Looper[Any]]:
|
292
|
+
with suppress_super_object_attribute_error():
|
293
|
+
yield from super()._yield_sub_loopers() # pyright: ignore[reportAttributeAccessIssue]
|
294
|
+
yield self._counter2
|
295
|
+
|
296
|
+
|
297
|
+
@dataclass(kw_only=True)
|
298
|
+
class LooperWithCounterMixins(CounterMixin1, CounterMixin2, Looper): ...
|
299
|
+
|
300
|
+
|
301
|
+
# queue looper
|
302
|
+
|
303
|
+
|
304
|
+
@dataclass(kw_only=True)
|
305
|
+
class QueueLooper(Looper[int]):
|
306
|
+
@override
|
307
|
+
async def core(self) -> None:
|
308
|
+
await super().core()
|
309
|
+
if not self.empty():
|
310
|
+
_ = self.get_left_nowait()
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from typing import TYPE_CHECKING, Any
|
5
|
+
|
6
|
+
from tests.test_asyncio_classes.loopers import _BACKOFF, _FREQ
|
7
|
+
from utilities.asyncio import Looper
|
8
|
+
from utilities.redis import PublishServiceMixin, SubscribeServiceMixin
|
9
|
+
from utilities.text import unique_str
|
10
|
+
|
11
|
+
if TYPE_CHECKING:
|
12
|
+
from utilities.types import Duration
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass(kw_only=True)
|
16
|
+
class LooperWithPublishAndSubscribeMixins(
|
17
|
+
PublishServiceMixin[Any], SubscribeServiceMixin[Any], Looper[Any]
|
18
|
+
):
|
19
|
+
freq: Duration = field(default=_FREQ, repr=False)
|
20
|
+
backoff: Duration = field(default=_BACKOFF, repr=False)
|
21
|
+
_debug: bool = field(default=True, repr=False)
|
22
|
+
publish_service_freq: Duration = field(default=_FREQ, repr=False)
|
23
|
+
publish_service_backoff: Duration = field(default=_BACKOFF, repr=False)
|
24
|
+
publish_service_debug: bool = field(default=True, repr=False)
|
25
|
+
subscribe_service_freq: Duration = field(default=_FREQ, repr=False)
|
26
|
+
subscribe_service_backoff: Duration = field(default=_BACKOFF, repr=False)
|
27
|
+
subscribe_service_debug: bool = field(default=True, repr=False)
|
28
|
+
subscribe_service_channel: str = field(default_factory=unique_str)
|
@@ -99,7 +99,6 @@ from utilities.hypothesis import (
|
|
99
99
|
triples,
|
100
100
|
uint32s,
|
101
101
|
uint64s,
|
102
|
-
unique_strs,
|
103
102
|
versions,
|
104
103
|
zoned_datetimes,
|
105
104
|
)
|
@@ -1171,13 +1170,6 @@ class TestUInt64s:
|
|
1171
1170
|
assert max(min_value, MIN_UINT64) <= x <= min(max_value, MAX_UINT64)
|
1172
1171
|
|
1173
1172
|
|
1174
|
-
class TestUniqueStrs:
|
1175
|
-
@given(data=data())
|
1176
|
-
def test_main(self, *, data: DataObject) -> None:
|
1177
|
-
first, second = data.draw(pairs(unique_strs()))
|
1178
|
-
assert first != second
|
1179
|
-
|
1180
|
-
|
1181
1173
|
class TestVersions:
|
1182
1174
|
@given(data=data(), suffix=booleans())
|
1183
1175
|
def test_main(self, *, data: DataObject, suffix: bool) -> None:
|
@@ -234,7 +234,7 @@ class TestGetLogRecords:
|
|
234
234
|
handler.setFormatter(OrjsonFormatter())
|
235
235
|
for level_, message_, extra_ in items:
|
236
236
|
_ = assume(set(extra_) & set(_LOG_RECORD_DEFAULT_ATTRS) == set())
|
237
|
-
logger.log(get_logging_level_number(level_), message_, extra=extra_)
|
237
|
+
logger.log(get_logging_level_number(level_), "%s", message_, extra=extra_)
|
238
238
|
output = get_log_records(root, parallelism="threads")
|
239
239
|
output = output.filter(
|
240
240
|
index=index,
|