sqlalchemy-multiple-db 2.1.1__tar.gz → 3.1.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.
- sqlalchemy_multiple_db-3.1.0/.github/SECURITY.md +8 -0
- sqlalchemy_multiple_db-3.1.0/.github/dependabot.yml +10 -0
- sqlalchemy_multiple_db-3.1.0/.github/workflows/ci.yml +36 -0
- sqlalchemy_multiple_db-3.1.0/.github/workflows/publish.yml +41 -0
- sqlalchemy_multiple_db-3.1.0/.gitignore +77 -0
- sqlalchemy_multiple_db-3.1.0/CHANGELOG.md +108 -0
- sqlalchemy_multiple_db-3.1.0/Makefile +134 -0
- sqlalchemy_multiple_db-3.1.0/PKG-INFO +124 -0
- sqlalchemy_multiple_db-3.1.0/README.md +98 -0
- sqlalchemy_multiple_db-3.1.0/examples/basic_usage.py +38 -0
- sqlalchemy_multiple_db-3.1.0/examples/healthcheck_and_options.py +39 -0
- sqlalchemy_multiple_db-3.1.0/examples/transaction_rollback.py +35 -0
- sqlalchemy_multiple_db-3.1.0/pyproject.toml +62 -0
- sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/__init__.py +14 -0
- sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/config.py +28 -0
- sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/exceptions.py +6 -0
- sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/manager.py +110 -0
- sqlalchemy_multiple_db-3.1.0/tests/test_config.py +44 -0
- sqlalchemy_multiple_db-3.1.0/tests/test_examples.py +29 -0
- sqlalchemy_multiple_db-3.1.0/tests/test_manager.py +144 -0
- sqlalchemy_multiple_db-3.1.0/tests/test_public_api.py +15 -0
- sqlalchemy_multiple_db-3.1.0/uv.lock +626 -0
- sqlalchemy-multiple-db-2.1.1/MANIFEST.in +0 -6
- sqlalchemy-multiple-db-2.1.1/PKG-INFO +0 -81
- sqlalchemy-multiple-db-2.1.1/README.md +0 -51
- sqlalchemy-multiple-db-2.1.1/pyproject.toml +0 -18
- sqlalchemy-multiple-db-2.1.1/setup.cfg +0 -4
- sqlalchemy-multiple-db-2.1.1/setup.py +0 -54
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db/__init__.py +0 -3
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db/helper.py +0 -107
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db/version.py +0 -3
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/PKG-INFO +0 -81
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/SOURCES.txt +0 -14
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/dependency_links.txt +0 -1
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/not-zip-safe +0 -1
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/requires.txt +0 -1
- sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/top_level.txt +0 -2
- {sqlalchemy-multiple-db-2.1.1 → sqlalchemy_multiple_db-3.1.0}/LICENSE +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
quality:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.14"
|
|
17
|
+
- uses: astral-sh/setup-uv@v7
|
|
18
|
+
- run: uv sync --all-groups --locked
|
|
19
|
+
- run: uv run ruff format --check src tests
|
|
20
|
+
- run: uv run ruff check src tests
|
|
21
|
+
- run: uv run mypy src
|
|
22
|
+
|
|
23
|
+
test:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: ${{ matrix.python-version }}
|
|
34
|
+
- uses: astral-sh/setup-uv@v7
|
|
35
|
+
- run: uv sync --all-groups --locked
|
|
36
|
+
- run: uv run pytest --cov=sqlalchemy_multiple_db --cov-report=term-missing
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/setup-python@v5
|
|
13
|
+
with:
|
|
14
|
+
python-version: "3.14"
|
|
15
|
+
- uses: astral-sh/setup-uv@v7
|
|
16
|
+
- run: uv sync --all-groups --locked
|
|
17
|
+
- run: uv run ruff format --check src tests
|
|
18
|
+
- run: uv run ruff check src tests
|
|
19
|
+
- run: uv run mypy src
|
|
20
|
+
- run: uv run pytest
|
|
21
|
+
- run: uv build
|
|
22
|
+
- run: uv run python -m zipfile -t dist/*.whl
|
|
23
|
+
- uses: actions/upload-artifact@v4
|
|
24
|
+
with:
|
|
25
|
+
name: python-package-distributions
|
|
26
|
+
path: dist/
|
|
27
|
+
|
|
28
|
+
publish-to-pypi:
|
|
29
|
+
needs: build
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment:
|
|
32
|
+
name: pypi
|
|
33
|
+
url: https://pypi.org/p/sqlalchemy-multiple-db
|
|
34
|
+
permissions:
|
|
35
|
+
id-token: write
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/download-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: python-package-distributions
|
|
40
|
+
path: dist/
|
|
41
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
.idea
|
|
6
|
+
media
|
|
7
|
+
|
|
8
|
+
# C extensions
|
|
9
|
+
*.so
|
|
10
|
+
|
|
11
|
+
# Distribution / packaging
|
|
12
|
+
.Python
|
|
13
|
+
env/
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
configs/local_settings.py
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
|
|
50
|
+
# Translations
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Django stuff:
|
|
54
|
+
*.log
|
|
55
|
+
|
|
56
|
+
# Sphinx documentation
|
|
57
|
+
docs/_build/
|
|
58
|
+
|
|
59
|
+
# PyBuilder
|
|
60
|
+
target/
|
|
61
|
+
|
|
62
|
+
#Ipython Notebook
|
|
63
|
+
.ipynb_checkpoints
|
|
64
|
+
venv/*
|
|
65
|
+
env/*
|
|
66
|
+
.venv/
|
|
67
|
+
.env/*
|
|
68
|
+
.uv/
|
|
69
|
+
.mypy_cache/
|
|
70
|
+
.pytest_cache/
|
|
71
|
+
.ruff_cache/
|
|
72
|
+
|
|
73
|
+
address.txt
|
|
74
|
+
private_keys.txt
|
|
75
|
+
|
|
76
|
+
# Idea modules
|
|
77
|
+
*.iml
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
* Update changelog [Pavel Liashkov]
|
|
6
|
+
|
|
7
|
+
## 3.0.0 (2026-07-20)
|
|
8
|
+
|
|
9
|
+
* feat: modernize SQLAlchemy multi-database library [Pavel Liashkov]
|
|
10
|
+
|
|
11
|
+
## 2.1.1 (2024-10-18)
|
|
12
|
+
|
|
13
|
+
* Update changelog [Pavel Liashkov]
|
|
14
|
+
* Fix url in README [Pavel Liashkov]
|
|
15
|
+
|
|
16
|
+
## 2.1.0 (2024-10-18)
|
|
17
|
+
|
|
18
|
+
* Update pipeline [Pavel Liashkov]
|
|
19
|
+
* Update version [Pavel Liashkov]
|
|
20
|
+
|
|
21
|
+
## 2.0.0 (2023-08-03)
|
|
22
|
+
|
|
23
|
+
* Update sqlalchemy requirement from <1.5,>=1.4 to >=1.4,<2.1 [dependabot[bot]]
|
|
24
|
+
* Fix pipeline (#27) [Pavel Liashkov]
|
|
25
|
+
* Update version [Pavel Liashkov]
|
|
26
|
+
|
|
27
|
+
## 1.8.0 (2021-07-26)
|
|
28
|
+
|
|
29
|
+
* Relax reqs version [Pavel Liashkov]
|
|
30
|
+
|
|
31
|
+
## 1.7.0 (2021-07-23)
|
|
32
|
+
|
|
33
|
+
* Bump sqlalchemy from 1.4.18 to 1.4.19 [dependabot[bot]]
|
|
34
|
+
* Bump sqlalchemy from 1.4.19 to 1.4.20 [dependabot[bot]]
|
|
35
|
+
* Bump sqlalchemy from 1.4.20 to 1.4.22 [dependabot[bot]]
|
|
36
|
+
* Update version.py [Pavel Liashkov]
|
|
37
|
+
|
|
38
|
+
## 1.6.0 (2021-06-18)
|
|
39
|
+
|
|
40
|
+
* Bump sqlalchemy from 1.4.17 to 1.4.18 [dependabot[bot]]
|
|
41
|
+
* Update version.py [Pavel Liashkov]
|
|
42
|
+
|
|
43
|
+
## 1.5.0 (2021-06-04)
|
|
44
|
+
|
|
45
|
+
* Bump sqlalchemy from 1.4.15 to 1.4.17 [dependabot[bot]]
|
|
46
|
+
* Update version.py [Pavel Liashkov]
|
|
47
|
+
|
|
48
|
+
## 1.4.0 (2021-05-18)
|
|
49
|
+
|
|
50
|
+
* Bump sqlalchemy from 1.4.13 to 1.4.15 [dependabot[bot]]
|
|
51
|
+
* Update version.py [Pavel Liashkov]
|
|
52
|
+
|
|
53
|
+
## 1.3.0 (2021-05-05)
|
|
54
|
+
|
|
55
|
+
* Bump sqlalchemy from 1.4.8 to 1.4.13 [dependabot[bot]]
|
|
56
|
+
* Update version.py [Pavel Liashkov]
|
|
57
|
+
|
|
58
|
+
## 1.2.0 (2021-04-16)
|
|
59
|
+
|
|
60
|
+
* Bump sqlalchemy from 1.4.5 to 1.4.8 [dependabot[bot]]
|
|
61
|
+
* Update version.py [Pavel Liashkov]
|
|
62
|
+
|
|
63
|
+
## 1.1.0 (2021-04-05)
|
|
64
|
+
|
|
65
|
+
* Bump sqlalchemy from 1.4.0 to 1.4.2 [dependabot[bot]]
|
|
66
|
+
* Bump sqlalchemy from 1.4.2 to 1.4.5 [dependabot[bot]]
|
|
67
|
+
* Bump version [Pavel Liashkov]
|
|
68
|
+
|
|
69
|
+
## 1.0.0 (2021-03-17)
|
|
70
|
+
|
|
71
|
+
* Add command for checking code [Pavel Liashkov]
|
|
72
|
+
* Bump sqlalchemy from 1.3.23 to 1.4.0 [dependabot[bot]]
|
|
73
|
+
* NOTASK: Bump version [Pavel Liashkov]
|
|
74
|
+
* Bump version [Pavel Liashkov]
|
|
75
|
+
|
|
76
|
+
## 0.1.6 (2021-02-26)
|
|
77
|
+
|
|
78
|
+
* Add codecov settings [Pavel Liashkov]
|
|
79
|
+
|
|
80
|
+
## 0.1.5 (2021-02-20)
|
|
81
|
+
|
|
82
|
+
* Update Makefile [Pavel Liashkov]
|
|
83
|
+
* Bump version [Pavel Liashkov]
|
|
84
|
+
|
|
85
|
+
## 0.1.4 (2021-02-08)
|
|
86
|
+
|
|
87
|
+
* Update readme [Pavel Liashkov]
|
|
88
|
+
|
|
89
|
+
## 0.1.3 (2021-02-02)
|
|
90
|
+
|
|
91
|
+
* Bump sqlalchemy from 1.3.22 to 1.3.23 [dependabot[bot]]
|
|
92
|
+
* Bump version [Pavel Liashkov]
|
|
93
|
+
|
|
94
|
+
## 0.1.2 (2021-01-23)
|
|
95
|
+
|
|
96
|
+
* Fix Readme [Pavel Liashkov]
|
|
97
|
+
* Bump version [Pavel Liashkov]
|
|
98
|
+
* Fix package format [Pavel Liashkov]
|
|
99
|
+
|
|
100
|
+
## 0.1.0 (2021-01-22)
|
|
101
|
+
|
|
102
|
+
* Add base [Pavel Liashkov]
|
|
103
|
+
* Add helper [Pavel Liashkov]
|
|
104
|
+
* Add support for multiple db [Pavel Liashkov]
|
|
105
|
+
* Fix DBConfig [Pavel Liashkov]
|
|
106
|
+
* Purge debug code [Pavel Liashkov]
|
|
107
|
+
* Add helper tests [Pavel Liashkov]
|
|
108
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Project configuration
|
|
3
|
+
# =============================================================================
|
|
4
|
+
PROJECT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
|
5
|
+
PROJECT_NAME := sqlalchemy-multiple-db
|
|
6
|
+
SRC_PATH := src/sqlalchemy_multiple_db
|
|
7
|
+
LINT_PATHS := src tests examples
|
|
8
|
+
UV ?= uv
|
|
9
|
+
|
|
10
|
+
.DEFAULT_GOAL := help
|
|
11
|
+
|
|
12
|
+
.PHONY: help venv/install/main venv/install/all lint/ruff lint/mypy lint format clean test build sys/changelog sys/tag
|
|
13
|
+
|
|
14
|
+
# =============================================================================
|
|
15
|
+
# Help
|
|
16
|
+
# =============================================================================
|
|
17
|
+
help: ## Display this help message
|
|
18
|
+
@awk 'BEGIN { FS = ":.*##"; printf "\nUsage:\n make <target>\n\nTargets:\n" } /^[a-zA-Z0-9_./-]+:.*##/ { printf " \033[36m%-24s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
|
|
19
|
+
|
|
20
|
+
# =============================================================================
|
|
21
|
+
# Environment
|
|
22
|
+
# =============================================================================
|
|
23
|
+
venv/install/main: ## Install main dependencies
|
|
24
|
+
@echo "==> Installing $(PROJECT_NAME) main dependencies"
|
|
25
|
+
$(UV) sync --no-group dev
|
|
26
|
+
|
|
27
|
+
venv/install/all: ## Install all dependency groups
|
|
28
|
+
@echo "==> Installing $(PROJECT_NAME) with all dependency groups"
|
|
29
|
+
$(UV) sync --all-groups
|
|
30
|
+
|
|
31
|
+
# =============================================================================
|
|
32
|
+
# Code quality
|
|
33
|
+
# =============================================================================
|
|
34
|
+
lint/ruff: ## Check formatting and lint with Ruff
|
|
35
|
+
@echo "==> Checking formatting and linting with Ruff"
|
|
36
|
+
$(UV) run --locked ruff format --check $(LINT_PATHS)
|
|
37
|
+
$(UV) run --locked ruff check $(LINT_PATHS)
|
|
38
|
+
|
|
39
|
+
lint/mypy: ## Type-check the source package with mypy
|
|
40
|
+
@echo "==> Type-checking $(SRC_PATH) with mypy"
|
|
41
|
+
$(UV) run --locked mypy $(SRC_PATH)
|
|
42
|
+
|
|
43
|
+
lint: lint/ruff lint/mypy ## Run all lint checks
|
|
44
|
+
@echo "==> Lint checks passed"
|
|
45
|
+
|
|
46
|
+
format: ## Format and auto-fix lint issues
|
|
47
|
+
@echo "==> Formatting and auto-fixing lint issues"
|
|
48
|
+
$(UV) run --locked ruff format $(LINT_PATHS)
|
|
49
|
+
$(UV) run --locked ruff check --fix $(LINT_PATHS)
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# Test and build
|
|
53
|
+
# =============================================================================
|
|
54
|
+
test: ## Run the test suite with coverage
|
|
55
|
+
@echo "==> Testing $(PROJECT_NAME) with coverage"
|
|
56
|
+
$(UV) run --locked pytest --cov=sqlalchemy_multiple_db --cov-report=term-missing
|
|
57
|
+
|
|
58
|
+
build: ## Build distribution packages
|
|
59
|
+
@echo "==> Building $(PROJECT_NAME) distribution packages"
|
|
60
|
+
$(UV) build
|
|
61
|
+
|
|
62
|
+
clean: ## Remove generated files and caches
|
|
63
|
+
@echo "==> Removing generated files and caches"
|
|
64
|
+
rm -rf .coverage .mypy_cache .pytest_cache .ruff_cache build dist
|
|
65
|
+
find . -type d -name __pycache__ -prune -exec rm -rf {} +
|
|
66
|
+
|
|
67
|
+
# =============================================================================
|
|
68
|
+
# Release automation
|
|
69
|
+
# =============================================================================
|
|
70
|
+
# Requires mktemp, available on the project's supported macOS and Linux development environments.
|
|
71
|
+
sys/changelog: ## Generate CHANGELOG.md from historical version tags
|
|
72
|
+
@echo "==> Generating CHANGELOG.md from version tags"
|
|
73
|
+
@tmp_file=$$(mktemp CHANGELOG.md.XXXXXX) || exit $$?; \
|
|
74
|
+
cleanup() { rm -f "$$tmp_file"; }; \
|
|
75
|
+
trap cleanup 0; \
|
|
76
|
+
trap 'exit 1' HUP INT TERM; \
|
|
77
|
+
tags="$$( $(UV) run --locked python -c 'import re, subprocess, sys; pattern = re.compile(r"^v?(\d+)\.(\d+)\.(\d+)$$"); raw = subprocess.check_output(["git", "tag"], text=True).splitlines(); tagged = [(tuple(map(int, match.groups())), tag) for tag in raw if (match := pattern.fullmatch(tag))]; len({version for version, _ in tagged}) == len(tagged) or sys.exit("duplicate semantic-version tags are not supported"); tagged.sort(reverse=True); print(" ".join(tag for _, tag in tagged))' )" || exit $$?; \
|
|
78
|
+
if [ -z "$$tags" ]; then \
|
|
79
|
+
echo "No semantic-version tags found." >&2; \
|
|
80
|
+
exit 1; \
|
|
81
|
+
fi; \
|
|
82
|
+
{ \
|
|
83
|
+
printf '# Changelog\n\n'; \
|
|
84
|
+
set -- $$tags; \
|
|
85
|
+
newest_tag=$$1; \
|
|
86
|
+
unreleased=$$(git log --no-merges "$$newest_tag..HEAD" --format='* %s [%an]' --reverse) || exit $$?; \
|
|
87
|
+
if [ -n "$$unreleased" ]; then \
|
|
88
|
+
printf '## Unreleased\n\n%s\n\n' "$$unreleased"; \
|
|
89
|
+
fi; \
|
|
90
|
+
while [ $$# -gt 0 ]; do \
|
|
91
|
+
current_tag=$$1; \
|
|
92
|
+
shift; \
|
|
93
|
+
older_tag=$${1:-}; \
|
|
94
|
+
tag_date=$$(git log -1 --format=%as "$$current_tag") || exit $$?; \
|
|
95
|
+
display_tag=$${current_tag#v}; \
|
|
96
|
+
printf '## %s (%s)\n\n' "$$display_tag" "$$tag_date"; \
|
|
97
|
+
if [ -n "$$older_tag" ]; then range="$$older_tag..$$current_tag"; else range="$$current_tag"; fi; \
|
|
98
|
+
git log --no-merges "$$range" --format='* %s [%an]' --reverse || exit $$?; \
|
|
99
|
+
printf '\n'; \
|
|
100
|
+
done; \
|
|
101
|
+
} > "$$tmp_file" || exit $$?; \
|
|
102
|
+
if ! mv "$$tmp_file" CHANGELOG.md; then \
|
|
103
|
+
echo "Unable to replace CHANGELOG.md." >&2; \
|
|
104
|
+
exit 1; \
|
|
105
|
+
fi; \
|
|
106
|
+
trap - 0; \
|
|
107
|
+
echo "CHANGELOG.md generated successfully."
|
|
108
|
+
|
|
109
|
+
sys/tag: ## Create and push a vX.Y.Z release tag
|
|
110
|
+
@read -r -p "Enter tag version (e.g., 2.0.0): " TAG; \
|
|
111
|
+
if ! printf '%s\n' "$$TAG" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$$'; then \
|
|
112
|
+
echo "Tag version must use the X.Y.Z format (for example, 2.0.0)" >&2; \
|
|
113
|
+
exit 1; \
|
|
114
|
+
fi; \
|
|
115
|
+
project_version="$$( $(UV) run --locked python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])' )" || exit $$?; \
|
|
116
|
+
if [ "$$project_version" != "$$TAG" ]; then \
|
|
117
|
+
echo "Tag version $$TAG does not match pyproject.toml project.version $$project_version" >&2; \
|
|
118
|
+
exit 1; \
|
|
119
|
+
fi; \
|
|
120
|
+
if [ -n "$$(git status --porcelain --untracked-files=all)" ]; then \
|
|
121
|
+
echo "Worktree must be clean before creating a release tag." >&2; \
|
|
122
|
+
exit 1; \
|
|
123
|
+
fi; \
|
|
124
|
+
if git show-ref --verify --quiet "refs/tags/v$$TAG"; then \
|
|
125
|
+
echo "Local tag v$$TAG already exists." >&2; \
|
|
126
|
+
exit 1; \
|
|
127
|
+
fi; \
|
|
128
|
+
echo "==> Creating and pushing v$$TAG"; \
|
|
129
|
+
git tag -a "v$$TAG" -m "Release v$$TAG" || exit $$?; \
|
|
130
|
+
if ! git push origin "v$$TAG"; then \
|
|
131
|
+
echo "Push failed; deleting local tag v$$TAG." >&2; \
|
|
132
|
+
git tag -d "v$$TAG" || exit $$?; \
|
|
133
|
+
exit 1; \
|
|
134
|
+
fi
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sqlalchemy-multiple-db
|
|
3
|
+
Version: 3.1.0
|
|
4
|
+
Summary: Explicit SQLAlchemy session management for multiple named databases
|
|
5
|
+
Project-URL: Changelog, https://github.com/bigbag/sqlalchemy-multiple-db/blob/main/CHANGELOG.md
|
|
6
|
+
Project-URL: Homepage, https://github.com/bigbag/sqlalchemy-multiple-db
|
|
7
|
+
Project-URL: Issues, https://github.com/bigbag/sqlalchemy-multiple-db/issues
|
|
8
|
+
Author-email: Pavel Liashkov <pavel.liashkov@protonmail.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: database,multi-database,session,sqlalchemy
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
21
|
+
Classifier: Topic :: Database
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: sqlalchemy>=2.0.51
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# sqlalchemy-multiple-db
|
|
28
|
+
|
|
29
|
+
Explicit, typed SQLAlchemy 2.x session management for multiple named databases.
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Python 3.11, 3.12, 3.13, 3.14, or 3.15
|
|
34
|
+
- SQLAlchemy 2.x (installed automatically)
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv add sqlalchemy-multiple-db
|
|
40
|
+
# or
|
|
41
|
+
uv pip install sqlalchemy-multiple-db
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from sqlalchemy import text
|
|
48
|
+
from sqlalchemy_multiple_db import DatabaseConfig, DatabaseManager
|
|
49
|
+
|
|
50
|
+
configs = {
|
|
51
|
+
"primary": DatabaseConfig(url="sqlite:///primary.db"),
|
|
52
|
+
"analytics": DatabaseConfig(url="sqlite:///analytics.db"),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
with DatabaseManager(configs) as databases:
|
|
56
|
+
with databases.session("primary") as session:
|
|
57
|
+
session.execute(text("CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY)"))
|
|
58
|
+
session.execute(text("INSERT INTO events DEFAULT VALUES"))
|
|
59
|
+
|
|
60
|
+
with databases.session("analytics") as session:
|
|
61
|
+
session.execute(text("SELECT 1"))
|
|
62
|
+
|
|
63
|
+
assert databases.healthcheck() == {"primary": True, "analytics": True}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Each `session(name)` call creates a new SQLAlchemy `Session`. A normal block commits; an exception
|
|
67
|
+
rolls back and is re-raised; the session always closes. Call `close()` when not using the manager as a
|
|
68
|
+
context manager. `close()` is idempotent, but the manager cannot be used afterward.
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
`DatabaseConfig(url, engine_options={}, session_options={})` forwards option mappings to SQLAlchemy's
|
|
73
|
+
`create_engine` and `sessionmaker`. Options are copied at construction. The library deliberately does
|
|
74
|
+
not choose pool sizes or deprecated autocommit behavior; configure dialect-specific options explicitly
|
|
75
|
+
when needed.
|
|
76
|
+
|
|
77
|
+
`healthcheck()` runs `SELECT 1` for every configured engine and returns a `{name: bool}` mapping.
|
|
78
|
+
SQLAlchemy connection failures are logged and reported as `False` without stopping checks for other
|
|
79
|
+
databases.
|
|
80
|
+
|
|
81
|
+
## Migrating from 2.x
|
|
82
|
+
|
|
83
|
+
| 2.x | 3.0 |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `DBConfig(dsn="...")` | `DatabaseConfig(url="...")` |
|
|
86
|
+
| `DBHelper` or global `db` | `DatabaseManager(configurations)` |
|
|
87
|
+
| `setup()` | construct the manager |
|
|
88
|
+
| `session_scope()` | `session(name)` |
|
|
89
|
+
| `get_status_info()` | `healthcheck()` |
|
|
90
|
+
| `shutdown()` | `close()` or `with DatabaseManager(...)` |
|
|
91
|
+
|
|
92
|
+
There is no global manager or implicit `default` database in 3.0. Name every database explicitly.
|
|
93
|
+
|
|
94
|
+
## Examples
|
|
95
|
+
|
|
96
|
+
Each example uses temporary SQLite files and needs no external database server:
|
|
97
|
+
|
|
98
|
+
- [basic_usage.py](examples/basic_usage.py) — create, write to, and query isolated primary and analytics databases.
|
|
99
|
+
- [transaction_rollback.py](examples/transaction_rollback.py) — verify that an exception rolls back a transaction.
|
|
100
|
+
- [healthcheck_and_options.py](examples/healthcheck_and_options.py) — configure engine/session options and inspect database health.
|
|
101
|
+
|
|
102
|
+
Run an example from a synced checkout:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uv run python examples/basic_usage.py
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv sync --all-groups
|
|
112
|
+
uv run pytest
|
|
113
|
+
uv run ruff format --check src tests
|
|
114
|
+
uv run ruff check src tests
|
|
115
|
+
uv run mypy src
|
|
116
|
+
uv build
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use `uv add <package>` to add a dependency and `uv lock --upgrade` to refresh all locked dependency
|
|
120
|
+
versions. Commit every `pyproject.toml` and `uv.lock` change together.
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# sqlalchemy-multiple-db
|
|
2
|
+
|
|
3
|
+
Explicit, typed SQLAlchemy 2.x session management for multiple named databases.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Python 3.11, 3.12, 3.13, 3.14, or 3.15
|
|
8
|
+
- SQLAlchemy 2.x (installed automatically)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uv add sqlalchemy-multiple-db
|
|
14
|
+
# or
|
|
15
|
+
uv pip install sqlalchemy-multiple-db
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from sqlalchemy import text
|
|
22
|
+
from sqlalchemy_multiple_db import DatabaseConfig, DatabaseManager
|
|
23
|
+
|
|
24
|
+
configs = {
|
|
25
|
+
"primary": DatabaseConfig(url="sqlite:///primary.db"),
|
|
26
|
+
"analytics": DatabaseConfig(url="sqlite:///analytics.db"),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
with DatabaseManager(configs) as databases:
|
|
30
|
+
with databases.session("primary") as session:
|
|
31
|
+
session.execute(text("CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY)"))
|
|
32
|
+
session.execute(text("INSERT INTO events DEFAULT VALUES"))
|
|
33
|
+
|
|
34
|
+
with databases.session("analytics") as session:
|
|
35
|
+
session.execute(text("SELECT 1"))
|
|
36
|
+
|
|
37
|
+
assert databases.healthcheck() == {"primary": True, "analytics": True}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each `session(name)` call creates a new SQLAlchemy `Session`. A normal block commits; an exception
|
|
41
|
+
rolls back and is re-raised; the session always closes. Call `close()` when not using the manager as a
|
|
42
|
+
context manager. `close()` is idempotent, but the manager cannot be used afterward.
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
`DatabaseConfig(url, engine_options={}, session_options={})` forwards option mappings to SQLAlchemy's
|
|
47
|
+
`create_engine` and `sessionmaker`. Options are copied at construction. The library deliberately does
|
|
48
|
+
not choose pool sizes or deprecated autocommit behavior; configure dialect-specific options explicitly
|
|
49
|
+
when needed.
|
|
50
|
+
|
|
51
|
+
`healthcheck()` runs `SELECT 1` for every configured engine and returns a `{name: bool}` mapping.
|
|
52
|
+
SQLAlchemy connection failures are logged and reported as `False` without stopping checks for other
|
|
53
|
+
databases.
|
|
54
|
+
|
|
55
|
+
## Migrating from 2.x
|
|
56
|
+
|
|
57
|
+
| 2.x | 3.0 |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `DBConfig(dsn="...")` | `DatabaseConfig(url="...")` |
|
|
60
|
+
| `DBHelper` or global `db` | `DatabaseManager(configurations)` |
|
|
61
|
+
| `setup()` | construct the manager |
|
|
62
|
+
| `session_scope()` | `session(name)` |
|
|
63
|
+
| `get_status_info()` | `healthcheck()` |
|
|
64
|
+
| `shutdown()` | `close()` or `with DatabaseManager(...)` |
|
|
65
|
+
|
|
66
|
+
There is no global manager or implicit `default` database in 3.0. Name every database explicitly.
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
Each example uses temporary SQLite files and needs no external database server:
|
|
71
|
+
|
|
72
|
+
- [basic_usage.py](examples/basic_usage.py) — create, write to, and query isolated primary and analytics databases.
|
|
73
|
+
- [transaction_rollback.py](examples/transaction_rollback.py) — verify that an exception rolls back a transaction.
|
|
74
|
+
- [healthcheck_and_options.py](examples/healthcheck_and_options.py) — configure engine/session options and inspect database health.
|
|
75
|
+
|
|
76
|
+
Run an example from a synced checkout:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv run python examples/basic_usage.py
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv sync --all-groups
|
|
86
|
+
uv run pytest
|
|
87
|
+
uv run ruff format --check src tests
|
|
88
|
+
uv run ruff check src tests
|
|
89
|
+
uv run mypy src
|
|
90
|
+
uv build
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Use `uv add <package>` to add a dependency and `uv lock --upgrade` to refresh all locked dependency
|
|
94
|
+
versions. Commit every `pyproject.toml` and `uv.lock` change together.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Create and query two isolated named SQLite databases."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tempfile import TemporaryDirectory
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import text
|
|
7
|
+
|
|
8
|
+
from sqlalchemy_multiple_db import DatabaseConfig, DatabaseManager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> None:
|
|
12
|
+
with TemporaryDirectory(prefix="sqlalchemy-multiple-db-") as directory:
|
|
13
|
+
database_directory = Path(directory)
|
|
14
|
+
configurations = {
|
|
15
|
+
"primary": DatabaseConfig(url=f"sqlite:///{database_directory / 'primary.db'}"),
|
|
16
|
+
"analytics": DatabaseConfig(url=f"sqlite:///{database_directory / 'analytics.db'}"),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
with DatabaseManager(configurations) as databases:
|
|
20
|
+
with databases.session("primary") as session:
|
|
21
|
+
session.execute(text("CREATE TABLE events (name TEXT NOT NULL)"))
|
|
22
|
+
session.execute(text("INSERT INTO events (name) VALUES ('deployment')"))
|
|
23
|
+
|
|
24
|
+
with databases.session("analytics") as session:
|
|
25
|
+
session.execute(text("CREATE TABLE reports (name TEXT NOT NULL)"))
|
|
26
|
+
session.execute(text("INSERT INTO reports (name) VALUES ('daily summary')"))
|
|
27
|
+
|
|
28
|
+
with databases.session("primary") as session:
|
|
29
|
+
primary_event = session.scalar(text("SELECT name FROM events"))
|
|
30
|
+
with databases.session("analytics") as session:
|
|
31
|
+
analytics_report = session.scalar(text("SELECT name FROM reports"))
|
|
32
|
+
|
|
33
|
+
print(f"Primary event: {primary_event}")
|
|
34
|
+
print(f"Analytics report: {analytics_report}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|