dycw-utilities 0.130.2__tar.gz → 0.131.1__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.130.2 → dycw_utilities-0.131.1}/PKG-INFO +1 -1
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/pyproject.toml +3 -2
- dycw_utilities-0.131.1/src/tests/conftest.py +425 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_hypothesis.py +0 -39
- dycw_utilities-0.131.1/src/tests/test_pottery.py +92 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_sqlalchemy.py +283 -258
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_sqlalchemy_polars.py +125 -98
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/hypothesis.py +0 -48
- dycw_utilities-0.131.1/src/utilities/pottery.py +116 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/sqlalchemy_polars.py +1 -17
- dycw_utilities-0.130.2/src/tests/conftest.py +0 -27
- dycw_utilities-0.130.2/src/tests/test_pottery.py +0 -28
- dycw_utilities-0.130.2/src/utilities/pottery.py +0 -50
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/.gitignore +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/LICENSE +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/README.md +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_aiolimiter.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_asyncio_classes/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_asyncio_classes/loopers.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_asyncio_classes/redis.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_inflect.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/aiolimiter.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/click.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/git.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/http.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/math.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/os.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/period.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/random.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/re.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/string.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/text.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/types.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/version.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.130.2 → dycw_utilities-0.131.1}/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.131.1"
|
98
98
|
|
99
99
|
[project.optional-dependencies]
|
100
100
|
logging = [
|
@@ -341,7 +341,7 @@ zzz-test-zoneinfo = [
|
|
341
341
|
# bump-my-version
|
342
342
|
[tool.bumpversion]
|
343
343
|
allow_dirty = true
|
344
|
-
current_version = "0.
|
344
|
+
current_version = "0.131.1"
|
345
345
|
|
346
346
|
[[tool.bumpversion.files]]
|
347
347
|
filename = "src/utilities/__init__.py"
|
@@ -460,6 +460,7 @@ filterwarnings = [
|
|
460
460
|
"ignore:unclosed <StreamWriter .*>:ResourceWarning", # redis
|
461
461
|
"ignore:unclosed <socket.*socket .*>:ResourceWarning", # redis
|
462
462
|
"ignore:unclosed Connection <redis.*asyncio.*connection.*Connection.*>:ResourceWarning", # redis
|
463
|
+
"ignore:unclosed connection <asyncpg.*connection.*Connection.*>:ResourceWarning", # asyncpg
|
463
464
|
"ignore:unclosed event loop <_UnixSelectorEventLoop .*>:ResourceWarning", # redis
|
464
465
|
"ignore:unclosed file <_io.*TextIOWrapper .*>:ResourceWarning", # logging
|
465
466
|
"ignore:unclosed transport <_SelectorSocketTransport .*>:ResourceWarning", # redis
|
@@ -0,0 +1,425 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from asyncio import sleep
|
5
|
+
from contextlib import suppress
|
6
|
+
from os import environ
|
7
|
+
from re import MULTILINE, Pattern
|
8
|
+
from typing import TYPE_CHECKING, Any
|
9
|
+
|
10
|
+
from pytest import fixture, mark, param
|
11
|
+
from sqlalchemy import text
|
12
|
+
|
13
|
+
from utilities.datetime import MINUTE, get_now, parse_datetime_compact
|
14
|
+
from utilities.platform import IS_NOT_LINUX, IS_WINDOWS
|
15
|
+
from utilities.re import ExtractGroupError, extract_group
|
16
|
+
from utilities.text import strip_and_dedent
|
17
|
+
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
from collections.abc import Sequence
|
20
|
+
from pathlib import Path
|
21
|
+
|
22
|
+
from _pytest.fixtures import SubRequest
|
23
|
+
|
24
|
+
|
25
|
+
FLAKY = mark.flaky(reruns=5, reruns_delay=1)
|
26
|
+
IS_CI = "CI" in environ
|
27
|
+
SKIPIF_CI = mark.skipif(IS_CI, reason="Skipped for CI")
|
28
|
+
IS_CI_AND_WINDOWS = IS_CI and IS_WINDOWS
|
29
|
+
SKIPIF_CI_AND_WINDOWS = mark.skipif(IS_CI_AND_WINDOWS, reason="Skipped for CI/Windows")
|
30
|
+
SKIPIF_CI_AND_NOT_LINUX = mark.skipif(
|
31
|
+
IS_CI and IS_NOT_LINUX, reason="Skipped for CI/non-Linux"
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
# hypothesis
|
36
|
+
|
37
|
+
|
38
|
+
try:
|
39
|
+
from utilities.hypothesis import setup_hypothesis_profiles
|
40
|
+
except ModuleNotFoundError:
|
41
|
+
pass
|
42
|
+
else:
|
43
|
+
setup_hypothesis_profiles()
|
44
|
+
|
45
|
+
|
46
|
+
# fixtures - sqlalchemy
|
47
|
+
|
48
|
+
|
49
|
+
@fixture(params=[param("sqlite"), param("postgresql", marks=SKIPIF_CI)])
|
50
|
+
async def test_engine(*, request: SubRequest, tmp_path: Path) -> Any:
|
51
|
+
from utilities.sqlalchemy import create_async_engine
|
52
|
+
|
53
|
+
dialect = request.param
|
54
|
+
match dialect:
|
55
|
+
case "sqlite":
|
56
|
+
db_path = tmp_path / "db.sqlite"
|
57
|
+
return create_async_engine("sqlite+aiosqlite", database=str(db_path))
|
58
|
+
case "postgresql":
|
59
|
+
engine = create_async_engine(
|
60
|
+
"postgresql+asyncpg", host="localhost", port=5432, database="testing"
|
61
|
+
)
|
62
|
+
query = text("SELECT tablename FROM pg_tables")
|
63
|
+
async with engine.begin() as conn:
|
64
|
+
tables: Sequence[str] = (await conn.execute(query)).scalars().all()
|
65
|
+
for table in tables:
|
66
|
+
if _is_to_drop(table):
|
67
|
+
async with engine.begin() as conn:
|
68
|
+
with suppress(Exception):
|
69
|
+
_ = await conn.execute(
|
70
|
+
text(f'DROP TABLE IF EXISTS "{table}" CASCADE')
|
71
|
+
)
|
72
|
+
await sleep(0.01)
|
73
|
+
return engine
|
74
|
+
case _:
|
75
|
+
msg = f"Unsupported dialect: {dialect}"
|
76
|
+
raise NotImplementedError(msg)
|
77
|
+
|
78
|
+
|
79
|
+
def _is_to_drop(table: str, /) -> bool:
|
80
|
+
try:
|
81
|
+
datetime_str = extract_group(r"^(\d{8}T\d{6})_", table)
|
82
|
+
except ExtractGroupError:
|
83
|
+
return True
|
84
|
+
datetime = parse_datetime_compact(datetime_str)
|
85
|
+
now = get_now()
|
86
|
+
return (now - datetime) >= MINUTE
|
87
|
+
|
88
|
+
|
89
|
+
# fixtures - traceback
|
90
|
+
|
91
|
+
|
92
|
+
@fixture
|
93
|
+
def traceback_func_chain() -> Pattern[str]:
|
94
|
+
return re.compile(
|
95
|
+
strip_and_dedent(
|
96
|
+
r"""
|
97
|
+
Date/time \| .+
|
98
|
+
Started \| .+
|
99
|
+
Duration \| .+
|
100
|
+
User \| .+
|
101
|
+
Host \| .+
|
102
|
+
Version \|\s
|
103
|
+
|
104
|
+
Exception chain 1/2:
|
105
|
+
Frame 1/1: func_chain_first \(tests\.test_traceback_funcs\.chain\)
|
106
|
+
Inputs:
|
107
|
+
args\[0\] = 1
|
108
|
+
args\[1\] = 2
|
109
|
+
args\[2\] = 3
|
110
|
+
args\[3\] = 4
|
111
|
+
kwargs\[c\] = 5
|
112
|
+
kwargs\[d\] = 6
|
113
|
+
kwargs\[e\] = 7
|
114
|
+
Locals:
|
115
|
+
a = 2
|
116
|
+
b = 4
|
117
|
+
c = 10
|
118
|
+
args = \(6, 8\)
|
119
|
+
kwargs = {'d': 12, 'e': 14}
|
120
|
+
msg = 'Assertion failed: Result \(112\) must be divisible by 10'
|
121
|
+
Line 19:
|
122
|
+
raise ValueError\(msg\) from error
|
123
|
+
Raised:
|
124
|
+
builtins\.ValueError\(Assertion failed: Result \(112\) must be divisible by 10\)
|
125
|
+
|
126
|
+
Exception chain 2/2:
|
127
|
+
Frame 1/1: func_chain_second \(tests\.test_traceback_funcs\.chain\)
|
128
|
+
Inputs:
|
129
|
+
args\[0\] = 2
|
130
|
+
args\[1\] = 4
|
131
|
+
args\[2\] = 6
|
132
|
+
args\[3\] = 8
|
133
|
+
kwargs\[c\] = 10
|
134
|
+
kwargs\[d\] = 12
|
135
|
+
kwargs\[e\] = 14
|
136
|
+
Locals:
|
137
|
+
a = 4
|
138
|
+
b = 8
|
139
|
+
c = 20
|
140
|
+
args = \(12, 16\)
|
141
|
+
kwargs = {'d': 24, 'e': 28}
|
142
|
+
result = 112
|
143
|
+
Line 30:
|
144
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
145
|
+
Raised:
|
146
|
+
builtins\.AssertionError\(Result \(112\) must be divisible by 10\)
|
147
|
+
"""
|
148
|
+
),
|
149
|
+
flags=MULTILINE,
|
150
|
+
)
|
151
|
+
|
152
|
+
|
153
|
+
@fixture
|
154
|
+
def traceback_func_one() -> Pattern[str]:
|
155
|
+
return re.compile(
|
156
|
+
strip_and_dedent(
|
157
|
+
r"""
|
158
|
+
Date/time \| .+
|
159
|
+
Started \| .+
|
160
|
+
Duration \| .+
|
161
|
+
User \| .+
|
162
|
+
Host \| .+
|
163
|
+
Version \|\s
|
164
|
+
|
165
|
+
Frame 1/1: func_one \(tests\.test_traceback_funcs\.one\)
|
166
|
+
Inputs:
|
167
|
+
args\[0\] = 1
|
168
|
+
args\[1\] = 2
|
169
|
+
args\[2\] = 3
|
170
|
+
args\[3\] = 4
|
171
|
+
kwargs\[c\] = 5
|
172
|
+
kwargs\[d\] = 6
|
173
|
+
kwargs\[e\] = 7
|
174
|
+
Locals:
|
175
|
+
a = 2
|
176
|
+
b = 4
|
177
|
+
c = 10
|
178
|
+
args = \(6, 8\)
|
179
|
+
kwargs = {'d': 12, 'e': 14}
|
180
|
+
result = 56
|
181
|
+
Line 16:
|
182
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
183
|
+
Raised:
|
184
|
+
builtins\.AssertionError\(Result \(56\) must be divisible by 10\)
|
185
|
+
"""
|
186
|
+
),
|
187
|
+
flags=MULTILINE,
|
188
|
+
)
|
189
|
+
|
190
|
+
|
191
|
+
@fixture
|
192
|
+
def traceback_func_many_long() -> Pattern[str]:
|
193
|
+
return re.compile(
|
194
|
+
strip_and_dedent(
|
195
|
+
r"""
|
196
|
+
Date/time \| .+
|
197
|
+
Started \| .+
|
198
|
+
Duration \| .+
|
199
|
+
User \| .+
|
200
|
+
Host \| .+
|
201
|
+
Version \|\s
|
202
|
+
|
203
|
+
Frame 1/1: func_many \(tests.test_traceback_funcs.many\)
|
204
|
+
Inputs:
|
205
|
+
args\[0\] = 1
|
206
|
+
args\[1\] = 2
|
207
|
+
args\[2\] = 3
|
208
|
+
args\[3\] = 4
|
209
|
+
kwargs\[c\] = 5
|
210
|
+
kwargs\[d\] = 6
|
211
|
+
kwargs\[e\] = 7
|
212
|
+
Locals:
|
213
|
+
a = 2
|
214
|
+
b = 4
|
215
|
+
c = 10
|
216
|
+
args = \(
|
217
|
+
6,
|
218
|
+
8,
|
219
|
+
0,
|
220
|
+
2,
|
221
|
+
4,
|
222
|
+
6,
|
223
|
+
8,
|
224
|
+
10,
|
225
|
+
12,
|
226
|
+
14,
|
227
|
+
16,
|
228
|
+
18,
|
229
|
+
20,
|
230
|
+
22,
|
231
|
+
24,
|
232
|
+
26,
|
233
|
+
28,
|
234
|
+
30,
|
235
|
+
32,
|
236
|
+
34,
|
237
|
+
... \+82
|
238
|
+
\)
|
239
|
+
kwargs = {'d': 12, 'e': 14}
|
240
|
+
result = 9956
|
241
|
+
Line 16:
|
242
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
243
|
+
Raised:
|
244
|
+
builtins\.AssertionError\(Result \(9956\) must be divisible by 10\)
|
245
|
+
"""
|
246
|
+
),
|
247
|
+
flags=MULTILINE,
|
248
|
+
)
|
249
|
+
|
250
|
+
|
251
|
+
@fixture
|
252
|
+
def traceback_func_many_short() -> Pattern[str]:
|
253
|
+
return re.compile(
|
254
|
+
strip_and_dedent(
|
255
|
+
r"""
|
256
|
+
Date/time \| .+
|
257
|
+
Started \| .+
|
258
|
+
Duration \| .+
|
259
|
+
User \| .+
|
260
|
+
Host \| .+
|
261
|
+
Version \|\s
|
262
|
+
|
263
|
+
Frame 1/1: func_many \(tests.test_traceback_funcs.many\)
|
264
|
+
Inputs:
|
265
|
+
args\[0\] = 1
|
266
|
+
args\[1\] = 2
|
267
|
+
args\[2\] = 3
|
268
|
+
args\[3\] = 4
|
269
|
+
kwargs\[c\] = 5
|
270
|
+
kwargs\[d\] = 6
|
271
|
+
kwargs\[e\] = 7
|
272
|
+
Locals:
|
273
|
+
a = 2
|
274
|
+
b = 4
|
275
|
+
c = 10
|
276
|
+
args = \(6, 8, 0, 2, 4, ... \+97\)
|
277
|
+
kwargs = {'d': 12, 'e': 14}
|
278
|
+
result = 9956
|
279
|
+
Line 16:
|
280
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
281
|
+
Raised:
|
282
|
+
builtins\.AssertionError\(Result \(9956\) must be divisible by 10\)
|
283
|
+
"""
|
284
|
+
),
|
285
|
+
flags=MULTILINE,
|
286
|
+
)
|
287
|
+
|
288
|
+
|
289
|
+
@fixture
|
290
|
+
def traceback_func_task_group_one() -> Pattern[str]:
|
291
|
+
return re.compile(
|
292
|
+
strip_and_dedent(
|
293
|
+
r"""
|
294
|
+
Date/time \| .+
|
295
|
+
Started \| .+
|
296
|
+
Duration \| .+
|
297
|
+
User \| .+
|
298
|
+
Host \| .+
|
299
|
+
Version \|\s
|
300
|
+
|
301
|
+
Exception group:
|
302
|
+
Frame 1/1: func_task_group_one_first \(tests\.test_traceback_funcs\.task_group_one\)
|
303
|
+
Inputs:
|
304
|
+
args\[0\] = 1
|
305
|
+
args\[1\] = 2
|
306
|
+
args\[2\] = 3
|
307
|
+
args\[3\] = 4
|
308
|
+
kwargs\[c\] = 5
|
309
|
+
kwargs\[d\] = 6
|
310
|
+
kwargs\[e\] = 7
|
311
|
+
Locals:
|
312
|
+
a = 2
|
313
|
+
b = 4
|
314
|
+
c = 10
|
315
|
+
args = \(6, 8\)
|
316
|
+
kwargs = {'d': 12, 'e': 14}
|
317
|
+
tg = <TaskGroup cancelling>
|
318
|
+
_ = <Task finished name='Task-\d+' coro=<func_task_group_one_second\(\) done, defined at .+src.+utilities.+traceback\.py:\d+> exception=AssertionError\('Result \(112\) must be divisible by 10'\)>
|
319
|
+
Line 18:
|
320
|
+
async with TaskGroup\(\) as tg:
|
321
|
+
Raised:
|
322
|
+
builtins\.ExceptionGroup\(unhandled errors in a TaskGroup \(1 sub-exception\)\)
|
323
|
+
|
324
|
+
Exception group error 1/1:
|
325
|
+
Frame 1/1: func_task_group_one_second \(tests\.test_traceback_funcs\.task_group_one\)
|
326
|
+
Inputs:
|
327
|
+
args\[0\] = 2
|
328
|
+
args\[1\] = 4
|
329
|
+
args\[2\] = 6
|
330
|
+
args\[3\] = 8
|
331
|
+
kwargs\[c\] = 10
|
332
|
+
kwargs\[d\] = 12
|
333
|
+
kwargs\[e\] = 14
|
334
|
+
Locals:
|
335
|
+
a = 4
|
336
|
+
b = 8
|
337
|
+
c = 20
|
338
|
+
args = \(12, 16\)
|
339
|
+
kwargs = {'d': 24, 'e': 28}
|
340
|
+
result = 112
|
341
|
+
Line 33:
|
342
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
343
|
+
Raised:
|
344
|
+
builtins\.AssertionError\(Result \(112\) must be divisible by 10\)
|
345
|
+
"""
|
346
|
+
)
|
347
|
+
)
|
348
|
+
|
349
|
+
|
350
|
+
@fixture
|
351
|
+
def traceback_func_two() -> Pattern[str]:
|
352
|
+
return re.compile(
|
353
|
+
strip_and_dedent(
|
354
|
+
r"""
|
355
|
+
Date/time \| .+
|
356
|
+
Started \| .+
|
357
|
+
Duration \| .+
|
358
|
+
User \| .+
|
359
|
+
Host \| .+
|
360
|
+
Version \|\s
|
361
|
+
|
362
|
+
Frame 1/2: func_two_first \(tests\.test_traceback_funcs\.two\)
|
363
|
+
Inputs:
|
364
|
+
args\[0\] = 1
|
365
|
+
args\[1\] = 2
|
366
|
+
args\[2\] = 3
|
367
|
+
args\[3\] = 4
|
368
|
+
kwargs\[c\] = 5
|
369
|
+
kwargs\[d\] = 6
|
370
|
+
kwargs\[e\] = 7
|
371
|
+
Locals:
|
372
|
+
a = 2
|
373
|
+
b = 4
|
374
|
+
c = 10
|
375
|
+
args = \(6, 8\)
|
376
|
+
kwargs = {'d': 12, 'e': 14}
|
377
|
+
Line 15:
|
378
|
+
return func_two_second\(a, b, \*args, c=c, \*\*kwargs\)
|
379
|
+
|
380
|
+
Frame 2/2: func_two_second \(tests\.test_traceback_funcs\.two\)
|
381
|
+
Inputs:
|
382
|
+
args\[0\] = 2
|
383
|
+
args\[1\] = 4
|
384
|
+
args\[2\] = 6
|
385
|
+
args\[3\] = 8
|
386
|
+
kwargs\[c\] = 10
|
387
|
+
kwargs\[d\] = 12
|
388
|
+
kwargs\[e\] = 14
|
389
|
+
Locals:
|
390
|
+
a = 4
|
391
|
+
b = 8
|
392
|
+
c = 20
|
393
|
+
args = \(12, 16\)
|
394
|
+
kwargs = {'d': 24, 'e': 28}
|
395
|
+
result = 112
|
396
|
+
Line 26:
|
397
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
398
|
+
Raised:
|
399
|
+
builtins\.AssertionError\(Result \(112\) must be divisible by 10\)
|
400
|
+
"""
|
401
|
+
),
|
402
|
+
flags=MULTILINE,
|
403
|
+
)
|
404
|
+
|
405
|
+
|
406
|
+
@fixture
|
407
|
+
def traceback_func_untraced() -> Pattern[str]:
|
408
|
+
return re.compile(
|
409
|
+
strip_and_dedent(
|
410
|
+
r"""
|
411
|
+
Traceback \(most recent call last\):
|
412
|
+
|
413
|
+
File ".+src.+tests.+test_(logging|sys|traceback)\.py", line \d+, in test_.+
|
414
|
+
_ = func_untraced\(1, 2, 3, 4, c=5, d=6, e=7\)
|
415
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
416
|
+
|
417
|
+
File ".+src.+tests.+test_traceback_funcs.+untraced\.py", line 13, in func_untraced
|
418
|
+
assert result % 10 == 0, f"Result \({result}\) must be divisible by 10"
|
419
|
+
^^^^^^^^^^^^^^^^
|
420
|
+
|
421
|
+
AssertionError: Result \(56\) must be divisible by 10
|
422
|
+
"""
|
423
|
+
).replace("^", r"\^"),
|
424
|
+
flags=MULTILINE,
|
425
|
+
)
|
@@ -23,14 +23,11 @@ from hypothesis.strategies import (
|
|
23
23
|
sets,
|
24
24
|
timedeltas,
|
25
25
|
timezones,
|
26
|
-
uuids,
|
27
26
|
)
|
28
27
|
from luigi import Task
|
29
28
|
from numpy import inf, int64, isfinite, isinf, isnan, ravel, rint
|
30
29
|
from pathvalidate import validate_filepath
|
31
30
|
from pytest import mark, raises
|
32
|
-
from sqlalchemy import Column, Integer, MetaData, Table, insert, select
|
33
|
-
from sqlalchemy.ext.asyncio import AsyncEngine
|
34
31
|
|
35
32
|
from tests.conftest import SKIPIF_CI_AND_WINDOWS
|
36
33
|
from utilities.datetime import (
|
@@ -45,7 +42,6 @@ from utilities.datetime import (
|
|
45
42
|
)
|
46
43
|
from utilities.functions import ensure_int
|
47
44
|
from utilities.hypothesis import (
|
48
|
-
_SQLALCHEMY_ENGINE_DIALECTS,
|
49
45
|
MaybeSearchStrategy,
|
50
46
|
PlainDateTimesError,
|
51
47
|
Shape,
|
@@ -83,7 +79,6 @@ from utilities.hypothesis import (
|
|
83
79
|
settings_with_reduced_examples,
|
84
80
|
setup_hypothesis_profiles,
|
85
81
|
slices,
|
86
|
-
sqlalchemy_engines,
|
87
82
|
str_arrays,
|
88
83
|
temp_dirs,
|
89
84
|
temp_paths,
|
@@ -119,7 +114,6 @@ from utilities.math import (
|
|
119
114
|
from utilities.os import temp_environ
|
120
115
|
from utilities.platform import maybe_yield_lower_case
|
121
116
|
from utilities.sentinel import Sentinel
|
122
|
-
from utilities.sqlalchemy import Dialect, _get_dialect
|
123
117
|
from utilities.version import Version
|
124
118
|
from utilities.whenever import (
|
125
119
|
MAX_SERIALIZABLE_TIMEDELTA,
|
@@ -134,7 +128,6 @@ from utilities.whenever import (
|
|
134
128
|
if TYPE_CHECKING:
|
135
129
|
from collections.abc import Iterable
|
136
130
|
from collections.abc import Set as AbstractSet
|
137
|
-
from uuid import UUID
|
138
131
|
from zoneinfo import ZoneInfo
|
139
132
|
|
140
133
|
from utilities.datetime import Month
|
@@ -917,38 +910,6 @@ class TestSlices:
|
|
917
910
|
_ = data.draw(slices(iter_len, slice_len=iter_len + 1))
|
918
911
|
|
919
912
|
|
920
|
-
class TestSQLAlchemyEngines:
|
921
|
-
@given(
|
922
|
-
data=data(),
|
923
|
-
name=uuids(),
|
924
|
-
dialect=_SQLALCHEMY_ENGINE_DIALECTS,
|
925
|
-
ids=sets(integers(0, 10), min_size=1),
|
926
|
-
)
|
927
|
-
@settings(phases={Phase.generate})
|
928
|
-
async def test_main(
|
929
|
-
self, *, data: DataObject, name: UUID, dialect: Dialect, ids: set[int]
|
930
|
-
) -> None:
|
931
|
-
table = Table(
|
932
|
-
f"test_{name}", MetaData(), Column("id_", Integer, primary_key=True)
|
933
|
-
)
|
934
|
-
engine = await sqlalchemy_engines(data, table, dialect=dialect)
|
935
|
-
assert isinstance(engine, AsyncEngine)
|
936
|
-
assert _get_dialect(engine) == dialect
|
937
|
-
if dialect == "sqlite":
|
938
|
-
database = engine.url.database
|
939
|
-
assert database is not None
|
940
|
-
assert not Path(database).exists()
|
941
|
-
async with engine.begin() as conn:
|
942
|
-
await conn.run_sync(table.metadata.create_all)
|
943
|
-
ins = insert(table).values([(id_,) for id_ in ids])
|
944
|
-
async with engine.begin() as conn:
|
945
|
-
_ = await conn.execute(ins)
|
946
|
-
sel = select(table.c["id_"])
|
947
|
-
async with engine.begin() as conn:
|
948
|
-
results = (await conn.execute(sel)).scalars().all()
|
949
|
-
assert set(results) == ids
|
950
|
-
|
951
|
-
|
952
913
|
class TestStrArrays:
|
953
914
|
@given(
|
954
915
|
data=data(),
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from asyncio import TaskGroup, sleep
|
4
|
+
from re import search
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from pytest import mark, param, raises
|
8
|
+
|
9
|
+
from tests.conftest import SKIPIF_CI_AND_NOT_LINUX
|
10
|
+
from tests.test_redis import yield_test_redis
|
11
|
+
from utilities.iterables import one
|
12
|
+
from utilities.pottery import (
|
13
|
+
_YieldAccessNumLocksError,
|
14
|
+
_YieldAccessUnableToAcquireLockError,
|
15
|
+
yield_access,
|
16
|
+
)
|
17
|
+
from utilities.text import unique_str
|
18
|
+
from utilities.timer import Timer
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from redis.asyncio import Redis
|
22
|
+
|
23
|
+
|
24
|
+
async def _func_access(num_tasks: int, key: str, /, *, num_locks: int = 1) -> None:
|
25
|
+
async def coroutine() -> None:
|
26
|
+
async with yield_test_redis() as redis, yield_access(redis, key, num=num_locks):
|
27
|
+
await sleep(0.1)
|
28
|
+
|
29
|
+
async with TaskGroup() as tg:
|
30
|
+
_ = [tg.create_task(coroutine()) for _ in range(num_tasks)]
|
31
|
+
|
32
|
+
|
33
|
+
class TestYieldAccess:
|
34
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
35
|
+
@mark.parametrize(
|
36
|
+
("num_tasks", "num_locks", "min_time"),
|
37
|
+
[
|
38
|
+
param(1, 1, 0.1),
|
39
|
+
param(1, 2, 0.1),
|
40
|
+
param(1, 3, 0.1),
|
41
|
+
param(2, 1, 0.2),
|
42
|
+
param(2, 2, 0.1),
|
43
|
+
param(2, 3, 0.1),
|
44
|
+
param(2, 4, 0.1),
|
45
|
+
param(2, 5, 0.1),
|
46
|
+
param(3, 1, 0.3),
|
47
|
+
param(3, 2, 0.2),
|
48
|
+
param(3, 3, 0.1),
|
49
|
+
param(3, 4, 0.1),
|
50
|
+
param(3, 5, 0.1),
|
51
|
+
param(4, 1, 0.4),
|
52
|
+
param(4, 2, 0.2),
|
53
|
+
param(4, 3, 0.2),
|
54
|
+
param(4, 4, 0.1),
|
55
|
+
param(4, 5, 0.1),
|
56
|
+
],
|
57
|
+
)
|
58
|
+
async def test_main(
|
59
|
+
self, *, num_tasks: int, num_locks: int, min_time: float
|
60
|
+
) -> None:
|
61
|
+
with Timer() as timer:
|
62
|
+
await _func_access(num_tasks, unique_str(), num_locks=num_locks)
|
63
|
+
assert min_time <= float(timer) <= 3 * min_time
|
64
|
+
|
65
|
+
async def test_error_num_locks(self) -> None:
|
66
|
+
key = unique_str()
|
67
|
+
with raises(
|
68
|
+
_YieldAccessNumLocksError,
|
69
|
+
match=r"Number of locks for '\w+' must be positive; got 0",
|
70
|
+
):
|
71
|
+
async with yield_test_redis() as redis, yield_access(redis, key, num=0):
|
72
|
+
...
|
73
|
+
|
74
|
+
@SKIPIF_CI_AND_NOT_LINUX
|
75
|
+
async def test_error_unable_to_acquire_lock(self) -> None:
|
76
|
+
key = unique_str()
|
77
|
+
|
78
|
+
async def coroutine(redis: Redis, key: str, /) -> None:
|
79
|
+
async with yield_access(
|
80
|
+
redis, key, num=1, timeout_acquire=0.1, throttle=0.5
|
81
|
+
):
|
82
|
+
await sleep(0.1)
|
83
|
+
|
84
|
+
with raises(ExceptionGroup) as exc_info: # noqa: PT012
|
85
|
+
async with yield_test_redis() as redis, TaskGroup() as tg:
|
86
|
+
_ = tg.create_task(coroutine(redis, key))
|
87
|
+
_ = tg.create_task(coroutine(redis, key))
|
88
|
+
error = one(exc_info.value.exceptions)
|
89
|
+
assert isinstance(error, _YieldAccessUnableToAcquireLockError)
|
90
|
+
assert search(
|
91
|
+
r"Unable to acquire any 1 of 1 locks for '\w+' after 0\.1", str(error)
|
92
|
+
)
|