dycw-utilities 0.125.24__tar.gz → 0.125.26__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.24 → dycw_utilities-0.125.26}/PKG-INFO +1 -1
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/pyproject.toml +2 -2
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_asyncio.py +10 -8
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_slack_sdk.py +46 -2
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sqlalchemy.py +30 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/asyncio.py +2 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/slack_sdk.py +93 -3
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sqlalchemy.py +39 -1
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/.gitignore +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/LICENSE +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/README.md +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_datetime.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_git.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_loguru.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_luigi.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pydantic.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pyrsistent.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_python_dotenv.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_rich.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_statsmodel.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_streamlit.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_sys.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tenacity.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/chain.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/decorated_async.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/decorated_sync.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/error_bind.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/many.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/one.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/recursive.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/task_group_one.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/task_group_two.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/two.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_traceback_funcs/untraced.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/click.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/datetime.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/git.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/http.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/loguru.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/luigi.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/math.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/os.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/period.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pydantic.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pyrsistent.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/python_dotenv.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/random.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/re.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/rich.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/streamlit.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/string.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/sys.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tenacity.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/text.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/types.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/version.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/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.125.
|
97
|
+
version = "0.125.26"
|
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.125.
|
338
|
+
current_version = "0.125.26"
|
339
339
|
|
340
340
|
[[tool.bumpversion.files]]
|
341
341
|
filename = "src/utilities/__init__.py"
|
@@ -1381,25 +1381,27 @@ class TestLooper:
|
|
1381
1381
|
stats = looper.stats
|
1382
1382
|
assert stats.entries == entries
|
1383
1383
|
assert stats.core_attempts == (stats.core_successes + stats.core_failures)
|
1384
|
-
assert stats.core_successes == approx(core_successes, rel=0.
|
1385
|
-
assert stats.core_failures == approx(core_failures, rel=0.
|
1384
|
+
assert stats.core_successes == approx(core_successes, rel=0.75)
|
1385
|
+
assert stats.core_failures == approx(core_failures, rel=0.75)
|
1386
1386
|
assert stats.initialization_attempts == (
|
1387
1387
|
stats.initialization_successes + stats.initialization_failures
|
1388
1388
|
)
|
1389
1389
|
assert stats.initialization_successes == approx(
|
1390
|
-
initialization_successes, rel=0.
|
1390
|
+
initialization_successes, rel=0.75
|
1391
|
+
)
|
1392
|
+
assert stats.initialization_failures == approx(
|
1393
|
+
initialization_failures, rel=0.75
|
1391
1394
|
)
|
1392
|
-
assert stats.initialization_failures == approx(initialization_failures, rel=0.5)
|
1393
1395
|
assert stats.tear_down_attempts == (
|
1394
1396
|
stats.tear_down_successes + stats.tear_down_failures
|
1395
1397
|
)
|
1396
|
-
assert stats.tear_down_successes == approx(tear_down_successes, rel=0.
|
1397
|
-
assert stats.tear_down_failures == approx(tear_down_failures, rel=0.
|
1398
|
+
assert stats.tear_down_successes == approx(tear_down_successes, rel=0.75)
|
1399
|
+
assert stats.tear_down_failures == approx(tear_down_failures, rel=0.75)
|
1398
1400
|
assert stats.restart_attempts == (
|
1399
1401
|
stats.restart_successes + stats.restart_failures
|
1400
1402
|
)
|
1401
|
-
assert stats.restart_successes == approx(restart_successes, rel=0.
|
1402
|
-
assert stats.restart_failures == approx(restart_failures, rel=0.
|
1403
|
+
assert stats.restart_successes == approx(restart_successes, rel=0.75)
|
1404
|
+
assert stats.restart_failures == approx(restart_failures, rel=0.75)
|
1403
1405
|
assert stats.stops == stops
|
1404
1406
|
|
1405
1407
|
|
@@ -5,13 +5,18 @@ from logging import getLogger
|
|
5
5
|
from typing import TYPE_CHECKING
|
6
6
|
|
7
7
|
from aiohttp import InvalidUrlClientError
|
8
|
-
from pytest import mark, raises
|
8
|
+
from pytest import mark, param, raises
|
9
9
|
from slack_sdk.webhook.async_client import AsyncWebhookClient
|
10
10
|
|
11
11
|
from utilities.datetime import MINUTE
|
12
12
|
from utilities.os import get_env_var
|
13
13
|
from utilities.pytest import throttle
|
14
|
-
from utilities.slack_sdk import
|
14
|
+
from utilities.slack_sdk import (
|
15
|
+
SlackHandler,
|
16
|
+
SlackHandlerService,
|
17
|
+
_get_client,
|
18
|
+
send_to_slack,
|
19
|
+
)
|
15
20
|
|
16
21
|
if TYPE_CHECKING:
|
17
22
|
from collections.abc import Sequence
|
@@ -64,3 +69,42 @@ class TestSlackHandler:
|
|
64
69
|
logger.warning(
|
65
70
|
"message %d from %s", i, TestSlackHandler.test_real.__qualname__
|
66
71
|
)
|
72
|
+
|
73
|
+
# service
|
74
|
+
|
75
|
+
@mark.parametrize("auto_start", [param(True), param(False)])
|
76
|
+
async def test_main_service(self, *, tmp_path: Path, auto_start: bool) -> None:
|
77
|
+
messages: Sequence[str] = []
|
78
|
+
|
79
|
+
async def sender(_: str, text: str, /) -> None:
|
80
|
+
messages.append(text)
|
81
|
+
|
82
|
+
logger = getLogger(str(tmp_path))
|
83
|
+
logger.addHandler(
|
84
|
+
handler := SlackHandlerService(
|
85
|
+
auto_start=auto_start, url="url", freq=0.05, sender=sender, timeout=1.0
|
86
|
+
)
|
87
|
+
)
|
88
|
+
async with handler:
|
89
|
+
logger.warning("message")
|
90
|
+
assert messages == ["message"]
|
91
|
+
|
92
|
+
@mark.skipif(get_env_var("SLACK", nullable=True) is None, reason="'SLACK' not set")
|
93
|
+
@throttle(duration=5 * MINUTE)
|
94
|
+
async def test_real_service(self, *, tmp_path: Path) -> None:
|
95
|
+
url = get_env_var("SLACK")
|
96
|
+
logger = getLogger(str(tmp_path))
|
97
|
+
logger.addHandler(handler := SlackHandlerService(url=url, freq=0.05))
|
98
|
+
async with timeout(1.0), handler:
|
99
|
+
for i in range(10):
|
100
|
+
logger.warning(
|
101
|
+
"message %d from %s",
|
102
|
+
i,
|
103
|
+
TestSlackHandler.test_real_service.__qualname__,
|
104
|
+
)
|
105
|
+
|
106
|
+
async def test_replace(self) -> None:
|
107
|
+
handler = SlackHandlerService(url="url")
|
108
|
+
new = handler.replace(freq=10.0)
|
109
|
+
assert new.url == handler.url
|
110
|
+
assert new.freq == 10.0
|
@@ -45,6 +45,7 @@ from utilities.sqlalchemy import (
|
|
45
45
|
Upserter,
|
46
46
|
UpserterError,
|
47
47
|
UpsertItemsError,
|
48
|
+
UpsertService,
|
48
49
|
_get_dialect,
|
49
50
|
_get_dialect_max_params,
|
50
51
|
_InsertItem,
|
@@ -1183,6 +1184,35 @@ class TestUpserter:
|
|
1183
1184
|
with raises(UpserterError, match="Error running 'Upserter'"):
|
1184
1185
|
raise UpserterError(upserter=upserter)
|
1185
1186
|
|
1187
|
+
# service
|
1188
|
+
|
1189
|
+
@given(
|
1190
|
+
data=data(),
|
1191
|
+
name=_table_names(),
|
1192
|
+
triples=_upsert_lists(nullable=True, min_size=1),
|
1193
|
+
)
|
1194
|
+
@mark.flaky
|
1195
|
+
@settings(max_examples=1, phases={Phase.generate})
|
1196
|
+
async def test_main_service(
|
1197
|
+
self, *, data: DataObject, name: str, triples: list[tuple[int, bool, bool]]
|
1198
|
+
) -> None:
|
1199
|
+
table = Table(
|
1200
|
+
name,
|
1201
|
+
MetaData(),
|
1202
|
+
Column("id_", Integer, primary_key=True),
|
1203
|
+
Column("value", Boolean, nullable=True),
|
1204
|
+
)
|
1205
|
+
engine = await sqlalchemy_engines(data, table)
|
1206
|
+
service = UpsertService(freq=0.1, timeout=1.0, engine=engine)
|
1207
|
+
pairs = [(id_, init) for id_, init, _ in triples]
|
1208
|
+
async with service:
|
1209
|
+
service.put_right_nowait((pairs, table))
|
1210
|
+
|
1211
|
+
sel = select(table)
|
1212
|
+
async with engine.begin() as conn:
|
1213
|
+
res = (await conn.execute(sel)).all()
|
1214
|
+
assert set(res) == set(pairs)
|
1215
|
+
|
1186
1216
|
|
1187
1217
|
class TestUpsertItems:
|
1188
1218
|
@given(data=data(), name=_table_names(), triple=_upsert_triples(nullable=True))
|
@@ -901,6 +901,7 @@ class Looper(Generic[_T]):
|
|
901
901
|
timeout: Duration | None | Sentinel = sentinel,
|
902
902
|
timeout_error: type[Exception] | Sentinel = sentinel,
|
903
903
|
_debug: bool | Sentinel = sentinel,
|
904
|
+
**kwargs: Any,
|
904
905
|
) -> Self:
|
905
906
|
"""Replace elements of the looper."""
|
906
907
|
return replace_non_sentinel(
|
@@ -913,6 +914,7 @@ class Looper(Generic[_T]):
|
|
913
914
|
timeout=timeout,
|
914
915
|
timeout_error=timeout_error,
|
915
916
|
_debug=_debug,
|
917
|
+
**kwargs,
|
916
918
|
)
|
917
919
|
|
918
920
|
def request_restart(self) -> None:
|
@@ -3,14 +3,20 @@ from __future__ import annotations
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from http import HTTPStatus
|
5
5
|
from logging import NOTSET, Handler, LogRecord
|
6
|
-
from typing import TYPE_CHECKING, override
|
6
|
+
from typing import TYPE_CHECKING, Any, Self, override
|
7
7
|
|
8
8
|
from slack_sdk.webhook.async_client import AsyncWebhookClient
|
9
9
|
|
10
|
-
from utilities.asyncio import
|
10
|
+
from utilities.asyncio import (
|
11
|
+
InfiniteQueueLooper,
|
12
|
+
Looper,
|
13
|
+
LooperTimeoutError,
|
14
|
+
timeout_dur,
|
15
|
+
)
|
11
16
|
from utilities.datetime import MINUTE, SECOND, datetime_duration_to_float
|
12
17
|
from utilities.functools import cache
|
13
18
|
from utilities.math import safe_round
|
19
|
+
from utilities.sentinel import Sentinel, sentinel
|
14
20
|
|
15
21
|
if TYPE_CHECKING:
|
16
22
|
from collections.abc import Callable
|
@@ -73,6 +79,90 @@ class SlackHandler(Handler, InfiniteQueueLooper[None, str]):
|
|
73
79
|
await self.sender(self.url, text)
|
74
80
|
|
75
81
|
|
82
|
+
@dataclass(init=False, unsafe_hash=True)
|
83
|
+
class SlackHandlerService(Handler, Looper[str]):
|
84
|
+
"""Service to send messages to Slack."""
|
85
|
+
|
86
|
+
@override
|
87
|
+
def __init__(
|
88
|
+
self,
|
89
|
+
*,
|
90
|
+
url: str,
|
91
|
+
auto_start: bool = False,
|
92
|
+
empty_upon_exit: bool = True,
|
93
|
+
freq: Duration = SECOND,
|
94
|
+
backoff: Duration = SECOND,
|
95
|
+
logger: str | None = None,
|
96
|
+
timeout: Duration | None = None,
|
97
|
+
timeout_error: type[Exception] = LooperTimeoutError,
|
98
|
+
_debug: bool = False,
|
99
|
+
level: int = NOTSET,
|
100
|
+
sender: Callable[[str, str], Coroutine1[None]] = _send_adapter,
|
101
|
+
send_timeout: Duration = SECOND,
|
102
|
+
) -> None:
|
103
|
+
Looper.__init__( # Looper first
|
104
|
+
self,
|
105
|
+
auto_start=auto_start,
|
106
|
+
freq=freq,
|
107
|
+
empty_upon_exit=empty_upon_exit,
|
108
|
+
backoff=backoff,
|
109
|
+
logger=logger,
|
110
|
+
timeout=timeout,
|
111
|
+
timeout_error=timeout_error,
|
112
|
+
_debug=_debug,
|
113
|
+
)
|
114
|
+
Looper.__post_init__(self)
|
115
|
+
Handler.__init__(self, level=level) # Handler next
|
116
|
+
self.url = url
|
117
|
+
self.sender = sender
|
118
|
+
self.send_timeout = send_timeout
|
119
|
+
|
120
|
+
@override
|
121
|
+
def emit(self, record: LogRecord) -> None:
|
122
|
+
fmtted = self.format(record)
|
123
|
+
try:
|
124
|
+
self.put_right_nowait(fmtted)
|
125
|
+
except Exception: # noqa: BLE001 # pragma: no cover
|
126
|
+
self.handleError(record)
|
127
|
+
|
128
|
+
@override
|
129
|
+
async def core(self) -> None:
|
130
|
+
await super().core()
|
131
|
+
if self.empty():
|
132
|
+
return
|
133
|
+
text = "\n".join(self.get_all_nowait())
|
134
|
+
async with timeout_dur(duration=self.send_timeout):
|
135
|
+
await self.sender(self.url, text)
|
136
|
+
|
137
|
+
@override
|
138
|
+
def replace(
|
139
|
+
self,
|
140
|
+
*,
|
141
|
+
auto_start: bool | Sentinel = sentinel,
|
142
|
+
empty_upon_exit: bool | Sentinel = sentinel,
|
143
|
+
freq: Duration | Sentinel = sentinel,
|
144
|
+
backoff: Duration | Sentinel = sentinel,
|
145
|
+
logger: str | None | Sentinel = sentinel,
|
146
|
+
timeout: Duration | None | Sentinel = sentinel,
|
147
|
+
timeout_error: type[Exception] | Sentinel = sentinel,
|
148
|
+
_debug: bool | Sentinel = sentinel,
|
149
|
+
**kwargs: Any,
|
150
|
+
) -> Self:
|
151
|
+
"""Replace elements of the looper."""
|
152
|
+
return super().replace(
|
153
|
+
url=self.url,
|
154
|
+
auto_start=auto_start,
|
155
|
+
empty_upon_exit=empty_upon_exit,
|
156
|
+
freq=freq,
|
157
|
+
backoff=backoff,
|
158
|
+
logger=logger,
|
159
|
+
timeout=timeout,
|
160
|
+
timeout_error=timeout_error,
|
161
|
+
_debug=_debug,
|
162
|
+
**kwargs,
|
163
|
+
)
|
164
|
+
|
165
|
+
|
76
166
|
##
|
77
167
|
|
78
168
|
|
@@ -106,4 +196,4 @@ def _get_client(url: str, /, *, timeout: Duration = _TIMEOUT) -> AsyncWebhookCli
|
|
106
196
|
return AsyncWebhookClient(url, timeout=timeout_use)
|
107
197
|
|
108
198
|
|
109
|
-
__all__ = ["SendToSlackError", "SlackHandler", "send_to_slack"]
|
199
|
+
__all__ = ["SendToSlackError", "SlackHandler", "SlackHandlerService", "send_to_slack"]
|
@@ -57,7 +57,8 @@ from sqlalchemy.orm import (
|
|
57
57
|
from sqlalchemy.orm.exc import UnmappedClassError
|
58
58
|
from sqlalchemy.pool import NullPool, Pool
|
59
59
|
|
60
|
-
from utilities.asyncio import InfiniteQueueLooper, timeout_dur
|
60
|
+
from utilities.asyncio import InfiniteQueueLooper, Looper, timeout_dur
|
61
|
+
from utilities.datetime import SECOND
|
61
62
|
from utilities.functions import (
|
62
63
|
ensure_str,
|
63
64
|
get_class_name,
|
@@ -654,6 +655,42 @@ class UpserterError(Exception):
|
|
654
655
|
return f"Error running {get_class_name(self.upserter)!r}"
|
655
656
|
|
656
657
|
|
658
|
+
@dataclass(kw_only=True)
|
659
|
+
class UpsertService(Looper[_InsertItem]):
|
660
|
+
"""Service to upsert items to a database."""
|
661
|
+
|
662
|
+
# base
|
663
|
+
freq: Duration = field(default=SECOND, repr=False)
|
664
|
+
backoff: Duration = field(default=SECOND, repr=False)
|
665
|
+
empty_upon_exit: bool = field(default=True, repr=False)
|
666
|
+
# self
|
667
|
+
engine: AsyncEngine
|
668
|
+
snake: bool = False
|
669
|
+
selected_or_all: _SelectedOrAll = "selected"
|
670
|
+
chunk_size_frac: float = CHUNK_SIZE_FRAC
|
671
|
+
assume_tables_exist: bool = False
|
672
|
+
timeout_create: Duration | None = None
|
673
|
+
error_create: type[Exception] = TimeoutError
|
674
|
+
timeout_insert: Duration | None = None
|
675
|
+
error_insert: type[Exception] = TimeoutError
|
676
|
+
|
677
|
+
@override
|
678
|
+
async def core(self) -> None:
|
679
|
+
await super().core()
|
680
|
+
await upsert_items(
|
681
|
+
self.engine,
|
682
|
+
*self.get_all_nowait(),
|
683
|
+
snake=self.snake,
|
684
|
+
selected_or_all=self.selected_or_all,
|
685
|
+
chunk_size_frac=self.chunk_size_frac,
|
686
|
+
assume_tables_exist=self.assume_tables_exist,
|
687
|
+
timeout_create=self.timeout_create,
|
688
|
+
error_create=self.error_create,
|
689
|
+
timeout_insert=self.timeout_insert,
|
690
|
+
error_insert=self.error_insert,
|
691
|
+
)
|
692
|
+
|
693
|
+
|
657
694
|
##
|
658
695
|
|
659
696
|
|
@@ -1109,6 +1146,7 @@ __all__ = [
|
|
1109
1146
|
"InsertItemsError",
|
1110
1147
|
"TablenameMixin",
|
1111
1148
|
"UpsertItemsError",
|
1149
|
+
"UpsertService",
|
1112
1150
|
"Upserter",
|
1113
1151
|
"UpserterError",
|
1114
1152
|
"check_engine",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_missing/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_missing/module.py
RENAMED
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/outer_1.py
RENAMED
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_with/outer_2.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/module_1.py
RENAMED
File without changes
|
{dycw_utilities-0.125.24 → dycw_utilities-0.125.26}/src/tests/modules/package_without/module_2.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|