persistkat library
Project description
PersistKat
Database abstraction layer for the KatCity framework. Provides async-first SQLAlchemy 2.0+ integration with repository patterns, Pydantic schema generation, and comprehensive type safety.
Features
- Repository Pattern: Generic
BaseRepositorywith fluent query interface - Async-First: Built on SQLAlchemy 2.0+ async engine and sessions
- Type-Safe: Full type hints with generic repository support
- Pydantic Integration: Auto-generate schemas from SQLAlchemy models
- Mixins: UUID/Int primary keys, timestamp tracking, common patterns
- Bulk Operations: Efficient bulk inserts with conflict resolution
- Migration Support: Goose-based database migrations via CLI
- Configuration: YAML-based config with environment overrides
Installation
# Core package
uv pip install persistkat
# With PostgreSQL support
uv pip install "persistkat[pg]"
# With CLI tools
uv pip install "persistkat[cli]"
# Everything
uv pip install "persistkat[all]"
Quick Start
1. Initialize Database
# SQLite (default) - creates tables directly
persistkat db init
# Or with migrations (requires goose)
persistkat db up
# Check database health
persistkat db health
Note: db init only creates tables, it never drops them. For a fresh start, manually delete the database file or use migrations with db down.
2. Define Your Models
from persistkat import Base, UUIDPKMixin, UpdateMixin
from sqlalchemy.orm import Mapped, mapped_column
class User(Base, UUIDPKMixin, UpdateMixin):
"""User model with UUID PK and timestamps."""
__tablename__ = "users"
name: Mapped[str] = mapped_column()
email: Mapped[str] = mapped_column(unique=True)
is_active: Mapped[bool] = mapped_column(default=True)
3. Create a Repository
from persistkat import BaseRepository
from sqlalchemy.ext.asyncio import AsyncSession
class UserRepository(BaseRepository[User]):
def __init__(self, session: AsyncSession):
super().__init__(session, User)
async def get_active_users(self) -> list[User]:
query = self.where(User.is_active == True).order_by(User.name)
return await self.all(query=query)
4. Use in Your Application
import asyncio
from persistkat import PersistEngine
from persistkat.config import config
async def main():
cfg = config("config.yaml")
engine = PersistEngine(cfg.schema.database)
db_engine, session_factory = engine.session()
async with session_factory() as session:
repo = UserRepository(session)
# Create
user = User(name="Alice", email="alice@example.com")
await repo.save(user)
await session.commit()
# Query
found = await repo.find_by(email="alice@example.com")
active = await repo.get_active_users()
CLI Tools
# Generate Pydantic schemas from models
persistkat codegen generate-schemas your_app.models --output schemas.py
# Database migrations with Goose (supports PostgreSQL, MySQL, SQLite, MSSQL, Redshift)
persistkat db create add_users_table
persistkat db up
persistkat db status
persistkat db down
persistkat db health
# Version info
persistkat version
Migration Support
PersistKat uses Goose for SQL-based database migrations. Goose supports:
- ✅ PostgreSQL - Full support including triggers, functions, and advanced features
- ✅ MySQL - Stored procedures, triggers, views
- ✅ SQLite - Lightweight migrations for development/testing
- ✅ MSSQL - Microsoft SQL Server
- ✅ Redshift - Amazon Redshift data warehouse
For other databases (Oracle, ClickHouse, CockroachDB, Snowflake, etc.):
- ✅ PersistKat engine works with ANY SQLAlchemy-supported database
- ❌ Migration CLI won't work (Goose limitation)
- Workaround: Apply migrations manually using your database client or use Alembic (Python-based migrations)
Why Goose?
- Pure SQL migrations (not Python DSL)
- Simple, version-controlled
.sqlfiles - No code generation complexity
- Easy to audit and review changes
Configuration
PersistKat supports flexible database configuration with multiple formats:
Option 1: Full DSN (Recommended)
name: my-app
database:
dsn: postgresql+asyncpg://user:pass@localhost:5432/mydb
Option 1b: DSN with Field Overrides (Best for Secrets)
Combine DSN structure with environment variable overrides:
name: my-app
database:
dsn: postgresql+asyncpg://defaultuser:defaultpass@localhost:5432/mydb
user: ${DB_USER} # Overrides user from DSN
password: ${DB_PASSWORD} # Overrides password from DSN
Or in code:
export PERSISTKAT_DATABASE__USER=prod_user
export PERSISTKAT_DATABASE__PASSWORD=secret_password
The final DSN will be: postgresql+asyncpg://prod_user:secret_password@localhost:5432/mydb
Option 2: Driver-Only (Auto-Detects Dialect)
name: my-app
database:
driver: asyncpg # Auto-detects: dialect=postgresql
db: myapp-dev
host: localhost
port: 5432
user: postgres
password: postgres
Option 3: Individual Fields (Most Explicit)
name: my-app
database:
dialect: postgresql
driver: asyncpg
db: myapp-dev
user: postgres
password: postgres
host: localhost
port: 5432
debug: false
Supported Drivers
PostgreSQL:
asyncpg(recommended, fastest)psycopg(psycopg3, also excellent)
SQLite:
aiosqlite(recommended)
MySQL:
aiomysqlasyncmy
Others:
- Any async SQLAlchemy driver (
aioodbc, custom drivers, etc.)
Environment Variable Overrides
export PERSISTKAT_DATABASE__DSN=postgresql+asyncpg://localhost/prod-db
export PERSISTKAT_DATABASE__DEBUG=true
Limitations & Alternatives
Migration Tool Limitations
The persistkat db commands use Goose, which supports most common databases but not all SQLAlchemy dialects.
If you need migrations for unsupported databases:
-
Use Alembic - SQLAlchemy's official migration tool (Python-based, not pure SQL)
uv pip install alembic alembic init migrations alembic revision -m "create tables" alembic upgrade head
-
Apply SQL manually - Use your database's native client
# PostgreSQL psql -d mydb -f migrations/001_init.sql # MySQL mysql -u root -p mydb < migrations/001_init.sql
-
Request Goose support - Submit feature request to Goose project
Engine vs Migration Support
| Database | PersistKat Engine | Migration CLI |
|---|---|---|
| PostgreSQL | ✅ Full support | ✅ Via Goose |
| MySQL | ✅ Full support | ✅ Via Goose |
| SQLite | ✅ Full support | ✅ Via Goose |
| MSSQL | ✅ Full support | ✅ Via Goose |
| Redshift | ✅ Full support | ✅ Via Goose |
| Oracle | ✅ Full support | ⚠️ Manual SQL |
| ClickHouse | ✅ Full support | ⚠️ Manual SQL |
| CockroachDB | ✅ Full support | ⚠️ Use CockroachDB's tools |
| Snowflake | ✅ Full support | ⚠️ Use Snowflake's tools |
Development
- Run tests:
make test - Skip database tests:
SKIP_DB_TESTS=true make testorpytest -m "not db" - Run only database tests:
pytest -m db - Run linter & formatter:
make checkandmake fix - Run type checker:
make pyright
Running Tests
PersistKat tests support multiple database backends:
- SQLite (default) - No setup required, uses temporary in-memory database
- PostgreSQL - Requires running PostgreSQL server
- Both - Runs tests against both backends
Database Backend Selection:
# Use SQLite (default, no setup needed)
make test
# Use PostgreSQL (requires running database)
TEST_DB_BACKEND=postgresql make test
# Test against both backends
TEST_DB_BACKEND=both make test
# Skip database tests entirely
SKIP_DB_TESTS=true make test
# or
pytest -m "not db"
Test Categories:
- Unit tests - Don't require a database connection
- Database tests - Marked with
@pytest.mark.db - SQLite-specific - Marked with
@pytest.mark.sqlite - PostgreSQL-specific - Marked with
@pytest.mark.postgresql
Run specific backend tests:
# Only SQLite tests
pytest -m "db and sqlite"
# Only PostgreSQL tests (requires database on localhost:5434)
pytest -m "db and postgresql"
Documentation
See the KatCity monorepo README for architecture and design principles.
For detailed guides:
- Repository pattern best practices
- Transaction management
- FastAPI integration
- Bulk operations
- Schema generation
License
BSD 3-Clause License - See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file persistkat-0.1.3.tar.gz.
File metadata
- Download URL: persistkat-0.1.3.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1abc9c03e9b178bd062bdcf9e18f5b81b5f9c5d3acf531f25776a0447867b685
|
|
| MD5 |
0907178e137280796b3547974ad4e881
|
|
| BLAKE2b-256 |
dc019f488321a1c99395239791a0ac9f401980edcec71fd13da97b7e161f9df0
|
File details
Details for the file persistkat-0.1.3-py3-none-any.whl.
File metadata
- Download URL: persistkat-0.1.3-py3-none-any.whl
- Upload date:
- Size: 32.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5fb2cd71bd06a9aa7c7c5755614f558ba2eafc94acb0aec5acab9fef44b13a8
|
|
| MD5 |
731f468ca0ee8d374913f19ceddff16b
|
|
| BLAKE2b-256 |
d62b3883f783c57267e3da01d38f43a189041882833dee686c697157c55ba8d1
|