pgdbm 0.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.
- pgdbm-0.1.0/.claude/settings.local.json +27 -0
- pgdbm-0.1.0/.gitattributes +60 -0
- pgdbm-0.1.0/.gitignore +50 -0
- pgdbm-0.1.0/.pre-commit-config.yaml +52 -0
- pgdbm-0.1.0/.python-version +1 -0
- pgdbm-0.1.0/CHANGELOG.md +52 -0
- pgdbm-0.1.0/LICENSE +21 -0
- pgdbm-0.1.0/PKG-INFO +288 -0
- pgdbm-0.1.0/README.md +256 -0
- pgdbm-0.1.0/docs/api-reference.md +918 -0
- pgdbm-0.1.0/docs/migrations.md +567 -0
- pgdbm-0.1.0/docs/patterns.md +539 -0
- pgdbm-0.1.0/docs/quickstart.md +255 -0
- pgdbm-0.1.0/docs/testing.md +407 -0
- pgdbm-0.1.0/examples/README.md +49 -0
- pgdbm-0.1.0/examples/microservices/Dockerfile +22 -0
- pgdbm-0.1.0/examples/microservices/README.md +294 -0
- pgdbm-0.1.0/examples/microservices/docker-compose.yml +95 -0
- pgdbm-0.1.0/examples/microservices/gateway/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/gateway/main.py +134 -0
- pgdbm-0.1.0/examples/microservices/gateway/routes.py +250 -0
- pgdbm-0.1.0/examples/microservices/migrations/001_shared_tables.sql +40 -0
- pgdbm-0.1.0/examples/microservices/requirements.txt +21 -0
- pgdbm-0.1.0/examples/microservices/services/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/services/inventory/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/services/inventory/api.py +185 -0
- pgdbm-0.1.0/examples/microservices/services/inventory/db.py +260 -0
- pgdbm-0.1.0/examples/microservices/services/inventory/handlers.py +49 -0
- pgdbm-0.1.0/examples/microservices/services/inventory/main.py +115 -0
- pgdbm-0.1.0/examples/microservices/services/inventory/migrations/001_inventory_schema.sql +51 -0
- pgdbm-0.1.0/examples/microservices/services/notification/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/services/notification/handlers.py +129 -0
- pgdbm-0.1.0/examples/microservices/services/notification/main.py +107 -0
- pgdbm-0.1.0/examples/microservices/services/orders/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/services/orders/api.py +220 -0
- pgdbm-0.1.0/examples/microservices/services/orders/db.py +219 -0
- pgdbm-0.1.0/examples/microservices/services/orders/handlers.py +42 -0
- pgdbm-0.1.0/examples/microservices/services/orders/main.py +115 -0
- pgdbm-0.1.0/examples/microservices/services/orders/migrations/001_orders_schema.sql +54 -0
- pgdbm-0.1.0/examples/microservices/services/users/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/services/users/api.py +186 -0
- pgdbm-0.1.0/examples/microservices/services/users/db.py +136 -0
- pgdbm-0.1.0/examples/microservices/services/users/main.py +113 -0
- pgdbm-0.1.0/examples/microservices/services/users/migrations/001_users_schema.sql +30 -0
- pgdbm-0.1.0/examples/microservices/shared/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/shared/database.py +226 -0
- pgdbm-0.1.0/examples/microservices/shared/events.py +194 -0
- pgdbm-0.1.0/examples/microservices/shared/models.py +194 -0
- pgdbm-0.1.0/examples/microservices/shared/resilience.py +272 -0
- pgdbm-0.1.0/examples/microservices/tests/__init__.py +1 -0
- pgdbm-0.1.0/examples/microservices/tests/conftest.py +60 -0
- pgdbm-0.1.0/examples/microservices/tests/test_shared_pool.py +162 -0
- pgdbm-0.1.0/examples/saas-app/.gitignore +30 -0
- pgdbm-0.1.0/examples/saas-app/README.md +186 -0
- pgdbm-0.1.0/examples/saas-app/app/__init__.py +1 -0
- pgdbm-0.1.0/examples/saas-app/app/api/__init__.py +1 -0
- pgdbm-0.1.0/examples/saas-app/app/api/admin.py +212 -0
- pgdbm-0.1.0/examples/saas-app/app/api/auth.py +126 -0
- pgdbm-0.1.0/examples/saas-app/app/api/projects.py +199 -0
- pgdbm-0.1.0/examples/saas-app/app/api/tenants.py +202 -0
- pgdbm-0.1.0/examples/saas-app/app/config.py +80 -0
- pgdbm-0.1.0/examples/saas-app/app/db/__init__.py +1 -0
- pgdbm-0.1.0/examples/saas-app/app/db/__main__.py +117 -0
- pgdbm-0.1.0/examples/saas-app/app/db/admin.py +414 -0
- pgdbm-0.1.0/examples/saas-app/app/db/base.py +72 -0
- pgdbm-0.1.0/examples/saas-app/app/db/tenant.py +308 -0
- pgdbm-0.1.0/examples/saas-app/app/main.py +122 -0
- pgdbm-0.1.0/examples/saas-app/app/middleware/__init__.py +1 -0
- pgdbm-0.1.0/examples/saas-app/app/middleware/tenant.py +66 -0
- pgdbm-0.1.0/examples/saas-app/app/models/__init__.py +1 -0
- pgdbm-0.1.0/examples/saas-app/app/models/project.py +120 -0
- pgdbm-0.1.0/examples/saas-app/app/models/tenant.py +103 -0
- pgdbm-0.1.0/examples/saas-app/app/models/user.py +46 -0
- pgdbm-0.1.0/examples/saas-app/migrations/001_unified_schema.sql +226 -0
- pgdbm-0.1.0/examples/saas-app/requirements.txt +16 -0
- pgdbm-0.1.0/examples/saas-app/tests/__init__.py +1 -0
- pgdbm-0.1.0/examples/saas-app/tests/conftest.py +130 -0
- pgdbm-0.1.0/examples/saas-app/tests/test_auth.py +115 -0
- pgdbm-0.1.0/examples/saas-app/tests/test_projects.py +160 -0
- pgdbm-0.1.0/examples/saas-app/tests/test_tenant.py +99 -0
- pgdbm-0.1.0/examples/todo-app/README.md +140 -0
- pgdbm-0.1.0/examples/todo-app/app/__init__.py +1 -0
- pgdbm-0.1.0/examples/todo-app/app/api/__init__.py +1 -0
- pgdbm-0.1.0/examples/todo-app/app/api/health.py +26 -0
- pgdbm-0.1.0/examples/todo-app/app/api/todos.py +100 -0
- pgdbm-0.1.0/examples/todo-app/app/config.py +64 -0
- pgdbm-0.1.0/examples/todo-app/app/db.py +256 -0
- pgdbm-0.1.0/examples/todo-app/app/main.py +72 -0
- pgdbm-0.1.0/examples/todo-app/app/models.py +67 -0
- pgdbm-0.1.0/examples/todo-app/migrations/001_initial_schema.sql +37 -0
- pgdbm-0.1.0/examples/todo-app/requirements.txt +13 -0
- pgdbm-0.1.0/examples/todo-app/tests/__init__.py +1 -0
- pgdbm-0.1.0/examples/todo-app/tests/conftest.py +77 -0
- pgdbm-0.1.0/examples/todo-app/tests/test_api.py +178 -0
- pgdbm-0.1.0/examples/todo-app/tests/test_db.py +163 -0
- pgdbm-0.1.0/pyproject.toml +167 -0
- pgdbm-0.1.0/src/pgdbm/__init__.py +71 -0
- pgdbm-0.1.0/src/pgdbm/__version__.py +9 -0
- pgdbm-0.1.0/src/pgdbm/core.py +757 -0
- pgdbm-0.1.0/src/pgdbm/errors.py +140 -0
- pgdbm-0.1.0/src/pgdbm/fixtures/__init__.py +14 -0
- pgdbm-0.1.0/src/pgdbm/fixtures/conftest.py +289 -0
- pgdbm-0.1.0/src/pgdbm/migrations.py +560 -0
- pgdbm-0.1.0/src/pgdbm/monitoring.py +390 -0
- pgdbm-0.1.0/src/pgdbm/py.typed +0 -0
- pgdbm-0.1.0/src/pgdbm/testing.py +415 -0
- pgdbm-0.1.0/tests/conftest.py +83 -0
- pgdbm-0.1.0/tests/integration/test_basic_usage.py +456 -0
- pgdbm-0.1.0/tests/integration/test_migrations.py +419 -0
- pgdbm-0.1.0/tests/integration/test_monitoring.py +369 -0
- pgdbm-0.1.0/tests/integration/test_multi_tenancy.py +421 -0
- pgdbm-0.1.0/tests/integration/test_testing_utilities.py +417 -0
- pgdbm-0.1.0/tests/test_core.py +292 -0
- pgdbm-0.1.0/tests/test_migrations_unit.py +294 -0
- pgdbm-0.1.0/tests/test_security.py +267 -0
- pgdbm-0.1.0/tests/test_shared_pool.py +354 -0
- pgdbm-0.1.0/uv.lock +1253 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(python:*)",
|
|
5
|
+
"Bash(ls:*)",
|
|
6
|
+
"Bash(grep:*)",
|
|
7
|
+
"Bash(find:*)",
|
|
8
|
+
"Bash(source:*)",
|
|
9
|
+
"Bash(psql:*)",
|
|
10
|
+
"Bash(timeout:*)",
|
|
11
|
+
"Bash(pre-commit run:*)",
|
|
12
|
+
"Bash(ruff check:*)",
|
|
13
|
+
"Bash(mypy:*)",
|
|
14
|
+
"Bash(uv run mypy:*)",
|
|
15
|
+
"Bash(uv run pre-commit run:*)",
|
|
16
|
+
"Bash(git add:*)",
|
|
17
|
+
"Bash(git reset:*)",
|
|
18
|
+
"Bash(uv run ruff:*)",
|
|
19
|
+
"Bash(uv run isort:*)",
|
|
20
|
+
"Bash(uv run bandit:*)",
|
|
21
|
+
"Bash(uv run pytest:*)",
|
|
22
|
+
"Bash(git commit:*)",
|
|
23
|
+
"Bash(rm:*)"
|
|
24
|
+
],
|
|
25
|
+
"deny": []
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Auto detect text files and perform LF normalization
|
|
2
|
+
* text=auto
|
|
3
|
+
|
|
4
|
+
# Python files
|
|
5
|
+
*.py text
|
|
6
|
+
*.pyw text
|
|
7
|
+
*.pyx text
|
|
8
|
+
*.pyi text
|
|
9
|
+
|
|
10
|
+
# Documentation
|
|
11
|
+
*.md text
|
|
12
|
+
*.rst text
|
|
13
|
+
*.txt text
|
|
14
|
+
LICENSE text
|
|
15
|
+
AUTHORS text
|
|
16
|
+
CHANGELOG text
|
|
17
|
+
CONTRIBUTING text
|
|
18
|
+
README text
|
|
19
|
+
|
|
20
|
+
# Config files
|
|
21
|
+
*.json text
|
|
22
|
+
*.yaml text
|
|
23
|
+
*.yml text
|
|
24
|
+
*.toml text
|
|
25
|
+
*.ini text
|
|
26
|
+
*.cfg text
|
|
27
|
+
.gitignore text
|
|
28
|
+
.gitattributes text
|
|
29
|
+
|
|
30
|
+
# Shell scripts
|
|
31
|
+
*.sh text eol=lf
|
|
32
|
+
*.bash text eol=lf
|
|
33
|
+
|
|
34
|
+
# Windows scripts
|
|
35
|
+
*.bat text eol=crlf
|
|
36
|
+
*.cmd text eol=crlf
|
|
37
|
+
*.ps1 text eol=crlf
|
|
38
|
+
|
|
39
|
+
# Binary files
|
|
40
|
+
*.db binary
|
|
41
|
+
*.sqlite binary
|
|
42
|
+
*.sqlite3 binary
|
|
43
|
+
*.pyc binary
|
|
44
|
+
*.pyo binary
|
|
45
|
+
*.pyd binary
|
|
46
|
+
*.so binary
|
|
47
|
+
*.dylib binary
|
|
48
|
+
*.dll binary
|
|
49
|
+
*.exe binary
|
|
50
|
+
*.zip binary
|
|
51
|
+
*.tar binary
|
|
52
|
+
*.gz binary
|
|
53
|
+
*.bz2 binary
|
|
54
|
+
*.7z binary
|
|
55
|
+
*.png binary
|
|
56
|
+
*.jpg binary
|
|
57
|
+
*.jpeg binary
|
|
58
|
+
*.gif binary
|
|
59
|
+
*.ico binary
|
|
60
|
+
*.pdf binary
|
pgdbm-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Environment variables
|
|
2
|
+
.env*
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
*.so
|
|
9
|
+
.Python
|
|
10
|
+
build/
|
|
11
|
+
develop-eggs/
|
|
12
|
+
dist/
|
|
13
|
+
downloads/
|
|
14
|
+
eggs/
|
|
15
|
+
.eggs/
|
|
16
|
+
lib/
|
|
17
|
+
lib64/
|
|
18
|
+
parts/
|
|
19
|
+
sdist/
|
|
20
|
+
var/
|
|
21
|
+
wheels/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.coverage
|
|
27
|
+
htmlcov/
|
|
28
|
+
.tox/
|
|
29
|
+
.venv/
|
|
30
|
+
venv/
|
|
31
|
+
|
|
32
|
+
# Emacs
|
|
33
|
+
*~
|
|
34
|
+
\#*\#
|
|
35
|
+
/.emacs.desktop
|
|
36
|
+
/.emacs.desktop.lock
|
|
37
|
+
.emacs-bu/
|
|
38
|
+
*.elc
|
|
39
|
+
auto-save-list
|
|
40
|
+
tramp
|
|
41
|
+
.\#*
|
|
42
|
+
.dir-locals.el
|
|
43
|
+
.projectile
|
|
44
|
+
|
|
45
|
+
# OS
|
|
46
|
+
.DS_Store
|
|
47
|
+
Thumbs.db
|
|
48
|
+
|
|
49
|
+
Makefile
|
|
50
|
+
tmp
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# See https://pre-commit.com for more information
|
|
2
|
+
# See https://pre-commit.com/hooks.html for more hooks
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
5
|
+
rev: v5.0.0
|
|
6
|
+
hooks:
|
|
7
|
+
- id: trailing-whitespace
|
|
8
|
+
- id: end-of-file-fixer
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
- id: check-json
|
|
12
|
+
- id: check-toml
|
|
13
|
+
- id: check-merge-conflict
|
|
14
|
+
- id: debug-statements
|
|
15
|
+
- id: detect-private-key
|
|
16
|
+
- id: mixed-line-ending
|
|
17
|
+
args: ['--fix=lf']
|
|
18
|
+
- id: name-tests-test
|
|
19
|
+
args: ['--pytest-test-first']
|
|
20
|
+
|
|
21
|
+
- repo: https://github.com/psf/black
|
|
22
|
+
rev: 25.1.0
|
|
23
|
+
hooks:
|
|
24
|
+
- id: black
|
|
25
|
+
language_version: python3
|
|
26
|
+
args: ['--config=pyproject.toml']
|
|
27
|
+
|
|
28
|
+
- repo: https://github.com/pycqa/isort
|
|
29
|
+
rev: 6.0.1
|
|
30
|
+
hooks:
|
|
31
|
+
- id: isort
|
|
32
|
+
args: ['--settings-path=pyproject.toml']
|
|
33
|
+
|
|
34
|
+
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
|
35
|
+
rev: v0.12.5
|
|
36
|
+
hooks:
|
|
37
|
+
- id: ruff
|
|
38
|
+
args: ['--fix', '--exit-non-zero-on-fix']
|
|
39
|
+
|
|
40
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
41
|
+
rev: v1.17.0
|
|
42
|
+
hooks:
|
|
43
|
+
- id: mypy
|
|
44
|
+
additional_dependencies: [asyncpg-stubs, pydantic, pytest, pytest-asyncio]
|
|
45
|
+
args: ['--config-file=pyproject.toml', 'src/']
|
|
46
|
+
files: '^src/'
|
|
47
|
+
pass_filenames: false
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
ci:
|
|
51
|
+
autoupdate_schedule: weekly
|
|
52
|
+
skip: [mypy] # mypy requires installed dependencies
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
pgdbm-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to pgdbm will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- TLS/SSL support in `DatabaseConfig` with `ssl_enabled`, `ssl_mode`, CA/cert/key options
|
|
12
|
+
- Server-side timeouts in `DatabaseConfig` (`statement_timeout_ms`, `idle_in_transaction_session_timeout_ms`, `lock_timeout_ms`)
|
|
13
|
+
- Advisory locking in migrations to serialize runners per `module_name`
|
|
14
|
+
- Migration version extraction from filenames
|
|
15
|
+
- Supports numeric prefix (001_), Flyway style (V1__), and timestamp patterns
|
|
16
|
+
- Automatic version property on Migration model
|
|
17
|
+
- Better ordering and conflict prevention
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Replace generic exceptions with custom error types throughout codebase
|
|
21
|
+
- ConfigurationError, PoolError, QueryError, MigrationError, etc.
|
|
22
|
+
- Enhanced error messages with troubleshooting tips
|
|
23
|
+
- Better debugging experience
|
|
24
|
+
- `execute_and_return_id` now correctly detects existing RETURNING clauses to avoid duplication
|
|
25
|
+
- **BREAKING**: Minimum Python version raised to 3.9
|
|
26
|
+
- Python 3.8 reached EOL in October 2024
|
|
27
|
+
- Allows use of modern type annotations and features
|
|
28
|
+
|
|
29
|
+
## [0.1.0] - 2025-01-26
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- Initial public release
|
|
33
|
+
- Core async database management with connection pooling
|
|
34
|
+
- Schema-based multi-tenancy support
|
|
35
|
+
- Built-in migration system
|
|
36
|
+
- Comprehensive testing utilities
|
|
37
|
+
- Connection monitoring and debugging tools
|
|
38
|
+
- Shared connection pool support for microservices
|
|
39
|
+
- Full type hints and py.typed support
|
|
40
|
+
- Pytest fixtures for easy testing
|
|
41
|
+
- Production-ready patterns out of the box
|
|
42
|
+
|
|
43
|
+
### Features
|
|
44
|
+
- **AsyncDatabaseManager**: Main database interface with connection pooling
|
|
45
|
+
- **DatabaseConfig**: Pydantic-based configuration management
|
|
46
|
+
- **AsyncMigrationManager**: Database migration tracking and execution
|
|
47
|
+
- **MonitoredAsyncDatabaseManager**: Performance monitoring capabilities
|
|
48
|
+
- **Testing utilities**: Automatic test database creation and cleanup
|
|
49
|
+
- **Schema isolation**: Multi-tenant support with `{{tables.name}}` templating
|
|
50
|
+
|
|
51
|
+
[Unreleased]: https://github.com/juanreyero/pgdbm/compare/v0.1.0...HEAD
|
|
52
|
+
[0.1.0]: https://github.com/juanreyero/pgdbm/releases/tag/v0.1.0
|
pgdbm-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Juan Reyero
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pgdbm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pgdbm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Production-ready async PostgreSQL utilities with connection pooling, migrations, and testing support
|
|
5
|
+
Project-URL: Homepage, https://juanreyero.com/open/pgdbm
|
|
6
|
+
Project-URL: Documentation, https://github.com/juanre/pgdbm
|
|
7
|
+
Project-URL: Repository, https://github.com/juanre/pgdbm
|
|
8
|
+
Project-URL: Issues, https://github.com/juanre/pgdbm/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/juanre/pgdbm/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Juan Reyero <juan@juanreyero.com>
|
|
11
|
+
Maintainer-email: Juan Reyero <juan@juanreyero.com>
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: async,asyncio,asyncpg,database,migrations,postgresql,testing
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Framework :: AsyncIO
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Database
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Requires-Dist: asyncpg>=0.28.0
|
|
30
|
+
Requires-Dist: pydantic>=2.0.0
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# pgdbm
|
|
34
|
+
|
|
35
|
+
[](https://opensource.org/licenses/MIT)
|
|
36
|
+
[](https://pypi.org/project/pgdbm/)
|
|
37
|
+
[](https://pypi.org/project/pgdbm/)
|
|
38
|
+
|
|
39
|
+
A PostgreSQL library for Python that provides high-level async database operations with built-in migration management, connection pooling, and testing utilities. It offers:
|
|
40
|
+
|
|
41
|
+
- **Connection pooling** with proper resource management
|
|
42
|
+
- **Schema migrations** that are version-controlled and automated
|
|
43
|
+
- **Testing utilities** that provide isolated test databases
|
|
44
|
+
- **Module isolation** when multiple services share a database
|
|
45
|
+
- **Monitoring** for slow queries and connection issues
|
|
46
|
+
|
|
47
|
+
## Key Features
|
|
48
|
+
|
|
49
|
+
- **🚀 High Performance** - Built on asyncpg, the fastest PostgreSQL driver for Python
|
|
50
|
+
- **📦 Migration System** - Version-controlled schema migrations with automatic ordering
|
|
51
|
+
- **🧪 Testing Support** - Fixtures and utilities for database testing
|
|
52
|
+
- **🔧 Module Isolation** - Prevent table conflicts when modules share databases
|
|
53
|
+
- **📊 Monitoring** - Track slow queries and connection pool metrics
|
|
54
|
+
- **🔒 Type Safe** - Full type hints and Pydantic integration
|
|
55
|
+
|
|
56
|
+
### Design intent: dual ownership
|
|
57
|
+
|
|
58
|
+
pgdbm is designed so a module can either:
|
|
59
|
+
- **Own its database** when run independently (it creates a pool and runs its own migrations), or
|
|
60
|
+
- **Use a database owned by another** component via a shared pool while still running its own migrations into a schema namespace.
|
|
61
|
+
|
|
62
|
+
This keeps modules portable: the same code can run as a standalone service or as part of a larger app.
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Install using uv (recommended)
|
|
68
|
+
uv add pgdbm
|
|
69
|
+
|
|
70
|
+
# Or using pip
|
|
71
|
+
pip install pgdbm
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Quick Start
|
|
75
|
+
|
|
76
|
+
### 1. Create a migration file
|
|
77
|
+
|
|
78
|
+
```sql
|
|
79
|
+
-- migrations/001_initial.sql
|
|
80
|
+
CREATE TABLE IF NOT EXISTS {{tables.users}} (
|
|
81
|
+
id SERIAL PRIMARY KEY,
|
|
82
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
83
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Use pgdbm
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from pgdbm import AsyncDatabaseManager, DatabaseConfig, AsyncMigrationManager
|
|
91
|
+
|
|
92
|
+
# Configure and connect
|
|
93
|
+
config = DatabaseConfig(
|
|
94
|
+
host="localhost",
|
|
95
|
+
database="myapp",
|
|
96
|
+
user="postgres",
|
|
97
|
+
password="secret"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
db = AsyncDatabaseManager(config)
|
|
101
|
+
await db.connect()
|
|
102
|
+
|
|
103
|
+
# Apply migrations
|
|
104
|
+
migrations = AsyncMigrationManager(db, migrations_path="./migrations")
|
|
105
|
+
await migrations.apply_pending()
|
|
106
|
+
|
|
107
|
+
# Use your database
|
|
108
|
+
user_id = await db.execute_and_return_id(
|
|
109
|
+
"INSERT INTO {{tables.users}} (email) VALUES ($1)",
|
|
110
|
+
"user@example.com"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Clean up
|
|
114
|
+
await db.disconnect()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Standalone vs shared-pool usage:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# Standalone (owns the DB):
|
|
121
|
+
config = DatabaseConfig(connection_string="postgresql://localhost/app", schema="users")
|
|
122
|
+
db = AsyncDatabaseManager(config)
|
|
123
|
+
await db.connect()
|
|
124
|
+
await AsyncMigrationManager(db, "./migrations", module_name="users").apply_pending()
|
|
125
|
+
|
|
126
|
+
# Shared pool (uses external DB):
|
|
127
|
+
shared = await AsyncDatabaseManager.create_shared_pool(DatabaseConfig(connection_string="postgresql://localhost/app"))
|
|
128
|
+
db = AsyncDatabaseManager(pool=shared, schema="users") # Module still runs its own migrations
|
|
129
|
+
await AsyncMigrationManager(db, "./migrations", module_name="users").apply_pending()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Core Patterns
|
|
133
|
+
|
|
134
|
+
### 1. Migration Management
|
|
135
|
+
|
|
136
|
+
pgdbm includes a built-in migration system:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# Apply all pending migrations
|
|
140
|
+
migrations = AsyncMigrationManager(db, migrations_path="./migrations")
|
|
141
|
+
result = await migrations.apply_pending()
|
|
142
|
+
|
|
143
|
+
# Check what was applied
|
|
144
|
+
for migration in result['applied']:
|
|
145
|
+
print(f"Applied {migration['filename']} in {migration['execution_time_ms']}ms")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Migrations are automatically ordered by version number extracted from filenames.
|
|
149
|
+
|
|
150
|
+
### 2. Module Isolation
|
|
151
|
+
|
|
152
|
+
When multiple modules share a database, use schemas to prevent table conflicts:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# Each module can get its own schema
|
|
156
|
+
user_db = AsyncDatabaseManager(
|
|
157
|
+
config=DatabaseConfig(database="app", schema="user_module")
|
|
158
|
+
)
|
|
159
|
+
blog_db = AsyncDatabaseManager(
|
|
160
|
+
config=DatabaseConfig(database="app", schema="blog_module")
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Both can have a "users" table without conflict:
|
|
164
|
+
# - user_module.users
|
|
165
|
+
# - blog_module.users
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The `{{tables.tablename}}` syntax in queries automatically expands to the correct schema-qualified name.
|
|
169
|
+
|
|
170
|
+
### 3. Connection Pool Sharing
|
|
171
|
+
|
|
172
|
+
For applications with multiple services sharing a database:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
# Create shared pool
|
|
176
|
+
shared_pool = await AsyncDatabaseManager.create_shared_pool(config)
|
|
177
|
+
|
|
178
|
+
# Each service gets its own schema but shares connections
|
|
179
|
+
user_db = AsyncDatabaseManager(pool=shared_pool, schema="users")
|
|
180
|
+
billing_db = AsyncDatabaseManager(pool=shared_pool, schema="billing")
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 4. Testing Support
|
|
184
|
+
|
|
185
|
+
Built-in fixtures for database tests:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
# conftest.py
|
|
189
|
+
from pgdbm.fixtures.conftest import *
|
|
190
|
+
|
|
191
|
+
# test_users.py
|
|
192
|
+
async def test_create_user(test_db):
|
|
193
|
+
# Automatic test database with cleanup
|
|
194
|
+
await test_db.execute("""
|
|
195
|
+
CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT)
|
|
196
|
+
""")
|
|
197
|
+
|
|
198
|
+
user_id = await test_db.execute_and_return_id(
|
|
199
|
+
"INSERT INTO users (email) VALUES ($1)",
|
|
200
|
+
"test@example.com"
|
|
201
|
+
)
|
|
202
|
+
assert user_id == 1
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 5. Monitoring
|
|
206
|
+
|
|
207
|
+
Track database performance:
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from pgdbm import MonitoredAsyncDatabaseManager
|
|
211
|
+
|
|
212
|
+
db = MonitoredAsyncDatabaseManager(
|
|
213
|
+
config=config,
|
|
214
|
+
slow_query_threshold_ms=100 # Log queries over 100ms
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Get metrics
|
|
218
|
+
metrics = await db.get_query_metrics()
|
|
219
|
+
slow_queries = await db.get_slow_queries(limit=10)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 6. Production TLS and Timeouts
|
|
223
|
+
|
|
224
|
+
Enable TLS with certificate verification and enforce server-side timeouts:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
from pgdbm import AsyncDatabaseManager, DatabaseConfig
|
|
228
|
+
|
|
229
|
+
config = DatabaseConfig(
|
|
230
|
+
connection_string="postgresql://db.example.com/app",
|
|
231
|
+
ssl_enabled=True,
|
|
232
|
+
ssl_mode="verify-full", # 'require' | 'verify-ca' | 'verify-full'
|
|
233
|
+
ssl_ca_file="/etc/ssl/certs/ca.pem",
|
|
234
|
+
# Optional mutual TLS:
|
|
235
|
+
# ssl_cert_file="/etc/ssl/certs/client.crt",
|
|
236
|
+
# ssl_key_file="/etc/ssl/private/client.key",
|
|
237
|
+
|
|
238
|
+
# Sensible timeouts (ms)
|
|
239
|
+
statement_timeout_ms=60_000,
|
|
240
|
+
idle_in_transaction_session_timeout_ms=60_000,
|
|
241
|
+
lock_timeout_ms=5_000,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
db = AsyncDatabaseManager(config)
|
|
245
|
+
await db.connect()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Notes:
|
|
249
|
+
- Use `verify-full` for strict hostname and certificate validation in production.
|
|
250
|
+
- Timeouts are applied via `server_settings`; you can override or disable by passing None.
|
|
251
|
+
|
|
252
|
+
## Examples
|
|
253
|
+
|
|
254
|
+
The `examples/` directory contains applications:
|
|
255
|
+
|
|
256
|
+
- **todo-app/** - REST API with migrations, testing, and error handling
|
|
257
|
+
- **saas-app/** - Multi-tenant SaaS application
|
|
258
|
+
- **microservices/** - Multiple services sharing a connection pool
|
|
259
|
+
|
|
260
|
+
## Documentation
|
|
261
|
+
|
|
262
|
+
- [Quickstart Guide](docs/quickstart.md) - Get started
|
|
263
|
+
- [Patterns Guide](docs/patterns.md) - Deployment patterns, schema isolation, and framework integration
|
|
264
|
+
- [Migration Guide](docs/migrations.md) - Schema versioning and {{tables.}} syntax
|
|
265
|
+
- [API Reference](docs/api-reference.md) - Complete API documentation
|
|
266
|
+
- [Testing Guide](docs/testing.md) - Testing best practices
|
|
267
|
+
|
|
268
|
+
## Contributing
|
|
269
|
+
|
|
270
|
+
Short version:
|
|
271
|
+
|
|
272
|
+
- Requirements: Python 3.9+, PostgreSQL 12+, uv (or pip)
|
|
273
|
+
- Setup:
|
|
274
|
+
- uv: `uv sync`
|
|
275
|
+
- pip: `pip install -e ".[dev]"`
|
|
276
|
+
- hooks: `pre-commit install`
|
|
277
|
+
- Run tests: `pytest`
|
|
278
|
+
- Lint/type-check: `pre-commit run --all-files`
|
|
279
|
+
|
|
280
|
+
Notes:
|
|
281
|
+
|
|
282
|
+
- Integration tests use ephemeral databases; you can override with env vars like `TEST_DB_HOST`, `TEST_DB_PORT`, `TEST_DB_USER`, `TEST_DB_PASSWORD`.
|
|
283
|
+
- Keep PRs small and focused, include tests/docs for user-visible changes.
|
|
284
|
+
- Style is enforced via Black/Isort/Ruff/Mypy; run pre-commit locally before pushing.
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT License - see [LICENSE](LICENSE) for details.
|