dycw-utilities 0.125.25__tar.gz → 0.126.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.125.25 → dycw_utilities-0.126.0}/PKG-INFO +1 -1
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/pyproject.toml +2 -2
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_asyncio.py +1 -2
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_redis.py +324 -203
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_sqlalchemy.py +30 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/redis.py +271 -70
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/sqlalchemy.py +39 -1
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/.gitignore +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/LICENSE +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/README.md +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/click.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/git.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/http.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/math.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/os.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/period.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/random.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/re.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/string.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/text.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/types.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/version.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.125.25 → dycw_utilities-0.126.0}/src/utilities/zoneinfo.py +0 -0
@@ -94,7 +94,7 @@ dependencies = [
|
|
94
94
|
name = "dycw-utilities"
|
95
95
|
readme = "README.md"
|
96
96
|
requires-python = ">= 3.12"
|
97
|
-
version = "0.
|
97
|
+
version = "0.126.0"
|
98
98
|
|
99
99
|
[project.optional-dependencies]
|
100
100
|
test = [
|
@@ -335,7 +335,7 @@ zzz-test-zoneinfo = [
|
|
335
335
|
# bump-my-version
|
336
336
|
[tool.bumpversion]
|
337
337
|
allow_dirty = true
|
338
|
-
current_version = "0.
|
338
|
+
current_version = "0.126.0"
|
339
339
|
|
340
340
|
[[tool.bumpversion.files]]
|
341
341
|
filename = "src/utilities/__init__.py"
|
@@ -52,7 +52,6 @@ from utilities.dataclasses import replace_non_sentinel
|
|
52
52
|
from utilities.datetime import (
|
53
53
|
MILLISECOND,
|
54
54
|
MINUTE,
|
55
|
-
SECOND,
|
56
55
|
datetime_duration_to_timedelta,
|
57
56
|
get_now,
|
58
57
|
)
|
@@ -964,7 +963,7 @@ class TestLooper:
|
|
964
963
|
async def test_context_manager_already_entered(
|
965
964
|
self, *, caplog: LogCaptureFixture
|
966
965
|
) -> None:
|
967
|
-
looper = _ExampleCounterLooper(
|
966
|
+
looper = _ExampleCounterLooper(timeout=1.0)
|
968
967
|
async with looper, looper:
|
969
968
|
...
|
970
969
|
_ = one(m for m in caplog.messages if search(": already entered$", m))
|
@@ -1,25 +1,31 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from asyncio import
|
4
|
-
from
|
3
|
+
from asyncio import Queue, sleep
|
4
|
+
from os import getpid
|
5
|
+
from re import search
|
5
6
|
from typing import TYPE_CHECKING, Any
|
6
7
|
|
7
8
|
from hypothesis import HealthCheck, Phase, given, settings
|
8
9
|
from hypothesis.strategies import (
|
9
10
|
DataObject,
|
11
|
+
DrawFn,
|
12
|
+
binary,
|
10
13
|
booleans,
|
14
|
+
composite,
|
11
15
|
data,
|
12
16
|
dictionaries,
|
13
17
|
lists,
|
14
18
|
sampled_from,
|
19
|
+
uuids,
|
15
20
|
)
|
16
|
-
from pytest import mark, raises
|
21
|
+
from pytest import LogCaptureFixture, mark, param, raises
|
17
22
|
from redis.asyncio import Redis
|
23
|
+
from redis.asyncio.client import PubSub
|
18
24
|
|
19
25
|
from tests.conftest import SKIPIF_CI_AND_NOT_LINUX
|
20
26
|
from tests.test_operator import make_objects
|
21
|
-
from utilities.asyncio import
|
22
|
-
from utilities.
|
27
|
+
from utilities.asyncio import get_items_nowait
|
28
|
+
from utilities.datetime import serialize_compact
|
23
29
|
from utilities.hypothesis import (
|
24
30
|
int64s,
|
25
31
|
pairs,
|
@@ -27,174 +33,183 @@ from utilities.hypothesis import (
|
|
27
33
|
text_ascii,
|
28
34
|
yield_test_redis,
|
29
35
|
)
|
36
|
+
from utilities.iterables import one
|
37
|
+
from utilities.operator import is_equal
|
30
38
|
from utilities.orjson import deserialize, serialize
|
31
39
|
from utilities.redis import (
|
32
40
|
Publisher,
|
33
41
|
PublisherError,
|
42
|
+
PublishError,
|
43
|
+
PublishService,
|
44
|
+
SubscribeService,
|
45
|
+
_is_subscribe_message,
|
46
|
+
_RedisMessageSubscribe,
|
47
|
+
_RedisMessageUnsubscribe,
|
34
48
|
publish,
|
35
49
|
redis_hash_map_key,
|
36
50
|
redis_key,
|
37
51
|
subscribe,
|
38
|
-
|
52
|
+
yield_pubsub,
|
39
53
|
yield_redis,
|
40
54
|
)
|
41
55
|
from utilities.sentinel import SENTINEL_REPR, Sentinel, sentinel
|
56
|
+
from utilities.tzlocal import get_now_local
|
42
57
|
|
43
58
|
if TYPE_CHECKING:
|
44
|
-
from collections.abc import Mapping
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
from collections.abc import Mapping, Sequence
|
60
|
+
from pathlib import Path
|
61
|
+
|
62
|
+
|
63
|
+
_PUB_SUB_SLEEP = 0.1
|
64
|
+
|
65
|
+
|
66
|
+
@composite
|
67
|
+
def channels(draw: DrawFn, /) -> str:
|
68
|
+
now = serialize_compact(get_now_local())
|
69
|
+
key = draw(uuids())
|
70
|
+
pid = getpid()
|
71
|
+
return f"test_{now}_{key}_{pid}"
|
72
|
+
|
73
|
+
|
74
|
+
class TestIsSubscribeMessage:
|
75
|
+
@mark.parametrize(
|
76
|
+
("message", "channels", "expected"),
|
77
|
+
[
|
78
|
+
param(
|
79
|
+
{
|
80
|
+
"type": "message",
|
81
|
+
"pattern": None,
|
82
|
+
"channel": b"channel",
|
83
|
+
"data": b"data",
|
84
|
+
},
|
85
|
+
[b"channel"],
|
86
|
+
True,
|
87
|
+
),
|
88
|
+
param(None, [], False),
|
89
|
+
param({"type": "invalid"}, [], False),
|
90
|
+
param({"type": "message"}, [], False),
|
91
|
+
param({"type": "message", "pattern": False}, [], False),
|
92
|
+
param({"type": "message", "pattern": None}, [], False),
|
93
|
+
param(
|
94
|
+
{"type": "message", "pattern": None, "channel": b"channel1"},
|
95
|
+
[b"channel2"],
|
96
|
+
False,
|
97
|
+
),
|
98
|
+
param(
|
99
|
+
{"type": "message", "pattern": None, "channel": b"channel"},
|
100
|
+
[b"channel"],
|
101
|
+
False,
|
102
|
+
),
|
103
|
+
param(
|
104
|
+
{
|
105
|
+
"type": "message",
|
106
|
+
"pattern": None,
|
107
|
+
"channel": b"channel",
|
108
|
+
"data": None,
|
109
|
+
},
|
110
|
+
[b"channel"],
|
111
|
+
False,
|
112
|
+
),
|
113
|
+
],
|
63
114
|
)
|
64
|
-
|
65
|
-
|
66
|
-
|
115
|
+
def test_main(
|
116
|
+
self,
|
117
|
+
*,
|
118
|
+
message: _RedisMessageSubscribe | _RedisMessageUnsubscribe | None,
|
119
|
+
channels: Sequence[bytes],
|
120
|
+
expected: bool,
|
67
121
|
) -> None:
|
68
|
-
|
122
|
+
result = _is_subscribe_message(message, channels=channels)
|
123
|
+
assert result is expected
|
69
124
|
|
70
|
-
async def listener() -> None:
|
71
|
-
async for msg in subscribe(
|
72
|
-
test.redis.pubsub(), channel, deserializer=deserialize
|
73
|
-
):
|
74
|
-
print(msg) # noqa: T201
|
75
125
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
await sleep(0.05)
|
80
|
-
try:
|
81
|
-
out = capsys.readouterr().out
|
82
|
-
expected = f"{obj}\n"
|
83
|
-
assert out == expected
|
84
|
-
finally:
|
85
|
-
_ = task.cancel()
|
86
|
-
|
87
|
-
@given(
|
88
|
-
data=data(),
|
89
|
-
channel=text_ascii(min_size=1).map(
|
90
|
-
lambda c: f"{get_class_name(TestPublishAndSubscribe)}_text_without_serialize_{c}"
|
91
|
-
),
|
92
|
-
text=text_ascii(min_size=1),
|
93
|
-
)
|
94
|
-
@settings(
|
95
|
-
max_examples=1,
|
96
|
-
phases={Phase.generate},
|
97
|
-
suppress_health_check={HealthCheck.function_scoped_fixture},
|
98
|
-
)
|
126
|
+
class TestPublish:
|
127
|
+
@given(channel=channels(), data=lists(binary(min_size=1), min_size=1, max_size=5))
|
128
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
99
129
|
@SKIPIF_CI_AND_NOT_LINUX
|
100
|
-
async def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
_ = task.cancel()
|
119
|
-
|
120
|
-
|
121
|
-
class TestPublisher:
|
122
|
-
@given(
|
123
|
-
data=data(),
|
124
|
-
channel=text_ascii(min_size=1).map(
|
125
|
-
lambda c: f"{get_class_name(TestPublisher)}_main_{c}"
|
126
|
-
),
|
127
|
-
obj=make_objects(),
|
128
|
-
)
|
129
|
-
@mark.flaky
|
130
|
-
@settings(
|
131
|
-
max_examples=1,
|
132
|
-
phases={Phase.generate},
|
133
|
-
suppress_health_check={HealthCheck.function_scoped_fixture},
|
134
|
-
)
|
130
|
+
async def test_bytes(self, *, data: Sequence[bytes], channel: str) -> None:
|
131
|
+
queue: Queue[bytes] = Queue()
|
132
|
+
async with (
|
133
|
+
yield_redis() as redis,
|
134
|
+
subscribe(redis, channel, queue, output="bytes"),
|
135
|
+
):
|
136
|
+
await sleep(_PUB_SUB_SLEEP)
|
137
|
+
for datum in data:
|
138
|
+
_ = await publish(redis, channel, datum)
|
139
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
140
|
+
assert queue.qsize() == len(data)
|
141
|
+
results = get_items_nowait(queue)
|
142
|
+
for result, datum in zip(results, data, strict=True):
|
143
|
+
assert isinstance(result, bytes)
|
144
|
+
assert result == datum
|
145
|
+
|
146
|
+
@given(channel=channels(), objects=lists(make_objects(), min_size=1, max_size=5))
|
147
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
135
148
|
@SKIPIF_CI_AND_NOT_LINUX
|
136
|
-
async def
|
137
|
-
|
149
|
+
async def test_serializer(self, *, channel: str, objects: Sequence[Any]) -> None:
|
150
|
+
queue: Queue[Any] = Queue()
|
138
151
|
async with (
|
139
|
-
|
140
|
-
|
141
|
-
duration=1.0, redis=test.redis, serializer=serialize, sleep_core=0.1
|
142
|
-
) as publisher,
|
152
|
+
yield_redis() as redis,
|
153
|
+
subscribe(redis, channel, queue, output=deserialize),
|
143
154
|
):
|
155
|
+
await sleep(_PUB_SUB_SLEEP)
|
156
|
+
for obj in objects:
|
157
|
+
_ = await publish(redis, channel, obj, serializer=serialize)
|
158
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
159
|
+
assert queue.qsize() == len(objects)
|
160
|
+
results = get_items_nowait(queue)
|
161
|
+
for result, obj in zip(results, objects, strict=True):
|
162
|
+
assert is_equal(result, obj)
|
144
163
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
164
|
+
@given(
|
165
|
+
channel=channels(),
|
166
|
+
messages=lists(text_ascii(min_size=1), min_size=1, max_size=5),
|
167
|
+
)
|
168
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
169
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
170
|
+
async def test_text(self, *, channel: str, messages: Sequence[str]) -> None:
|
171
|
+
queue: Queue[str] = Queue()
|
172
|
+
async with yield_redis() as redis, subscribe(redis, channel, queue):
|
173
|
+
await sleep(_PUB_SUB_SLEEP)
|
174
|
+
for message in messages:
|
175
|
+
_ = await publish(redis, channel, message)
|
176
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
177
|
+
assert queue.qsize() == len(messages)
|
178
|
+
results = get_items_nowait(queue)
|
179
|
+
for result, message in zip(results, messages, strict=True):
|
180
|
+
assert isinstance(result, str)
|
181
|
+
assert result == message
|
182
|
+
|
183
|
+
async def test_error(self) -> None:
|
184
|
+
async with yield_redis() as redis:
|
185
|
+
with raises(
|
186
|
+
PublishError, match="Unable to publish data None with serializer None"
|
187
|
+
):
|
188
|
+
_ = await publish(redis, "channel", None)
|
159
189
|
|
160
|
-
assert buffer.getvalue() == str(obj)
|
161
190
|
|
191
|
+
class TestPublisher:
|
162
192
|
@given(
|
163
|
-
|
164
|
-
|
165
|
-
lambda c: f"{get_class_name(TestPublisher)}_text_without_serialize_{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},
|
193
|
+
channel=channels(),
|
194
|
+
messages=lists(text_ascii(min_size=1), min_size=1, max_size=5),
|
173
195
|
)
|
196
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
174
197
|
@SKIPIF_CI_AND_NOT_LINUX
|
175
|
-
async def
|
176
|
-
|
177
|
-
) -> None:
|
178
|
-
buffer = BytesIO()
|
198
|
+
async def test_main(self, *, channel: str, messages: Sequence[str]) -> None:
|
199
|
+
queue: Queue[str] = Queue()
|
179
200
|
async with (
|
180
|
-
|
181
|
-
Publisher(duration=1.0, redis=
|
201
|
+
yield_redis() as redis,
|
202
|
+
Publisher(duration=1.0, redis=redis, sleep_core=0.1) as publisher,
|
203
|
+
subscribe(redis, channel, queue),
|
182
204
|
):
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
with raises(ExceptionGroup): # noqa: PT012
|
193
|
-
async with EnhancedTaskGroup(timeout=1.0) as tg:
|
194
|
-
_ = tg.create_task(listener())
|
195
|
-
_ = tg.create_task(sleep_then_put())
|
196
|
-
|
197
|
-
assert buffer.getvalue() == text.encode()
|
205
|
+
await sleep(_PUB_SUB_SLEEP)
|
206
|
+
publisher.put_right_nowait(*((channel, m) for m in messages))
|
207
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
208
|
+
assert queue.qsize() == len(messages)
|
209
|
+
results = get_items_nowait(queue)
|
210
|
+
for result, message in zip(results, messages, strict=True):
|
211
|
+
assert isinstance(result, str)
|
212
|
+
assert result == message
|
198
213
|
|
199
214
|
@given(data=data())
|
200
215
|
@SKIPIF_CI_AND_NOT_LINUX
|
@@ -204,67 +219,27 @@ class TestPublisher:
|
|
204
219
|
with raises(PublisherError, match="Error running 'Publisher'"):
|
205
220
|
raise PublisherError(publisher=publisher)
|
206
221
|
|
207
|
-
|
208
|
-
class TestSubscribeMessages:
|
209
|
-
@given(
|
210
|
-
channel=text_ascii(min_size=1).map(
|
211
|
-
lambda c: f"{get_class_name(TestSubscribeMessages)}_redis_{c}"
|
212
|
-
),
|
213
|
-
message=text_ascii(min_size=1),
|
214
|
-
)
|
215
|
-
@settings(
|
216
|
-
max_examples=1,
|
217
|
-
phases={Phase.generate},
|
218
|
-
suppress_health_check={HealthCheck.function_scoped_fixture},
|
219
|
-
)
|
220
|
-
@SKIPIF_CI_AND_NOT_LINUX
|
221
|
-
async def test_redis(
|
222
|
-
self, *, capsys: CaptureFixture, channel: str, message: str
|
223
|
-
) -> None:
|
224
|
-
redis = Redis()
|
225
|
-
await self._run_test(redis, redis, capsys, channel, message)
|
226
|
-
|
227
222
|
@given(
|
228
|
-
channel=
|
229
|
-
|
230
|
-
),
|
231
|
-
message=text_ascii(min_size=1),
|
232
|
-
)
|
233
|
-
@settings(
|
234
|
-
max_examples=1,
|
235
|
-
phases={Phase.generate},
|
236
|
-
suppress_health_check={HealthCheck.function_scoped_fixture},
|
223
|
+
channel=channels(),
|
224
|
+
messages=lists(text_ascii(min_size=1), min_size=1, max_size=5),
|
237
225
|
)
|
226
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
238
227
|
@SKIPIF_CI_AND_NOT_LINUX
|
239
|
-
async def
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
message
|
252
|
-
|
253
|
-
|
254
|
-
async def listener() -> None:
|
255
|
-
async for msg in subscribe_messages(redis_or_pubsub, channel):
|
256
|
-
print(msg) # noqa: T201
|
257
|
-
|
258
|
-
task = get_running_loop().create_task(listener())
|
259
|
-
await sleep(0.05)
|
260
|
-
_ = await redis.publish(channel, message)
|
261
|
-
await sleep(0.05)
|
262
|
-
try:
|
263
|
-
out = capsys.readouterr().out
|
264
|
-
expected = f"{{'type': 'message', 'pattern': None, 'channel': b'{channel}', 'data': b'{message}'}}\n"
|
265
|
-
assert out == expected
|
266
|
-
finally:
|
267
|
-
_ = task.cancel()
|
228
|
+
async def test_main_service(self, *, channel: str, messages: Sequence[str]) -> None:
|
229
|
+
queue: Queue[str] = Queue()
|
230
|
+
async with (
|
231
|
+
yield_redis() as redis,
|
232
|
+
PublishService(freq=0.1, timeout=1.0, redis=redis) as service,
|
233
|
+
subscribe(redis, channel, queue),
|
234
|
+
):
|
235
|
+
await sleep(_PUB_SUB_SLEEP)
|
236
|
+
service.put_right_nowait(*((channel, m) for m in messages))
|
237
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
238
|
+
assert queue.qsize() == len(messages)
|
239
|
+
results = get_items_nowait(queue)
|
240
|
+
for result, message in zip(results, messages, strict=True):
|
241
|
+
assert isinstance(result, str)
|
242
|
+
assert result == message
|
268
243
|
|
269
244
|
|
270
245
|
class TestRedisHashMapKey:
|
@@ -536,7 +511,153 @@ class TestRedisKey:
|
|
536
511
|
assert not await key.exists(test.redis)
|
537
512
|
|
538
513
|
|
514
|
+
class TestSubscribe:
|
515
|
+
@given(
|
516
|
+
channel=channels(), messages=lists(binary(min_size=1), min_size=1, max_size=5)
|
517
|
+
)
|
518
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
519
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
520
|
+
async def test_bytes(self, *, channel: str, messages: Sequence[bytes]) -> None:
|
521
|
+
queue: Queue[bytes] = Queue()
|
522
|
+
async with (
|
523
|
+
yield_redis() as redis,
|
524
|
+
subscribe(redis, channel, queue, output="bytes"),
|
525
|
+
):
|
526
|
+
await sleep(_PUB_SUB_SLEEP)
|
527
|
+
for message in messages:
|
528
|
+
await redis.publish(channel, message)
|
529
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
530
|
+
assert queue.qsize() == len(messages)
|
531
|
+
results = get_items_nowait(queue)
|
532
|
+
for result, message in zip(results, messages, strict=True):
|
533
|
+
assert isinstance(result, bytes)
|
534
|
+
assert result == message
|
535
|
+
|
536
|
+
@given(channel=channels(), objs=lists(make_objects(), min_size=1, max_size=5))
|
537
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
538
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
539
|
+
async def test_deserialize(self, *, channel: str, objs: Sequence[Any]) -> None:
|
540
|
+
queue: Queue[Any] = Queue()
|
541
|
+
async with (
|
542
|
+
yield_redis() as redis,
|
543
|
+
subscribe(redis, channel, queue, output=deserialize),
|
544
|
+
):
|
545
|
+
await sleep(_PUB_SUB_SLEEP)
|
546
|
+
for obj in objs:
|
547
|
+
await redis.publish(channel, serialize(obj))
|
548
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
549
|
+
assert queue.qsize() == len(objs)
|
550
|
+
results = get_items_nowait(queue)
|
551
|
+
for result, obj in zip(results, objs, strict=True):
|
552
|
+
assert is_equal(result, obj)
|
553
|
+
|
554
|
+
@given(
|
555
|
+
channel=channels(),
|
556
|
+
messages=lists(text_ascii(min_size=1), min_size=1, max_size=5),
|
557
|
+
)
|
558
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
559
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
560
|
+
async def test_raw(self, *, channel: str, messages: Sequence[str]) -> None:
|
561
|
+
queue: Queue[_RedisMessageSubscribe] = Queue()
|
562
|
+
async with (
|
563
|
+
yield_redis() as redis,
|
564
|
+
subscribe(redis, channel, queue, output="raw"),
|
565
|
+
):
|
566
|
+
await sleep(_PUB_SUB_SLEEP)
|
567
|
+
for message in messages:
|
568
|
+
await redis.publish(channel, message)
|
569
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
570
|
+
assert queue.qsize() == len(messages)
|
571
|
+
results = get_items_nowait(queue)
|
572
|
+
for result, message in zip(results, messages, strict=True):
|
573
|
+
assert isinstance(result, dict)
|
574
|
+
assert result["type"] == "message"
|
575
|
+
assert result["pattern"] is None
|
576
|
+
assert result["channel"] == channel.encode()
|
577
|
+
assert result["data"] == message.encode()
|
578
|
+
|
579
|
+
@given(
|
580
|
+
channel=channels(),
|
581
|
+
messages=lists(text_ascii(min_size=1), min_size=1, max_size=5),
|
582
|
+
)
|
583
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
584
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
585
|
+
async def test_text(self, *, channel: str, messages: Sequence[str]) -> None:
|
586
|
+
queue: Queue[_RedisMessageSubscribe] = Queue()
|
587
|
+
async with (
|
588
|
+
yield_redis() as redis,
|
589
|
+
subscribe(redis, channel, queue, output="raw"),
|
590
|
+
):
|
591
|
+
await sleep(_PUB_SUB_SLEEP)
|
592
|
+
for message in messages:
|
593
|
+
await redis.publish(channel, message)
|
594
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
595
|
+
assert queue.qsize() == len(messages)
|
596
|
+
results = get_items_nowait(queue)
|
597
|
+
for result, message in zip(results, messages, strict=True):
|
598
|
+
assert isinstance(result, dict)
|
599
|
+
assert result["type"] == "message"
|
600
|
+
assert result["pattern"] is None
|
601
|
+
assert result["channel"] == channel.encode()
|
602
|
+
assert result["data"] == message.encode()
|
603
|
+
|
604
|
+
|
605
|
+
class TestSubscribeService:
|
606
|
+
@given(
|
607
|
+
channel=channels(),
|
608
|
+
messages=lists(text_ascii(min_size=1), min_size=1, max_size=5),
|
609
|
+
)
|
610
|
+
@settings_with_reduced_examples(phases={Phase.generate})
|
611
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
612
|
+
async def test_main(self, *, channel: str, messages: list[str]) -> None:
|
613
|
+
async with (
|
614
|
+
yield_redis() as redis,
|
615
|
+
SubscribeService(timeout=1.0, redis=redis, channel=channel) as service,
|
616
|
+
):
|
617
|
+
await sleep(_PUB_SUB_SLEEP)
|
618
|
+
for message in messages:
|
619
|
+
await redis.publish(channel, message)
|
620
|
+
await sleep(_PUB_SUB_SLEEP) # keep in context
|
621
|
+
assert service.qsize() == len(messages)
|
622
|
+
results = service.get_all_nowait()
|
623
|
+
for result, message in zip(results, messages, strict=True):
|
624
|
+
assert isinstance(result, str)
|
625
|
+
assert result == message
|
626
|
+
|
627
|
+
@given(channel=channels())
|
628
|
+
@settings(
|
629
|
+
max_examples=1,
|
630
|
+
phases={Phase.generate},
|
631
|
+
suppress_health_check={HealthCheck.function_scoped_fixture},
|
632
|
+
)
|
633
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
634
|
+
async def test_context_manager_already_subscribing(
|
635
|
+
self, *, channel: str, caplog: LogCaptureFixture
|
636
|
+
) -> None:
|
637
|
+
async with yield_redis() as redis:
|
638
|
+
looper = SubscribeService(
|
639
|
+
timeout=1.0, _debug=True, redis=redis, channel=channel
|
640
|
+
)
|
641
|
+
async with looper, looper:
|
642
|
+
...
|
643
|
+
_ = one(m for m in caplog.messages if search(": already subscribing$", m))
|
644
|
+
_ = one(
|
645
|
+
m
|
646
|
+
for m in caplog.messages
|
647
|
+
if search(": already stopped subscription$", m)
|
648
|
+
)
|
649
|
+
|
650
|
+
|
539
651
|
class TestYieldClient:
|
540
|
-
|
652
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
653
|
+
async def test_main(self) -> None:
|
541
654
|
async with yield_redis() as client:
|
542
655
|
assert isinstance(client, Redis)
|
656
|
+
|
657
|
+
|
658
|
+
class TestYieldPubSub:
|
659
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
660
|
+
async def test_main(self, *, tmp_path: Path) -> None:
|
661
|
+
channel = str(tmp_path)
|
662
|
+
async with yield_redis() as redis, yield_pubsub(redis, channel) as pubsub:
|
663
|
+
assert isinstance(pubsub, PubSub)
|