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