pgdbm


Namepgdbm JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryProduction-ready async PostgreSQL utilities with connection pooling, migrations, and testing support
upload_time2025-08-13 19:52:13
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT
keywords async asyncio asyncpg database migrations postgresql testing
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pgdbm

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Python](https://img.shields.io/pypi/pyversions/pgdbm.svg)](https://pypi.org/project/pgdbm/)
[![PyPI](https://img.shields.io/pypi/v/pgdbm.svg)](https://pypi.org/project/pgdbm/)

A PostgreSQL library for Python that provides high-level async database operations with built-in migration management, connection pooling, and testing utilities. It offers:

- **Connection pooling** with proper resource management
- **Schema migrations** that are version-controlled and automated
- **Testing utilities** that provide isolated test databases
- **Module isolation** when multiple services share a database
- **Monitoring** for slow queries and connection issues

## Key Features

- **๐Ÿš€ High Performance** - Built on asyncpg, the fastest PostgreSQL driver for Python
- **๐Ÿ“ฆ Migration System** - Version-controlled schema migrations with automatic ordering
- **๐Ÿงช Testing Support** - Fixtures and utilities for database testing
- **๐Ÿ”ง Module Isolation** - Prevent table conflicts when modules share databases
- **๐Ÿ“Š Monitoring** - Track slow queries and connection pool metrics
- **๐Ÿ”’ Type Safe** - Full type hints and Pydantic integration

### Design intent: dual ownership

pgdbm is designed so a module can either:
- **Own its database** when run independently (it creates a pool and runs its own migrations), or
- **Use a database owned by another** component via a shared pool while still running its own migrations into a schema namespace.

This keeps modules portable: the same code can run as a standalone service or as part of a larger app.

## Installation

```bash
# Install using uv (recommended)
uv add pgdbm

# Or using pip
pip install pgdbm
```

## Quick Start

### 1. Create a migration file

```sql
-- migrations/001_initial.sql
CREATE TABLE IF NOT EXISTS {{tables.users}} (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

### 2. Use pgdbm

```python
from pgdbm import AsyncDatabaseManager, DatabaseConfig, AsyncMigrationManager

# Configure and connect
config = DatabaseConfig(
    host="localhost",
    database="myapp",
    user="postgres",
    password="secret"
)

db = AsyncDatabaseManager(config)
await db.connect()

# Apply migrations
migrations = AsyncMigrationManager(db, migrations_path="./migrations")
await migrations.apply_pending()

# Use your database
user_id = await db.execute_and_return_id(
    "INSERT INTO {{tables.users}} (email) VALUES ($1)",
    "user@example.com"
)

# Clean up
await db.disconnect()
```

Standalone vs shared-pool usage:

```python
# Standalone (owns the DB):
config = DatabaseConfig(connection_string="postgresql://localhost/app", schema="users")
db = AsyncDatabaseManager(config)
await db.connect()
await AsyncMigrationManager(db, "./migrations", module_name="users").apply_pending()

# Shared pool (uses external DB):
shared = await AsyncDatabaseManager.create_shared_pool(DatabaseConfig(connection_string="postgresql://localhost/app"))
db = AsyncDatabaseManager(pool=shared, schema="users")  # Module still runs its own migrations
await AsyncMigrationManager(db, "./migrations", module_name="users").apply_pending()
```

## Core Patterns

### 1. Migration Management

pgdbm includes a built-in migration system:

```python
# Apply all pending migrations
migrations = AsyncMigrationManager(db, migrations_path="./migrations")
result = await migrations.apply_pending()

# Check what was applied
for migration in result['applied']:
    print(f"Applied {migration['filename']} in {migration['execution_time_ms']}ms")
```

Migrations are automatically ordered by version number extracted from filenames.

### 2. Module Isolation

When multiple modules share a database, use schemas to prevent table conflicts:

```python
# Each module can get its own schema
user_db = AsyncDatabaseManager(
    config=DatabaseConfig(database="app", schema="user_module")
)
blog_db = AsyncDatabaseManager(
    config=DatabaseConfig(database="app", schema="blog_module")
)

# Both can have a "users" table without conflict:
# - user_module.users
# - blog_module.users
```

The `{{tables.tablename}}` syntax in queries automatically expands to the correct schema-qualified name.

### 3. Connection Pool Sharing

For applications with multiple services sharing a database:

```python
# Create shared pool
shared_pool = await AsyncDatabaseManager.create_shared_pool(config)

# Each service gets its own schema but shares connections
user_db = AsyncDatabaseManager(pool=shared_pool, schema="users")
billing_db = AsyncDatabaseManager(pool=shared_pool, schema="billing")
```

### 4. Testing Support

Built-in fixtures for database tests:

```python
# conftest.py
from pgdbm.fixtures.conftest import *

# test_users.py
async def test_create_user(test_db):
    # Automatic test database with cleanup
    await test_db.execute("""
        CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT)
    """)

    user_id = await test_db.execute_and_return_id(
        "INSERT INTO users (email) VALUES ($1)",
        "test@example.com"
    )
    assert user_id == 1
```

### 5. Monitoring

Track database performance:

```python
from pgdbm import MonitoredAsyncDatabaseManager

db = MonitoredAsyncDatabaseManager(
    config=config,
    slow_query_threshold_ms=100  # Log queries over 100ms
)

# Get metrics
metrics = await db.get_query_metrics()
slow_queries = await db.get_slow_queries(limit=10)
```

### 6. Production TLS and Timeouts

Enable TLS with certificate verification and enforce server-side timeouts:

```python
from pgdbm import AsyncDatabaseManager, DatabaseConfig

config = DatabaseConfig(
    connection_string="postgresql://db.example.com/app",
    ssl_enabled=True,
    ssl_mode="verify-full",           # 'require' | 'verify-ca' | 'verify-full'
    ssl_ca_file="/etc/ssl/certs/ca.pem",
    # Optional mutual TLS:
    # ssl_cert_file="/etc/ssl/certs/client.crt",
    # ssl_key_file="/etc/ssl/private/client.key",

    # Sensible timeouts (ms)
    statement_timeout_ms=60_000,
    idle_in_transaction_session_timeout_ms=60_000,
    lock_timeout_ms=5_000,
)

db = AsyncDatabaseManager(config)
await db.connect()
```

Notes:
- Use `verify-full` for strict hostname and certificate validation in production.
- Timeouts are applied via `server_settings`; you can override or disable by passing None.

## Examples

The `examples/` directory contains applications:

- **todo-app/** - REST API with migrations, testing, and error handling
- **saas-app/** - Multi-tenant SaaS application
- **microservices/** - Multiple services sharing a connection pool

## Documentation

- [Quickstart Guide](docs/quickstart.md) - Get started
- [Patterns Guide](docs/patterns.md) - Deployment patterns, schema isolation, and framework integration
- [Migration Guide](docs/migrations.md) - Schema versioning and {{tables.}} syntax
- [API Reference](docs/api-reference.md) - Complete API documentation
- [Testing Guide](docs/testing.md) - Testing best practices

## Contributing

Short version:

- Requirements: Python 3.9+, PostgreSQL 12+, uv (or pip)
- Setup:
  - uv: `uv sync`
  - pip: `pip install -e ".[dev]"`
  - hooks: `pre-commit install`
- Run tests: `pytest`
- Lint/type-check: `pre-commit run --all-files`

Notes:

- Integration tests use ephemeral databases; you can override with env vars like `TEST_DB_HOST`, `TEST_DB_PORT`, `TEST_DB_USER`, `TEST_DB_PASSWORD`.
- Keep PRs small and focused, include tests/docs for user-visible changes.
- Style is enforced via Black/Isort/Ruff/Mypy; run pre-commit locally before pushing.

## License

MIT License - see [LICENSE](LICENSE) for details.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pgdbm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "Juan Reyero <juan@juanreyero.com>",
    "keywords": "async, asyncio, asyncpg, database, migrations, postgresql, testing",
    "author": null,
    "author_email": "Juan Reyero <juan@juanreyero.com>",
    "download_url": "https://files.pythonhosted.org/packages/3b/39/dad7ba86632ccd8fdfd92e1cf0e3f209ee36e8e4eafcba4fa7ec9f24af36/pgdbm-0.1.0.tar.gz",
    "platform": null,
    "description": "# pgdbm\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Python](https://img.shields.io/pypi/pyversions/pgdbm.svg)](https://pypi.org/project/pgdbm/)\n[![PyPI](https://img.shields.io/pypi/v/pgdbm.svg)](https://pypi.org/project/pgdbm/)\n\nA PostgreSQL library for Python that provides high-level async database operations with built-in migration management, connection pooling, and testing utilities. It offers:\n\n- **Connection pooling** with proper resource management\n- **Schema migrations** that are version-controlled and automated\n- **Testing utilities** that provide isolated test databases\n- **Module isolation** when multiple services share a database\n- **Monitoring** for slow queries and connection issues\n\n## Key Features\n\n- **\ud83d\ude80 High Performance** - Built on asyncpg, the fastest PostgreSQL driver for Python\n- **\ud83d\udce6 Migration System** - Version-controlled schema migrations with automatic ordering\n- **\ud83e\uddea Testing Support** - Fixtures and utilities for database testing\n- **\ud83d\udd27 Module Isolation** - Prevent table conflicts when modules share databases\n- **\ud83d\udcca Monitoring** - Track slow queries and connection pool metrics\n- **\ud83d\udd12 Type Safe** - Full type hints and Pydantic integration\n\n### Design intent: dual ownership\n\npgdbm is designed so a module can either:\n- **Own its database** when run independently (it creates a pool and runs its own migrations), or\n- **Use a database owned by another** component via a shared pool while still running its own migrations into a schema namespace.\n\nThis keeps modules portable: the same code can run as a standalone service or as part of a larger app.\n\n## Installation\n\n```bash\n# Install using uv (recommended)\nuv add pgdbm\n\n# Or using pip\npip install pgdbm\n```\n\n## Quick Start\n\n### 1. Create a migration file\n\n```sql\n-- migrations/001_initial.sql\nCREATE TABLE IF NOT EXISTS {{tables.users}} (\n    id SERIAL PRIMARY KEY,\n    email VARCHAR(255) UNIQUE NOT NULL,\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n```\n\n### 2. Use pgdbm\n\n```python\nfrom pgdbm import AsyncDatabaseManager, DatabaseConfig, AsyncMigrationManager\n\n# Configure and connect\nconfig = DatabaseConfig(\n    host=\"localhost\",\n    database=\"myapp\",\n    user=\"postgres\",\n    password=\"secret\"\n)\n\ndb = AsyncDatabaseManager(config)\nawait db.connect()\n\n# Apply migrations\nmigrations = AsyncMigrationManager(db, migrations_path=\"./migrations\")\nawait migrations.apply_pending()\n\n# Use your database\nuser_id = await db.execute_and_return_id(\n    \"INSERT INTO {{tables.users}} (email) VALUES ($1)\",\n    \"user@example.com\"\n)\n\n# Clean up\nawait db.disconnect()\n```\n\nStandalone vs shared-pool usage:\n\n```python\n# Standalone (owns the DB):\nconfig = DatabaseConfig(connection_string=\"postgresql://localhost/app\", schema=\"users\")\ndb = AsyncDatabaseManager(config)\nawait db.connect()\nawait AsyncMigrationManager(db, \"./migrations\", module_name=\"users\").apply_pending()\n\n# Shared pool (uses external DB):\nshared = await AsyncDatabaseManager.create_shared_pool(DatabaseConfig(connection_string=\"postgresql://localhost/app\"))\ndb = AsyncDatabaseManager(pool=shared, schema=\"users\")  # Module still runs its own migrations\nawait AsyncMigrationManager(db, \"./migrations\", module_name=\"users\").apply_pending()\n```\n\n## Core Patterns\n\n### 1. Migration Management\n\npgdbm includes a built-in migration system:\n\n```python\n# Apply all pending migrations\nmigrations = AsyncMigrationManager(db, migrations_path=\"./migrations\")\nresult = await migrations.apply_pending()\n\n# Check what was applied\nfor migration in result['applied']:\n    print(f\"Applied {migration['filename']} in {migration['execution_time_ms']}ms\")\n```\n\nMigrations are automatically ordered by version number extracted from filenames.\n\n### 2. Module Isolation\n\nWhen multiple modules share a database, use schemas to prevent table conflicts:\n\n```python\n# Each module can get its own schema\nuser_db = AsyncDatabaseManager(\n    config=DatabaseConfig(database=\"app\", schema=\"user_module\")\n)\nblog_db = AsyncDatabaseManager(\n    config=DatabaseConfig(database=\"app\", schema=\"blog_module\")\n)\n\n# Both can have a \"users\" table without conflict:\n# - user_module.users\n# - blog_module.users\n```\n\nThe `{{tables.tablename}}` syntax in queries automatically expands to the correct schema-qualified name.\n\n### 3. Connection Pool Sharing\n\nFor applications with multiple services sharing a database:\n\n```python\n# Create shared pool\nshared_pool = await AsyncDatabaseManager.create_shared_pool(config)\n\n# Each service gets its own schema but shares connections\nuser_db = AsyncDatabaseManager(pool=shared_pool, schema=\"users\")\nbilling_db = AsyncDatabaseManager(pool=shared_pool, schema=\"billing\")\n```\n\n### 4. Testing Support\n\nBuilt-in fixtures for database tests:\n\n```python\n# conftest.py\nfrom pgdbm.fixtures.conftest import *\n\n# test_users.py\nasync def test_create_user(test_db):\n    # Automatic test database with cleanup\n    await test_db.execute(\"\"\"\n        CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT)\n    \"\"\")\n\n    user_id = await test_db.execute_and_return_id(\n        \"INSERT INTO users (email) VALUES ($1)\",\n        \"test@example.com\"\n    )\n    assert user_id == 1\n```\n\n### 5. Monitoring\n\nTrack database performance:\n\n```python\nfrom pgdbm import MonitoredAsyncDatabaseManager\n\ndb = MonitoredAsyncDatabaseManager(\n    config=config,\n    slow_query_threshold_ms=100  # Log queries over 100ms\n)\n\n# Get metrics\nmetrics = await db.get_query_metrics()\nslow_queries = await db.get_slow_queries(limit=10)\n```\n\n### 6. Production TLS and Timeouts\n\nEnable TLS with certificate verification and enforce server-side timeouts:\n\n```python\nfrom pgdbm import AsyncDatabaseManager, DatabaseConfig\n\nconfig = DatabaseConfig(\n    connection_string=\"postgresql://db.example.com/app\",\n    ssl_enabled=True,\n    ssl_mode=\"verify-full\",           # 'require' | 'verify-ca' | 'verify-full'\n    ssl_ca_file=\"/etc/ssl/certs/ca.pem\",\n    # Optional mutual TLS:\n    # ssl_cert_file=\"/etc/ssl/certs/client.crt\",\n    # ssl_key_file=\"/etc/ssl/private/client.key\",\n\n    # Sensible timeouts (ms)\n    statement_timeout_ms=60_000,\n    idle_in_transaction_session_timeout_ms=60_000,\n    lock_timeout_ms=5_000,\n)\n\ndb = AsyncDatabaseManager(config)\nawait db.connect()\n```\n\nNotes:\n- Use `verify-full` for strict hostname and certificate validation in production.\n- Timeouts are applied via `server_settings`; you can override or disable by passing None.\n\n## Examples\n\nThe `examples/` directory contains applications:\n\n- **todo-app/** - REST API with migrations, testing, and error handling\n- **saas-app/** - Multi-tenant SaaS application\n- **microservices/** - Multiple services sharing a connection pool\n\n## Documentation\n\n- [Quickstart Guide](docs/quickstart.md) - Get started\n- [Patterns Guide](docs/patterns.md) - Deployment patterns, schema isolation, and framework integration\n- [Migration Guide](docs/migrations.md) - Schema versioning and {{tables.}} syntax\n- [API Reference](docs/api-reference.md) - Complete API documentation\n- [Testing Guide](docs/testing.md) - Testing best practices\n\n## Contributing\n\nShort version:\n\n- Requirements: Python 3.9+, PostgreSQL 12+, uv (or pip)\n- Setup:\n  - uv: `uv sync`\n  - pip: `pip install -e \".[dev]\"`\n  - hooks: `pre-commit install`\n- Run tests: `pytest`\n- Lint/type-check: `pre-commit run --all-files`\n\nNotes:\n\n- Integration tests use ephemeral databases; you can override with env vars like `TEST_DB_HOST`, `TEST_DB_PORT`, `TEST_DB_USER`, `TEST_DB_PASSWORD`.\n- Keep PRs small and focused, include tests/docs for user-visible changes.\n- Style is enforced via Black/Isort/Ruff/Mypy; run pre-commit locally before pushing.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Production-ready async PostgreSQL utilities with connection pooling, migrations, and testing support",
    "version": "0.1.0",
    "project_urls": {
        "Changelog": "https://github.com/juanre/pgdbm/blob/main/CHANGELOG.md",
        "Documentation": "https://github.com/juanre/pgdbm",
        "Homepage": "https://juanreyero.com/open/pgdbm",
        "Issues": "https://github.com/juanre/pgdbm/issues",
        "Repository": "https://github.com/juanre/pgdbm"
    },
    "split_keywords": [
        "async",
        " asyncio",
        " asyncpg",
        " database",
        " migrations",
        " postgresql",
        " testing"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "00830e38f646b4869ddc0120f9b65785fded31644466fd7dde9aa7cd66bf0b75",
                "md5": "2583a820b06e82f62f4abf35b9e16127",
                "sha256": "b8eacee31763d8dab9311b9dfb8edfc79413473dffc7e50a10b3d7546ffe0ad6"
            },
            "downloads": -1,
            "filename": "pgdbm-0.1.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2583a820b06e82f62f4abf35b9e16127",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 30375,
            "upload_time": "2025-08-13T19:52:11",
            "upload_time_iso_8601": "2025-08-13T19:52:11.194882Z",
            "url": "https://files.pythonhosted.org/packages/00/83/0e38f646b4869ddc0120f9b65785fded31644466fd7dde9aa7cd66bf0b75/pgdbm-0.1.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "3b39dad7ba86632ccd8fdfd92e1cf0e3f209ee36e8e4eafcba4fa7ec9f24af36",
                "md5": "eb73f2db03de67eb1afbdd80876a4026",
                "sha256": "4ba0d8137bbf98b66f446d7a3b279feb86e3038f77c0496059e2e973c7516144"
            },
            "downloads": -1,
            "filename": "pgdbm-0.1.0.tar.gz",
            "has_sig": false,
            "md5_digest": "eb73f2db03de67eb1afbdd80876a4026",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 191693,
            "upload_time": "2025-08-13T19:52:13",
            "upload_time_iso_8601": "2025-08-13T19:52:13.066066Z",
            "url": "https://files.pythonhosted.org/packages/3b/39/dad7ba86632ccd8fdfd92e1cf0e3f209ee36e8e4eafcba4fa7ec9f24af36/pgdbm-0.1.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-13 19:52:13",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "juanre",
    "github_project": "pgdbm",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "pgdbm"
}
        
Elapsed time: 1.05515s