dycw-utilities 0.148.5__tar.gz → 0.149.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.148.5 → dycw_utilities-0.149.0}/PKG-INFO +1 -1
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/pyproject.toml +2 -2
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_postgres.py +39 -51
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/__init__.py +1 -1
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/postgres.py +214 -110
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/.gitignore +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/LICENSE +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/README.md +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/conftest.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_missing/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_missing/module.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/outer_1.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/outer_2.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/subpackage/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/subpackage/inner_1.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/subpackage/inner_2.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/subpackage/inner_3.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_without/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_without/module_1.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_without/module_2.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/standalone.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/with_imports.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__obj.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestMultipleRegressionFixtures__test_main__series.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_int.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__false.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_literal__true.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestOrjsonRegressionFixture__test_dataclass_nested.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_dataframe.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/regressions/test_pytest_regressions/TestPolarsRegressionFixture__test_series.json +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_altair.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_asyncio.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_atomicwrites.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_atools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_cachetools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_click.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_concurrent.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_contextlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_contextvars.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_cryptography.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_cvxpy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_dataclasses.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_enum.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_errors.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_eventkit.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_fastapi.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_fpdf2.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_functions.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_functools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_getpass.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_gzip.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_hashlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_http.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_hypothesis.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_importlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_inflect.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_ipython.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_iterables.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_json.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_jupyter.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_libcst.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_lightweight_charts.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_logging.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_math.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_memory_profiler.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_modules.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_more_itertools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_numpy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_objects/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_objects/objects.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_operator.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_optuna.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_orjson.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_os.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_parse.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pathlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_period.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pickle.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_platform.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_polars.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_polars_ols.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pottery.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pqdm.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_psutil.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pyinstrument.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pytest.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pytest_randomly.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_pytest_regressions.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_random.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_re.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_redis.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_reprlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_scipy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_sentinel.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_shelve.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_slack_sdk.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_socket.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_sqlalchemy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_statsmodels.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_string.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_tempfile.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_text.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_threading.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_timer.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_traceback.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_typed_settings.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_types.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_typing.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_typing_funcs/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_typing_funcs/no_future.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_typing_funcs/with_future.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_tzdata.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_tzlocal.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_uuid.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_version.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_warnings.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_whenever.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_yield_access/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_yield_access/script.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_yield_access/script.sh +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_zipfile.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/test_zoneinfo.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/altair.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/asyncio.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/atomicwrites.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/atools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/cachetools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/click.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/concurrent.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/contextlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/contextvars.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/cryptography.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/cvxpy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/dataclasses.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/enum.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/errors.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/eventkit.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/fastapi.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/fpdf2.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/functions.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/functools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/getpass.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/gzip.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/hashlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/http.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/hypothesis.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/importlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/inflect.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/ipython.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/iterables.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/json.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/jupyter.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/libcst.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/lightweight_charts.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/logging.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/math.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/memory_profiler.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/modules.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/more_itertools.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/numpy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/operator.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/optuna.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/orjson.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/os.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/parse.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pathlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/period.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pickle.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/platform.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/polars.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/polars_ols.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pottery.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pqdm.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/psutil.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/py.typed +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pyinstrument.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pytest.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pytest_plugins/__init__.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pytest_plugins/pytest_randomly.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pytest_plugins/pytest_regressions.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/pytest_regressions.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/random.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/re.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/redis.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/reprlib.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/scipy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/sentinel.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/shelve.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/slack_sdk.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/socket.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/sqlalchemy.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/sqlalchemy_polars.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/statsmodels.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/string.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/tempfile.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/text.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/threading.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/timer.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/traceback.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/typed_settings.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/types.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/typing.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/tzdata.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/tzlocal.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/uuid.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/version.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/warnings.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/whenever.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/zipfile.py +0 -0
- {dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/utilities/zoneinfo.py +0 -0
@@ -102,7 +102,7 @@ dependencies = [
|
|
102
102
|
name = "dycw-utilities"
|
103
103
|
readme = "README.md"
|
104
104
|
requires-python = ">= 3.12"
|
105
|
-
version = "0.
|
105
|
+
version = "0.149.0"
|
106
106
|
|
107
107
|
[project.entry-points.pytest11]
|
108
108
|
pytest-randomly = "utilities.pytest_plugins.pytest_randomly"
|
@@ -135,7 +135,7 @@ test = [
|
|
135
135
|
# bump-my-version
|
136
136
|
[tool.bumpversion]
|
137
137
|
allow_dirty = true
|
138
|
-
current_version = "0.
|
138
|
+
current_version = "0.149.0"
|
139
139
|
|
140
140
|
[[tool.bumpversion.files]]
|
141
141
|
filename = "src/utilities/__init__.py"
|
@@ -9,15 +9,15 @@ from sqlalchemy import URL, Column, Integer, MetaData, Table
|
|
9
9
|
|
10
10
|
from utilities.hypothesis import integers, temp_paths, text_ascii
|
11
11
|
from utilities.postgres import (
|
12
|
-
|
12
|
+
_build_pg_dump,
|
13
|
+
_build_pg_restore_or_psql,
|
14
|
+
_extract_url,
|
15
|
+
_ExtractURLDatabaseError,
|
16
|
+
_ExtractURLHostError,
|
17
|
+
_ExtractURLPortError,
|
13
18
|
_PGDumpFormat,
|
14
|
-
_PGDumpHostError,
|
15
|
-
_PGDumpPortError,
|
16
|
-
_PGRestoreDatabaseError,
|
17
|
-
_PGRestoreHostError,
|
18
|
-
_PGRestorePortError,
|
19
19
|
pg_dump,
|
20
|
-
|
20
|
+
restore,
|
21
21
|
)
|
22
22
|
from utilities.typing import get_literal_elements
|
23
23
|
|
@@ -53,10 +53,13 @@ def urls(draw: DrawFn, /) -> URL:
|
|
53
53
|
|
54
54
|
|
55
55
|
class TestPGDump:
|
56
|
+
@given(url=urls(), path=temp_paths(), logger=text_ascii(min_size=1) | none())
|
57
|
+
async def test_main(self, *, url: URL, path: Path, logger: str | None) -> None:
|
58
|
+
_ = await pg_dump(url, path, dry_run=True, logger=logger)
|
59
|
+
|
56
60
|
@given(
|
57
61
|
url=urls(),
|
58
62
|
path=temp_paths(),
|
59
|
-
docker=text_ascii(min_size=1) | none(),
|
60
63
|
format_=sampled_from(get_literal_elements(_PGDumpFormat)),
|
61
64
|
jobs=integers(min_value=0) | none(),
|
62
65
|
schemas=lists(text_ascii(min_size=1)) | none(),
|
@@ -65,14 +68,13 @@ class TestPGDump:
|
|
65
68
|
tables_exc=tables() | none(),
|
66
69
|
inserts=booleans(),
|
67
70
|
on_conflict_do_nothing=booleans(),
|
68
|
-
|
71
|
+
docker=text_ascii(min_size=1) | none(),
|
69
72
|
)
|
70
|
-
|
73
|
+
def test_build(
|
71
74
|
self,
|
72
75
|
*,
|
73
76
|
url: URL,
|
74
77
|
path: Path,
|
75
|
-
docker: str | None,
|
76
78
|
format_: _PGDumpFormat,
|
77
79
|
jobs: int | None,
|
78
80
|
schemas: list[str] | None,
|
@@ -81,12 +83,11 @@ class TestPGDump:
|
|
81
83
|
tables_exc: list[Table | str] | None,
|
82
84
|
inserts: bool,
|
83
85
|
on_conflict_do_nothing: bool,
|
84
|
-
|
86
|
+
docker: str | None,
|
85
87
|
) -> None:
|
86
|
-
_ =
|
88
|
+
_ = _build_pg_dump(
|
87
89
|
url,
|
88
90
|
path,
|
89
|
-
docker=docker,
|
90
91
|
format_=format_,
|
91
92
|
jobs=jobs,
|
92
93
|
schemas=schemas,
|
@@ -95,87 +96,74 @@ class TestPGDump:
|
|
95
96
|
tables_exc=tables_exc,
|
96
97
|
inserts=inserts,
|
97
98
|
on_conflict_do_nothing=on_conflict_do_nothing,
|
98
|
-
|
99
|
-
dry_run=True,
|
99
|
+
docker=docker,
|
100
100
|
)
|
101
101
|
|
102
|
-
async def test_error_database(self, *, tmp_path: Path) -> None:
|
103
|
-
url = URL.create("postgres")
|
104
|
-
with raises(
|
105
|
-
_PGDumpDatabaseError, match="Expected URL to contain a 'database'; got .*"
|
106
|
-
):
|
107
|
-
_ = await pg_dump(url, tmp_path, dry_run=True)
|
108
102
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
103
|
+
class TestRestore:
|
104
|
+
@given(url=urls(), path=temp_paths(), logger=text_ascii(min_size=1) | none())
|
105
|
+
async def test_main(self, *, url: URL, path: Path, logger: str | None) -> None:
|
106
|
+
_ = await restore(url, path, dry_run=True, logger=logger)
|
113
107
|
|
114
|
-
async def test_error_port(self, *, tmp_path: Path) -> None:
|
115
|
-
url = URL.create("postgres", database="database", host="host")
|
116
|
-
with raises(_PGDumpPortError, match="Expected URL to contain a 'port'; got .*"):
|
117
|
-
_ = await pg_dump(url, tmp_path, dry_run=True)
|
118
|
-
|
119
|
-
|
120
|
-
class TestPGRestore:
|
121
108
|
@given(
|
122
109
|
url=urls(),
|
123
110
|
path=temp_paths(),
|
111
|
+
psql=booleans(),
|
124
112
|
database=text_ascii(min_size=1) | none(),
|
125
|
-
docker=text_ascii(min_size=1) | none(),
|
126
113
|
data_only=booleans(),
|
127
114
|
jobs=integers(min_value=0) | none(),
|
128
115
|
schemas=lists(text_ascii(min_size=1)) | none(),
|
129
116
|
schemas_exc=lists(text_ascii(min_size=1)) | none(),
|
130
117
|
tables=tables() | none(),
|
131
|
-
|
118
|
+
docker=text_ascii(min_size=1) | none(),
|
132
119
|
)
|
133
|
-
|
120
|
+
def test_build(
|
134
121
|
self,
|
135
122
|
*,
|
136
123
|
url: URL,
|
137
124
|
path: Path,
|
125
|
+
psql: bool,
|
138
126
|
database: str | None,
|
139
|
-
docker: str | None,
|
140
127
|
data_only: bool,
|
141
128
|
jobs: int | None,
|
142
129
|
schemas: list[str] | None,
|
143
130
|
schemas_exc: list[str] | None,
|
144
131
|
tables: list[Table | str] | None,
|
145
|
-
|
132
|
+
docker: str | None,
|
146
133
|
) -> None:
|
147
|
-
_ =
|
134
|
+
_ = _build_pg_restore_or_psql(
|
148
135
|
url,
|
149
136
|
path,
|
137
|
+
psql=psql,
|
150
138
|
database=database,
|
151
|
-
docker=docker,
|
152
139
|
data_only=data_only,
|
153
140
|
jobs=jobs,
|
154
141
|
schemas=schemas,
|
155
142
|
schemas_exc=schemas_exc,
|
156
143
|
tables=tables,
|
157
|
-
|
158
|
-
dry_run=True,
|
144
|
+
docker=docker,
|
159
145
|
)
|
160
146
|
|
161
|
-
|
147
|
+
|
148
|
+
class TestExtractURL:
|
149
|
+
def test_database(self) -> None:
|
162
150
|
url = URL.create("postgres")
|
163
151
|
with raises(
|
164
|
-
|
152
|
+
_ExtractURLDatabaseError,
|
165
153
|
match="Expected URL to contain a 'database'; got .*",
|
166
154
|
):
|
167
|
-
_ =
|
155
|
+
_ = _extract_url(url)
|
168
156
|
|
169
|
-
|
157
|
+
def test_host(self) -> None:
|
170
158
|
url = URL.create("postgres", database="database")
|
171
159
|
with raises(
|
172
|
-
|
160
|
+
_ExtractURLHostError, match="Expected URL to contain a 'host'; got .*"
|
173
161
|
):
|
174
|
-
_ =
|
162
|
+
_ = _extract_url(url)
|
175
163
|
|
176
|
-
|
164
|
+
def test_port(self) -> None:
|
177
165
|
url = URL.create("postgres", database="database", host="host")
|
178
166
|
with raises(
|
179
|
-
|
167
|
+
_ExtractURLPortError, match="Expected URL to contain a 'port'; got .*"
|
180
168
|
):
|
181
|
-
_ =
|
169
|
+
_ = _extract_url(url)
|
@@ -12,6 +12,7 @@ from utilities.asyncio import stream_command
|
|
12
12
|
from utilities.iterables import always_iterable
|
13
13
|
from utilities.logging import get_logger
|
14
14
|
from utilities.os import temp_environ
|
15
|
+
from utilities.pathlib import ensure_suffix
|
15
16
|
from utilities.sqlalchemy import get_table_name
|
16
17
|
from utilities.timer import Timer
|
17
18
|
from utilities.types import PathLike
|
@@ -31,7 +32,6 @@ async def pg_dump(
|
|
31
32
|
path: PathLike,
|
32
33
|
/,
|
33
34
|
*,
|
34
|
-
docker: str | None = None,
|
35
35
|
format_: _PGDumpFormat = "plain",
|
36
36
|
jobs: int | None = None,
|
37
37
|
schemas: MaybeListStr | None = None,
|
@@ -40,26 +40,92 @@ async def pg_dump(
|
|
40
40
|
tables_exc: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
|
41
41
|
inserts: bool = False,
|
42
42
|
on_conflict_do_nothing: bool = False,
|
43
|
-
|
43
|
+
docker: str | None = None,
|
44
44
|
dry_run: bool = False,
|
45
|
+
logger: LoggerOrName | None = None,
|
45
46
|
) -> None:
|
46
47
|
"""Run `pg_dump`."""
|
47
48
|
path = Path(path)
|
48
49
|
path.parent.mkdir(parents=True, exist_ok=True)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
cmd = _build_pg_dump(
|
51
|
+
url,
|
52
|
+
path,
|
53
|
+
format_=format_,
|
54
|
+
jobs=jobs,
|
55
|
+
schemas=schemas,
|
56
|
+
schemas_exc=schemas_exc,
|
57
|
+
tables=tables,
|
58
|
+
tables_exc=tables_exc,
|
59
|
+
inserts=inserts,
|
60
|
+
on_conflict_do_nothing=on_conflict_do_nothing,
|
61
|
+
docker=docker,
|
62
|
+
)
|
63
|
+
if dry_run:
|
64
|
+
if logger is not None:
|
65
|
+
get_logger(logger=logger).info("Would run %r", str(cmd))
|
66
|
+
return
|
67
|
+
with temp_environ(PGPASSWORD=url.password), Timer() as timer: # pragma: no cover
|
68
|
+
try:
|
69
|
+
output = await stream_command(cmd)
|
70
|
+
except KeyboardInterrupt:
|
71
|
+
if logger is not None:
|
72
|
+
get_logger(logger=logger).info(
|
73
|
+
"Cancelled backup to %r after %s", str(path), timer
|
74
|
+
)
|
75
|
+
rmtree(path, ignore_errors=True)
|
76
|
+
else:
|
77
|
+
match output.return_code:
|
78
|
+
case 0:
|
79
|
+
if logger is not None:
|
80
|
+
get_logger(logger=logger).info(
|
81
|
+
"Backup to %r finished after %s", str(path), timer
|
82
|
+
)
|
83
|
+
case _:
|
84
|
+
if logger is not None:
|
85
|
+
get_logger(logger=logger).exception(
|
86
|
+
"Backup to %r failed after %s\nstderr:\n%s",
|
87
|
+
str(path),
|
88
|
+
timer,
|
89
|
+
output.stderr,
|
90
|
+
)
|
91
|
+
rmtree(path, ignore_errors=True)
|
92
|
+
|
93
|
+
|
94
|
+
def _build_pg_dump(
|
95
|
+
url: URL,
|
96
|
+
path: PathLike,
|
97
|
+
/,
|
98
|
+
*,
|
99
|
+
format_: _PGDumpFormat = "plain",
|
100
|
+
jobs: int | None = None,
|
101
|
+
schemas: MaybeListStr | None = None,
|
102
|
+
schemas_exc: MaybeListStr | None = None,
|
103
|
+
tables: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
|
104
|
+
tables_exc: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
|
105
|
+
inserts: bool = False,
|
106
|
+
on_conflict_do_nothing: bool = False,
|
107
|
+
docker: str | None = None,
|
108
|
+
) -> str:
|
109
|
+
database, host, port = _extract_url(url)
|
110
|
+
match format_:
|
111
|
+
case "plain":
|
112
|
+
suffix = ".sql"
|
113
|
+
case "custom":
|
114
|
+
suffix = ".pgdump"
|
115
|
+
case "directory":
|
116
|
+
suffix = None
|
117
|
+
case "tar":
|
118
|
+
suffix = ".tar"
|
119
|
+
case _ as never:
|
120
|
+
assert_never(never)
|
121
|
+
file = Path(path)
|
122
|
+
if suffix is not None:
|
123
|
+
file = ensure_suffix(file, suffix)
|
124
|
+
parts: list[str] = [
|
59
125
|
"pg_dump",
|
60
126
|
# general options
|
61
|
-
f"--dbname={
|
62
|
-
f"--file={str(
|
127
|
+
f"--dbname={database}",
|
128
|
+
f"--file={str(file)!r}",
|
63
129
|
f"--format={format_}",
|
64
130
|
"--verbose",
|
65
131
|
# output options
|
@@ -69,10 +135,10 @@ async def pg_dump(
|
|
69
135
|
"--no-privileges",
|
70
136
|
"--if-exists",
|
71
137
|
# connection options
|
72
|
-
f"--host={
|
73
|
-
f"--port={
|
138
|
+
f"--host={host}",
|
139
|
+
f"--port={port}",
|
74
140
|
"--no-password",
|
75
|
-
]
|
141
|
+
]
|
76
142
|
if (format_ == "directory") and (jobs is not None):
|
77
143
|
parts.append(f"--jobs={jobs}")
|
78
144
|
if schemas is not None:
|
@@ -91,7 +157,43 @@ async def pg_dump(
|
|
91
157
|
parts.append("--on-conflict-do-nothing")
|
92
158
|
if url.username is not None:
|
93
159
|
parts.append(f"--username={url.username}")
|
94
|
-
|
160
|
+
if docker is not None:
|
161
|
+
parts = _wrap_docker(parts, docker)
|
162
|
+
return " ".join(parts)
|
163
|
+
|
164
|
+
|
165
|
+
##
|
166
|
+
|
167
|
+
|
168
|
+
async def restore(
|
169
|
+
url: URL,
|
170
|
+
path: PathLike,
|
171
|
+
/,
|
172
|
+
*,
|
173
|
+
psql: bool = False,
|
174
|
+
database: str | None = None,
|
175
|
+
data_only: bool = False,
|
176
|
+
jobs: int | None = None,
|
177
|
+
schemas: MaybeListStr | None = None,
|
178
|
+
schemas_exc: MaybeListStr | None = None,
|
179
|
+
tables: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
|
180
|
+
docker: str | None = None,
|
181
|
+
dry_run: bool = False,
|
182
|
+
logger: LoggerOrName | None = None,
|
183
|
+
) -> None:
|
184
|
+
"""Run `pg_restore`/`psql`."""
|
185
|
+
cmd = _build_pg_restore_or_psql(
|
186
|
+
url,
|
187
|
+
path,
|
188
|
+
psql=psql,
|
189
|
+
database=database,
|
190
|
+
data_only=data_only,
|
191
|
+
jobs=jobs,
|
192
|
+
schemas=schemas,
|
193
|
+
schemas_exc=schemas_exc,
|
194
|
+
tables=tables,
|
195
|
+
docker=docker,
|
196
|
+
)
|
95
197
|
if dry_run:
|
96
198
|
if logger is not None:
|
97
199
|
get_logger(logger=logger).info("Would run %r", str(cmd))
|
@@ -102,89 +204,75 @@ async def pg_dump(
|
|
102
204
|
except KeyboardInterrupt:
|
103
205
|
if logger is not None:
|
104
206
|
get_logger(logger=logger).info(
|
105
|
-
"Cancelled
|
207
|
+
"Cancelled restore from %r after %s", str(path), timer
|
106
208
|
)
|
107
|
-
rmtree(path, ignore_errors=True)
|
108
209
|
else:
|
109
210
|
match output.return_code:
|
110
211
|
case 0:
|
111
212
|
if logger is not None:
|
112
213
|
get_logger(logger=logger).info(
|
113
|
-
"
|
214
|
+
"Restore from %r finished after %s", str(path), timer
|
114
215
|
)
|
115
216
|
case _:
|
116
217
|
if logger is not None:
|
117
218
|
get_logger(logger=logger).exception(
|
118
|
-
"
|
219
|
+
"Restore from %r failed after %s\nstderr:\n%s",
|
119
220
|
str(path),
|
120
221
|
timer,
|
121
222
|
output.stderr,
|
122
223
|
)
|
123
|
-
rmtree(path, ignore_errors=True)
|
124
|
-
|
125
|
-
|
126
|
-
@dataclass(kw_only=True, slots=True)
|
127
|
-
class PGDumpError(Exception):
|
128
|
-
url: URL
|
129
|
-
|
130
|
-
|
131
|
-
@dataclass(kw_only=True, slots=True)
|
132
|
-
class _PGDumpDatabaseError(PGDumpError):
|
133
|
-
@override
|
134
|
-
def __str__(self) -> str:
|
135
|
-
return f"Expected URL to contain a 'database'; got {self.url}"
|
136
|
-
|
137
|
-
|
138
|
-
@dataclass(kw_only=True, slots=True)
|
139
|
-
class _PGDumpHostError(PGDumpError):
|
140
|
-
@override
|
141
|
-
def __str__(self) -> str:
|
142
|
-
return f"Expected URL to contain a 'host'; got {self.url}"
|
143
|
-
|
144
|
-
|
145
|
-
@dataclass(kw_only=True, slots=True)
|
146
|
-
class _PGDumpPortError(PGDumpError):
|
147
|
-
@override
|
148
|
-
def __str__(self) -> str:
|
149
|
-
return f"Expected URL to contain a 'port'; got {self.url}"
|
150
224
|
|
151
225
|
|
152
226
|
##
|
153
227
|
|
154
228
|
|
155
|
-
|
229
|
+
def _build_pg_restore_or_psql(
|
156
230
|
url: URL,
|
157
231
|
path: PathLike,
|
158
232
|
/,
|
159
233
|
*,
|
234
|
+
psql: bool = False,
|
160
235
|
database: str | None = None,
|
236
|
+
data_only: bool = False,
|
237
|
+
jobs: int | None = None,
|
238
|
+
schemas: MaybeListStr | None = None,
|
239
|
+
schemas_exc: MaybeListStr | None = None,
|
240
|
+
tables: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
|
161
241
|
docker: str | None = None,
|
242
|
+
) -> str:
|
243
|
+
path = Path(path)
|
244
|
+
if (path.suffix == ".sql") or psql:
|
245
|
+
return _build_psql(url, path, database=database, docker=docker)
|
246
|
+
return _build_pg_restore(
|
247
|
+
url,
|
248
|
+
path,
|
249
|
+
database=database,
|
250
|
+
data_only=data_only,
|
251
|
+
jobs=jobs,
|
252
|
+
schemas=schemas,
|
253
|
+
schemas_exc=schemas_exc,
|
254
|
+
tables=tables,
|
255
|
+
docker=docker,
|
256
|
+
)
|
257
|
+
|
258
|
+
|
259
|
+
def _build_pg_restore(
|
260
|
+
url: URL,
|
261
|
+
path: PathLike,
|
262
|
+
/,
|
263
|
+
*,
|
264
|
+
database: str | None = None,
|
162
265
|
data_only: bool = False,
|
163
266
|
jobs: int | None = None,
|
164
267
|
schemas: MaybeListStr | None = None,
|
165
268
|
schemas_exc: MaybeListStr | None = None,
|
166
269
|
tables: MaybeSequence[TableOrORMInstOrClass | str] | None = None,
|
167
|
-
|
168
|
-
|
169
|
-
) -> None:
|
270
|
+
docker: str | None = None,
|
271
|
+
) -> str:
|
170
272
|
"""Run `pg_restore`."""
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
case None, str() as database_use:
|
175
|
-
...
|
176
|
-
case None, None:
|
177
|
-
raise _PGRestoreDatabaseError(url=url)
|
178
|
-
case _ as never:
|
179
|
-
assert_never(never)
|
180
|
-
if url.host is None:
|
181
|
-
raise _PGRestoreHostError(url=url)
|
182
|
-
if url.port is None:
|
183
|
-
raise _PGRestorePortError(url=url)
|
184
|
-
parts: list[str] = []
|
185
|
-
if docker is not None:
|
186
|
-
parts.extend(["docker", "exec", "-it", docker])
|
187
|
-
parts.extend([
|
273
|
+
url_database, host, port = _extract_url(url)
|
274
|
+
database_use = url_database if database is None else database
|
275
|
+
parts: list[str] = [
|
188
276
|
"pg_restore",
|
189
277
|
# general options
|
190
278
|
f"--dbname={database_use}",
|
@@ -194,10 +282,10 @@ async def pg_restore(
|
|
194
282
|
"--no-owner",
|
195
283
|
"--no-privileges",
|
196
284
|
# connection options
|
197
|
-
f"--host={
|
198
|
-
f"--port={
|
285
|
+
f"--host={host}",
|
286
|
+
f"--port={port}",
|
199
287
|
"--no-password",
|
200
|
-
]
|
288
|
+
]
|
201
289
|
if data_only:
|
202
290
|
parts.append("--data-only")
|
203
291
|
else:
|
@@ -212,68 +300,80 @@ async def pg_restore(
|
|
212
300
|
parts.extend([f"--table={_get_table_name(t)}" for t in always_iterable(tables)])
|
213
301
|
if url.username is not None:
|
214
302
|
parts.append(f"--username={url.username}")
|
303
|
+
if docker is not None:
|
304
|
+
parts = _wrap_docker(parts, docker)
|
215
305
|
parts.append(str(path))
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
306
|
+
return " ".join(parts)
|
307
|
+
|
308
|
+
|
309
|
+
def _build_psql(
|
310
|
+
url: URL,
|
311
|
+
path: PathLike,
|
312
|
+
/,
|
313
|
+
*,
|
314
|
+
database: str | None = None,
|
315
|
+
docker: str | None = None,
|
316
|
+
) -> str:
|
317
|
+
"""Run `psql`."""
|
318
|
+
url_database, host, port = _extract_url(url)
|
319
|
+
database_use = url_database if database is None else database
|
320
|
+
parts: list[str] = [
|
321
|
+
"psql",
|
322
|
+
# general options
|
323
|
+
f"--dbname={database_use}",
|
324
|
+
f"--file={str(path)!r}",
|
325
|
+
# connection options
|
326
|
+
f"--host={host}",
|
327
|
+
f"--port={port}",
|
328
|
+
"--no-password",
|
329
|
+
]
|
330
|
+
if url.username is not None:
|
331
|
+
parts.append(f"--username={url.username}")
|
332
|
+
if docker is not None:
|
333
|
+
parts = _wrap_docker(parts, docker)
|
334
|
+
return " ".join(parts)
|
335
|
+
|
336
|
+
|
337
|
+
##
|
338
|
+
|
339
|
+
|
340
|
+
def _extract_url(url: URL, /) -> tuple[str, str, int]:
|
341
|
+
if url.database is None:
|
342
|
+
raise _ExtractURLDatabaseError(url=url)
|
343
|
+
if url.host is None:
|
344
|
+
raise _ExtractURLHostError(url=url)
|
345
|
+
if url.port is None:
|
346
|
+
raise _ExtractURLPortError(url=url)
|
347
|
+
return url.database, url.host, url.port
|
244
348
|
|
245
349
|
|
246
350
|
@dataclass(kw_only=True, slots=True)
|
247
|
-
class
|
351
|
+
class ExtractURLError(Exception):
|
248
352
|
url: URL
|
249
353
|
|
250
354
|
|
251
355
|
@dataclass(kw_only=True, slots=True)
|
252
|
-
class
|
356
|
+
class _ExtractURLDatabaseError(ExtractURLError):
|
253
357
|
@override
|
254
358
|
def __str__(self) -> str:
|
255
359
|
return f"Expected URL to contain a 'database'; got {self.url}"
|
256
360
|
|
257
361
|
|
258
362
|
@dataclass(kw_only=True, slots=True)
|
259
|
-
class
|
363
|
+
class _ExtractURLHostError(ExtractURLError):
|
260
364
|
@override
|
261
365
|
def __str__(self) -> str:
|
262
366
|
return f"Expected URL to contain a 'host'; got {self.url}"
|
263
367
|
|
264
368
|
|
265
369
|
@dataclass(kw_only=True, slots=True)
|
266
|
-
class
|
370
|
+
class _ExtractURLPortError(ExtractURLError):
|
267
371
|
@override
|
268
372
|
def __str__(self) -> str:
|
269
373
|
return f"Expected URL to contain a 'port'; got {self.url}"
|
270
374
|
|
271
375
|
|
272
|
-
##
|
273
|
-
|
274
|
-
|
275
376
|
def _get_table_name(obj: TableOrORMInstOrClass | str, /) -> str:
|
276
|
-
"""Get the table name from a Table or mapped class."""
|
277
377
|
match obj:
|
278
378
|
case Table() | DeclarativeBase() | type() as table_or_orm:
|
279
379
|
return get_table_name(table_or_orm)
|
@@ -283,4 +383,8 @@ def _get_table_name(obj: TableOrORMInstOrClass | str, /) -> str:
|
|
283
383
|
assert_never(never)
|
284
384
|
|
285
385
|
|
286
|
-
|
386
|
+
def _wrap_docker(parts: list[str], container: str, /) -> list[str]:
|
387
|
+
return ["docker", "exec", "-it", container, *parts]
|
388
|
+
|
389
|
+
|
390
|
+
__all__ = ["ExtractURLError", "pg_dump", "restore"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_missing/__init__.py
RENAMED
File without changes
|
{dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_missing/module.py
RENAMED
File without changes
|
{dycw_utilities-0.148.5 → dycw_utilities-0.149.0}/src/tests/modules/package_with/__init__.py
RENAMED
File without changes
|