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.
Files changed (38) hide show
  1. sqlalchemy_multiple_db-3.1.0/.github/SECURITY.md +8 -0
  2. sqlalchemy_multiple_db-3.1.0/.github/dependabot.yml +10 -0
  3. sqlalchemy_multiple_db-3.1.0/.github/workflows/ci.yml +36 -0
  4. sqlalchemy_multiple_db-3.1.0/.github/workflows/publish.yml +41 -0
  5. sqlalchemy_multiple_db-3.1.0/.gitignore +77 -0
  6. sqlalchemy_multiple_db-3.1.0/CHANGELOG.md +108 -0
  7. sqlalchemy_multiple_db-3.1.0/Makefile +134 -0
  8. sqlalchemy_multiple_db-3.1.0/PKG-INFO +124 -0
  9. sqlalchemy_multiple_db-3.1.0/README.md +98 -0
  10. sqlalchemy_multiple_db-3.1.0/examples/basic_usage.py +38 -0
  11. sqlalchemy_multiple_db-3.1.0/examples/healthcheck_and_options.py +39 -0
  12. sqlalchemy_multiple_db-3.1.0/examples/transaction_rollback.py +35 -0
  13. sqlalchemy_multiple_db-3.1.0/pyproject.toml +62 -0
  14. sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/__init__.py +14 -0
  15. sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/config.py +28 -0
  16. sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/exceptions.py +6 -0
  17. sqlalchemy_multiple_db-3.1.0/src/sqlalchemy_multiple_db/manager.py +110 -0
  18. sqlalchemy_multiple_db-3.1.0/tests/test_config.py +44 -0
  19. sqlalchemy_multiple_db-3.1.0/tests/test_examples.py +29 -0
  20. sqlalchemy_multiple_db-3.1.0/tests/test_manager.py +144 -0
  21. sqlalchemy_multiple_db-3.1.0/tests/test_public_api.py +15 -0
  22. sqlalchemy_multiple_db-3.1.0/uv.lock +626 -0
  23. sqlalchemy-multiple-db-2.1.1/MANIFEST.in +0 -6
  24. sqlalchemy-multiple-db-2.1.1/PKG-INFO +0 -81
  25. sqlalchemy-multiple-db-2.1.1/README.md +0 -51
  26. sqlalchemy-multiple-db-2.1.1/pyproject.toml +0 -18
  27. sqlalchemy-multiple-db-2.1.1/setup.cfg +0 -4
  28. sqlalchemy-multiple-db-2.1.1/setup.py +0 -54
  29. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db/__init__.py +0 -3
  30. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db/helper.py +0 -107
  31. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db/version.py +0 -3
  32. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/PKG-INFO +0 -81
  33. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/SOURCES.txt +0 -14
  34. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/dependency_links.txt +0 -1
  35. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/not-zip-safe +0 -1
  36. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/requires.txt +0 -1
  37. sqlalchemy-multiple-db-2.1.1/sqlalchemy_multiple_db.egg-info/top_level.txt +0 -2
  38. {sqlalchemy-multiple-db-2.1.1 → sqlalchemy_multiple_db-3.1.0}/LICENSE +0 -0
@@ -0,0 +1,8 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ We will endeavour to support:
6
+ * the most recent minor release with bug fixes
7
+ * the latest minor release from the last major version
8
+ for 6 months after a new major version is released with critical bug fixes
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: uv
4
+ directory: /
5
+ schedule:
6
+ interval: monthly
7
+ - package-ecosystem: github-actions
8
+ directory: /
9
+ schedule:
10
+ interval: monthly
@@ -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()