dycw-utilities 0.117.1__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.1 → dycw_utilities-0.118.0}/PKG-INFO +1 -1
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/pyproject.toml +2 -2
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_asyncio.py +3 -389
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_fastapi.py +11 -3
- {dycw_utilities-0.117.1 → 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.1 → dycw_utilities-0.118.0}/src/tests/test_sqlalchemy.py +0 -30
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/asyncio.py +2 -224
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/fastapi.py +3 -8
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/redis.py +1 -18
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/slack_sdk.py +2 -68
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/sqlalchemy.py +1 -44
- dycw_utilities-0.117.1/src/tests/scripts/test_async_service/__init__.py +0 -45
- dycw_utilities-0.117.1/src/tests/scripts/test_async_service/__main__.py +0 -6
- dycw_utilities-0.117.1/src/tests/scripts/test_async_service/run.sh +0 -3
- dycw_utilities-0.117.1/src/tests/scripts/test_queue_processor/__init__.py +0 -51
- dycw_utilities-0.117.1/src/tests/scripts/test_queue_processor/__main__.py +0 -6
- dycw_utilities-0.117.1/src/tests/scripts/test_queue_processor/run.sh +0 -3
- dycw_utilities-0.117.1/src/tests/test_slack_sdk.py +0 -190
- dycw_utilities-0.117.1/src/tests/test_traceback_funcs/__init__.py +0 -1
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/.gitignore +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/LICENSE +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/README.md +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_astor.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.117.1/src/tests/scripts → dycw_utilities-0.118.0/src/tests/test_traceback_funcs}/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/astor.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/click.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/git.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/http.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/math.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/os.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/period.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/random.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/re.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/text.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/types.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/version.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.117.1 → dycw_utilities-0.118.0}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.117.1 → 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,16 +1,6 @@
|
|
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
|
@@ -28,18 +18,14 @@ from hypothesis.strategies import (
|
|
28
18
|
permutations,
|
29
19
|
sampled_from,
|
30
20
|
)
|
31
|
-
from pytest import LogCaptureFixture,
|
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,
|
45
31
|
_DurationOrEvery,
|
@@ -73,200 +59,6 @@ if TYPE_CHECKING:
|
|
73
59
|
from utilities.types import Coroutine1, Duration, MaybeCallableEvent, MaybeType
|
74
60
|
|
75
61
|
|
76
|
-
class TestAsyncLoopingService:
|
77
|
-
async def test_main(self) -> None:
|
78
|
-
@dataclass(kw_only=True)
|
79
|
-
class Example(AsyncLoopingService):
|
80
|
-
counter: int = 0
|
81
|
-
|
82
|
-
@override
|
83
|
-
async def _run(self) -> None:
|
84
|
-
self.counter += 1
|
85
|
-
|
86
|
-
async with Example(duration=1.0, sleep=0.1) as service:
|
87
|
-
pass
|
88
|
-
assert 5 <= service.counter <= 15
|
89
|
-
|
90
|
-
async def test_cancel(self) -> None:
|
91
|
-
@dataclass(kw_only=True)
|
92
|
-
class Example(AsyncLoopingService):
|
93
|
-
counter: int = 0
|
94
|
-
|
95
|
-
@override
|
96
|
-
async def _run(self) -> None:
|
97
|
-
self.counter += 1
|
98
|
-
if self.counter >= 10:
|
99
|
-
raise CancelledError
|
100
|
-
|
101
|
-
async with Example(sleep=0.1) as service:
|
102
|
-
pass
|
103
|
-
assert 5 <= service.counter <= 15
|
104
|
-
|
105
|
-
async def test_sleep_after_failure(self) -> None:
|
106
|
-
@dataclass(kw_only=True)
|
107
|
-
class Example(AsyncLoopingService):
|
108
|
-
counter: int = 0
|
109
|
-
errors: Counter[type[Exception]] = field(default_factory=Counter)
|
110
|
-
|
111
|
-
@override
|
112
|
-
async def _run(self) -> None:
|
113
|
-
self.counter += 1
|
114
|
-
if self.counter % 2 == 0:
|
115
|
-
raise ValueError
|
116
|
-
|
117
|
-
@override
|
118
|
-
async def _run_failure(self, error: Exception, /) -> None:
|
119
|
-
self.errors.update([type(error)])
|
120
|
-
|
121
|
-
async with Example(duration=1.0, sleep=0.1) as service:
|
122
|
-
pass
|
123
|
-
assert 5 <= service.counter <= 15
|
124
|
-
assert 3 <= service.errors[ValueError] <= 7
|
125
|
-
|
126
|
-
async def test_failure(self) -> None:
|
127
|
-
class CustomError(Exception): ...
|
128
|
-
|
129
|
-
@dataclass(kw_only=True)
|
130
|
-
class Example(AsyncLoopingService):
|
131
|
-
counter: int = 0
|
132
|
-
failed: bool = False
|
133
|
-
|
134
|
-
@override
|
135
|
-
async def _run(self) -> None:
|
136
|
-
self.counter += 1
|
137
|
-
if self.counter >= 5:
|
138
|
-
raise CustomError
|
139
|
-
|
140
|
-
with raises(CustomError):
|
141
|
-
async with Example(sleep=0.1):
|
142
|
-
pass
|
143
|
-
|
144
|
-
|
145
|
-
class TestAsyncService:
|
146
|
-
async def test_main(self) -> None:
|
147
|
-
@dataclass(kw_only=True)
|
148
|
-
class Example(AsyncService):
|
149
|
-
running: bool = False
|
150
|
-
|
151
|
-
@override
|
152
|
-
async def _start(self) -> None:
|
153
|
-
self.running = True
|
154
|
-
|
155
|
-
@override
|
156
|
-
async def stop(self) -> None:
|
157
|
-
self.running = False
|
158
|
-
await super().stop()
|
159
|
-
|
160
|
-
service = Example(duration=0.1)
|
161
|
-
for _ in range(2):
|
162
|
-
assert not service.running
|
163
|
-
async with service:
|
164
|
-
assert service.running
|
165
|
-
async with service:
|
166
|
-
assert service.running
|
167
|
-
assert service.running
|
168
|
-
assert not service.running
|
169
|
-
|
170
|
-
async def test_timeout(self) -> None:
|
171
|
-
@dataclass(kw_only=True)
|
172
|
-
class Example(AsyncService):
|
173
|
-
running: bool = False
|
174
|
-
|
175
|
-
@override
|
176
|
-
async def _start(self) -> None:
|
177
|
-
self.running = True
|
178
|
-
|
179
|
-
@override
|
180
|
-
async def stop(self) -> None:
|
181
|
-
self.running = False
|
182
|
-
await super().stop()
|
183
|
-
|
184
|
-
service = Example()
|
185
|
-
try:
|
186
|
-
async with timeout_dur(duration=0.05), service:
|
187
|
-
await sleep(0.1)
|
188
|
-
except TimeoutError:
|
189
|
-
assert not service.running
|
190
|
-
|
191
|
-
@mark.parametrize(
|
192
|
-
("duration", "expected"),
|
193
|
-
[
|
194
|
-
param(0.5, approx(5, abs=1)),
|
195
|
-
param(1.0, approx(10, abs=1)),
|
196
|
-
param(1.5, 10),
|
197
|
-
param(None, 10),
|
198
|
-
],
|
199
|
-
)
|
200
|
-
async def test_cancellation(
|
201
|
-
self, *, duration: Duration | None, expected: int
|
202
|
-
) -> None:
|
203
|
-
class Example(AsyncService):
|
204
|
-
counter: int = 0
|
205
|
-
|
206
|
-
@override
|
207
|
-
async def _start(self) -> None:
|
208
|
-
for _ in range(10):
|
209
|
-
self.counter += 1
|
210
|
-
await sleep(0.1)
|
211
|
-
raise CancelledError
|
212
|
-
|
213
|
-
async with Example(duration=duration) as service:
|
214
|
-
...
|
215
|
-
assert service.counter == expected
|
216
|
-
|
217
|
-
async def test_extra_context_managers(self) -> None:
|
218
|
-
@dataclass(kw_only=True)
|
219
|
-
class Inner(AsyncService):
|
220
|
-
duration: Duration | None = 0.1
|
221
|
-
running: bool = False
|
222
|
-
|
223
|
-
@override
|
224
|
-
async def _start(self) -> None:
|
225
|
-
self.running = True
|
226
|
-
|
227
|
-
@override
|
228
|
-
async def stop(self) -> None:
|
229
|
-
self.running = False
|
230
|
-
await super().stop()
|
231
|
-
|
232
|
-
@dataclass(kw_only=True)
|
233
|
-
class Outer(AsyncService):
|
234
|
-
duration: Duration | None = 0.1
|
235
|
-
running: bool = False
|
236
|
-
inner: Inner = field(default_factory=Inner, init=False, repr=False)
|
237
|
-
|
238
|
-
@override
|
239
|
-
async def _start(self) -> None:
|
240
|
-
self.running = True
|
241
|
-
_ = await self._stack.enter_async_context(self.inner)
|
242
|
-
|
243
|
-
@override
|
244
|
-
async def stop(self) -> None:
|
245
|
-
self.running = False
|
246
|
-
await super().stop()
|
247
|
-
|
248
|
-
outer = Outer()
|
249
|
-
for _ in range(2):
|
250
|
-
assert not outer.running
|
251
|
-
assert not outer.inner.running
|
252
|
-
async with outer:
|
253
|
-
assert outer.running
|
254
|
-
assert outer.inner.running
|
255
|
-
assert not outer.running
|
256
|
-
assert not outer.inner.running
|
257
|
-
|
258
|
-
def test_repr(self) -> None:
|
259
|
-
class Example(AsyncService):
|
260
|
-
@override
|
261
|
-
async def _start(self) -> None:
|
262
|
-
await sleep(0.01)
|
263
|
-
|
264
|
-
service = Example()
|
265
|
-
result = repr(service)
|
266
|
-
expected = "TestAsyncService.test_repr.<locals>.Example(duration=None)"
|
267
|
-
assert result == expected
|
268
|
-
|
269
|
-
|
270
62
|
class TestEnhancedTaskGroup:
|
271
63
|
async def test_max_tasks_disabled(self) -> None:
|
272
64
|
with Timer() as timer:
|
@@ -305,18 +97,6 @@ class TestEnhancedTaskGroup:
|
|
305
97
|
assert isinstance(error, CustomError)
|
306
98
|
|
307
99
|
|
308
|
-
class TestExceptionProcessor:
|
309
|
-
async def test_main(self) -> None:
|
310
|
-
processor = ExceptionProcessor()
|
311
|
-
|
312
|
-
class CustomError(Exception): ...
|
313
|
-
|
314
|
-
with raises(CustomError): # noqa: PT012
|
315
|
-
async with processor:
|
316
|
-
processor.enqueue(CustomError)
|
317
|
-
await sleep(0.1)
|
318
|
-
|
319
|
-
|
320
100
|
class TestGetEvent:
|
321
101
|
def test_event(self) -> None:
|
322
102
|
event = Event()
|
@@ -794,169 +574,6 @@ class TestPutAndGetItemsNoWait:
|
|
794
574
|
assert result == xs[:max_size]
|
795
575
|
|
796
576
|
|
797
|
-
class TestQueueProcessor:
|
798
|
-
async def test_one_processor_slow_tasks(self) -> None:
|
799
|
-
@dataclass(kw_only=True)
|
800
|
-
class Example(QueueProcessor[int]):
|
801
|
-
output: set[int] = field(default_factory=set)
|
802
|
-
|
803
|
-
@override
|
804
|
-
async def _process_item(self, item: int, /) -> None:
|
805
|
-
self.output.add(item)
|
806
|
-
|
807
|
-
async with Example() as processor:
|
808
|
-
|
809
|
-
async def add_tasks() -> None:
|
810
|
-
for i in range(10):
|
811
|
-
processor.enqueue(i)
|
812
|
-
await sleep(0.1)
|
813
|
-
|
814
|
-
async def run_until_empty() -> None:
|
815
|
-
await sleep(0.5)
|
816
|
-
await processor.run_until_empty()
|
817
|
-
|
818
|
-
async with TaskGroup() as tg:
|
819
|
-
_ = tg.create_task(add_tasks())
|
820
|
-
_ = tg.create_task(run_until_empty())
|
821
|
-
|
822
|
-
assert len(processor.output) == 10
|
823
|
-
|
824
|
-
async def test_one_processor_slow_run(self) -> None:
|
825
|
-
@dataclass(kw_only=True)
|
826
|
-
class Example(QueueProcessor[int]):
|
827
|
-
output: set[int] = field(default_factory=set)
|
828
|
-
|
829
|
-
@override
|
830
|
-
async def _process_item(self, item: int, /) -> None:
|
831
|
-
self.output.add(item)
|
832
|
-
await sleep(0.01)
|
833
|
-
|
834
|
-
async with Example() as processor:
|
835
|
-
processor.enqueue(*range(10))
|
836
|
-
await processor.run_until_empty()
|
837
|
-
assert len(processor.output) == 10
|
838
|
-
|
839
|
-
@given(n=integers(1, 10))
|
840
|
-
async def test_one_processor_continually_adding(self, *, n: int) -> None:
|
841
|
-
@dataclass(kw_only=True)
|
842
|
-
class Example(QueueProcessor[int]):
|
843
|
-
output: set[int] = field(default_factory=set)
|
844
|
-
|
845
|
-
@override
|
846
|
-
async def _process_item(self, item: int, /) -> None:
|
847
|
-
self.output.add(item)
|
848
|
-
|
849
|
-
async with Example() as processor:
|
850
|
-
for i in range(n):
|
851
|
-
processor.enqueue(i)
|
852
|
-
await sleep(0.01)
|
853
|
-
assert len(processor.output) == n
|
854
|
-
|
855
|
-
async def test_two_processors(self) -> None:
|
856
|
-
@dataclass(kw_only=True)
|
857
|
-
class First(QueueProcessor[int]):
|
858
|
-
second: Second
|
859
|
-
output: set[int] = field(default_factory=set)
|
860
|
-
|
861
|
-
@override
|
862
|
-
async def _process_item(self, item: int, /) -> None:
|
863
|
-
self.second.enqueue(item)
|
864
|
-
self.output.add(item)
|
865
|
-
await sleep(0.1)
|
866
|
-
|
867
|
-
@dataclass(kw_only=True)
|
868
|
-
class Second(QueueProcessor[int]):
|
869
|
-
output: set[int] = field(default_factory=set)
|
870
|
-
|
871
|
-
@override
|
872
|
-
async def _process_item(self, item: int, /) -> None:
|
873
|
-
self.output.add(item)
|
874
|
-
await sleep(0.01)
|
875
|
-
|
876
|
-
async with Second() as second, First(second=second) as first:
|
877
|
-
|
878
|
-
async def yield_tasks() -> None:
|
879
|
-
first.enqueue(*range(10))
|
880
|
-
await first.run_until_empty()
|
881
|
-
|
882
|
-
await yield_tasks()
|
883
|
-
assert len(first.output) == 10
|
884
|
-
assert len(second.output) == 10
|
885
|
-
|
886
|
-
@mark.parametrize("duration", [param(0.1), param(0.5), param(1.0), param(1.5)])
|
887
|
-
async def test_cancellation(self, *, duration: float) -> None:
|
888
|
-
@dataclass(kw_only=True)
|
889
|
-
class Example(QueueProcessor[int]):
|
890
|
-
output: set[int] = field(default_factory=set)
|
891
|
-
|
892
|
-
@override
|
893
|
-
async def _process_item(self, item: int, /) -> None:
|
894
|
-
self.output.add(item)
|
895
|
-
await sleep(0.1)
|
896
|
-
|
897
|
-
async with Example(duration=duration) as processor:
|
898
|
-
processor.enqueue(*range(10))
|
899
|
-
assert processor.output == set(range(10))
|
900
|
-
|
901
|
-
async def test_empty(self) -> None:
|
902
|
-
class Example(QueueProcessor[int]):
|
903
|
-
@override
|
904
|
-
async def _process_item(self, item: int, /) -> None:
|
905
|
-
_ = item
|
906
|
-
|
907
|
-
processor = Example()
|
908
|
-
assert processor.empty()
|
909
|
-
processor.enqueue(0)
|
910
|
-
assert not processor.empty()
|
911
|
-
|
912
|
-
@given(n=integers(0, 10))
|
913
|
-
async def test_get_items_nowait(self, *, n: int) -> None:
|
914
|
-
@dataclass(kw_only=True)
|
915
|
-
class Example(QueueProcessor[int]):
|
916
|
-
output: set[int] = field(default_factory=set)
|
917
|
-
|
918
|
-
@override
|
919
|
-
async def _process_item(self, _: int, /) -> None:
|
920
|
-
items = self._get_items_nowait()
|
921
|
-
self.output.add(len(items))
|
922
|
-
|
923
|
-
processor = Example()
|
924
|
-
processor.enqueue(*range(n + 1))
|
925
|
-
await processor._run()
|
926
|
-
result = one(processor.output)
|
927
|
-
assert result == n
|
928
|
-
|
929
|
-
@given(n=integers(0, 10))
|
930
|
-
async def test_len(self, *, n: int) -> None:
|
931
|
-
class Example(QueueProcessor[int]):
|
932
|
-
@override
|
933
|
-
async def _process_item(self, item: int) -> None:
|
934
|
-
_ = item
|
935
|
-
|
936
|
-
processor = Example()
|
937
|
-
assert len(processor) == 0
|
938
|
-
processor.enqueue(*range(n))
|
939
|
-
assert len(processor) == n
|
940
|
-
|
941
|
-
@given(data=data(), texts=lists(text_ascii(min_size=1), min_size=1))
|
942
|
-
async def test_priority_queue(self, *, data: DataObject, texts: list[str]) -> None:
|
943
|
-
@dataclass(kw_only=True)
|
944
|
-
class Example(QueueProcessor[tuple[int, str]]):
|
945
|
-
output: set[str] = field(default_factory=set)
|
946
|
-
|
947
|
-
@override
|
948
|
-
async def _process_item(self, item: tuple[int, str]) -> None:
|
949
|
-
_, text = item
|
950
|
-
self.output.add(text)
|
951
|
-
|
952
|
-
processor = Example(queue_type=PriorityQueue)
|
953
|
-
items = data.draw(permutations(list(enumerate(texts))))
|
954
|
-
processor.enqueue(*items)
|
955
|
-
await processor._run()
|
956
|
-
result = one(processor.output)
|
957
|
-
assert result == texts[0]
|
958
|
-
|
959
|
-
|
960
577
|
class TestUniquePriorityQueue:
|
961
578
|
@given(data=data(), texts=lists(text_ascii(min_size=1), min_size=1, unique=True))
|
962
579
|
async def test_main(self, *, data: DataObject, texts: list[str]) -> None:
|
@@ -1003,10 +620,7 @@ class TestSleepDur:
|
|
1003
620
|
|
1004
621
|
class TestSleepUntil:
|
1005
622
|
async def test_main(self) -> None:
|
1006
|
-
|
1007
|
-
with Timer() as timer:
|
1008
|
-
await sleep_until(now + 10 * MILLISECOND)
|
1009
|
-
assert timer >= datetime_duration_to_timedelta(5 * MILLISECOND)
|
623
|
+
await sleep_until(get_now() + 10 * MILLISECOND)
|
1010
624
|
|
1011
625
|
|
1012
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(),
|
@@ -0,0 +1,83 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from asyncio import sleep
|
4
|
+
from logging import getLogger
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from aiohttp import InvalidUrlClientError
|
8
|
+
from pytest import mark, raises
|
9
|
+
from slack_sdk.webhook.async_client import AsyncWebhookClient
|
10
|
+
|
11
|
+
from utilities.asyncio import EnhancedTaskGroup, sleep_dur
|
12
|
+
from utilities.datetime import MINUTE
|
13
|
+
from utilities.os import get_env_var
|
14
|
+
from utilities.pytest import throttle
|
15
|
+
from utilities.slack_sdk import SlackHandlerIQL, _get_client, send_to_slack
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from collections.abc import Sequence
|
19
|
+
from pathlib import Path
|
20
|
+
|
21
|
+
|
22
|
+
class TestGetClient:
|
23
|
+
def test_main(self) -> None:
|
24
|
+
client = _get_client("url")
|
25
|
+
assert isinstance(client, AsyncWebhookClient)
|
26
|
+
|
27
|
+
|
28
|
+
class TestSendToSlack:
|
29
|
+
async def test_main(self) -> None:
|
30
|
+
with raises(InvalidUrlClientError, match="url"):
|
31
|
+
await send_to_slack("url", "message")
|
32
|
+
|
33
|
+
@mark.skipif(get_env_var("SLACK", nullable=True) is None, reason="'SLACK' not set")
|
34
|
+
@throttle(duration=5 * MINUTE)
|
35
|
+
async def test_real(self) -> None:
|
36
|
+
url = get_env_var("SLACK")
|
37
|
+
await send_to_slack(
|
38
|
+
url, f"message from {TestSendToSlack.test_real.__qualname__}"
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
class TestSlackHandlerIQL:
|
43
|
+
async def test_main(self, *, tmp_path: Path) -> None:
|
44
|
+
messages: Sequence[str] = []
|
45
|
+
|
46
|
+
async def sender(_: str, text: str, /) -> None:
|
47
|
+
await sleep(0.01)
|
48
|
+
messages.append(text)
|
49
|
+
|
50
|
+
logger = getLogger(str(tmp_path))
|
51
|
+
logger.addHandler(
|
52
|
+
handler := SlackHandlerIQL("url", sleep_core=0.05, sender=sender)
|
53
|
+
)
|
54
|
+
|
55
|
+
async def sleep_then_log() -> None:
|
56
|
+
await sleep_dur(duration=0.05)
|
57
|
+
logger.warning("message")
|
58
|
+
|
59
|
+
with raises(ExceptionGroup): # noqa: PT012
|
60
|
+
async with EnhancedTaskGroup(timeout=0.5) as tg:
|
61
|
+
_ = tg.create_task(handler())
|
62
|
+
_ = tg.create_task(sleep_then_log())
|
63
|
+
|
64
|
+
assert messages == ["message"]
|
65
|
+
|
66
|
+
@mark.skipif(get_env_var("SLACK", nullable=True) is None, reason="'SLACK' not set")
|
67
|
+
@throttle(duration=5 * MINUTE)
|
68
|
+
async def test_real(self, *, tmp_path: Path) -> None:
|
69
|
+
url = get_env_var("SLACK")
|
70
|
+
logger = getLogger(str(tmp_path))
|
71
|
+
logger.addHandler(handler := SlackHandlerIQL(url, sleep_core=0.05))
|
72
|
+
|
73
|
+
async def sleep_then_log() -> None:
|
74
|
+
await sleep_dur(duration=0.05)
|
75
|
+
for i in range(10):
|
76
|
+
logger.warning(
|
77
|
+
"message %d from %s", i, TestSlackHandlerIQL.test_real.__qualname__
|
78
|
+
)
|
79
|
+
|
80
|
+
with raises(ExceptionGroup): # noqa: PT012
|
81
|
+
async with EnhancedTaskGroup(timeout=0.5) as tg:
|
82
|
+
_ = tg.create_task(handler())
|
83
|
+
_ = tg.create_task(sleep_then_log())
|