Skip to content

wey-gu/py-pglite

Repository files navigation

py-pglite

py-pglite Logo

A Pythonic interface for PGlite - the instant, zero-config PostgreSQL. ⚑️

py-pglite brings the magic of PGlite to Python with a high-level, developer-friendly API. Real PostgreSQL, instant testing.

pip install py-pglite


def test_users(pglite_session):
    user = User(name="Alice")
    pglite_session.add(user)
    pglite_session.commit()
    assert user.id == 1  # It's real PostgreSQL!

That's it. No Docker, no setup, no config files. Real PostgreSQL, instant testing.

CI PyPI Python

License MyPy Ruff codecov


Why py-pglite?

# ❌ Traditional testing
def test_old_way():
    # 1. Install PostgreSQL
    # 2. Configure connection  
    # 3. Manage test databases
    # 4. Handle cleanup
    # 5. Docker containers...
    pass

# βœ… py-pglite way  
def test_new_way(pglite_session):
    User.objects.create(name="Alice")  # Just works!

The magic:

  • 🎯 Zero config - No setup, no Docker, no servers
  • ⚑ Instant - 2-3s startup vs 30-60s Docker
  • πŸ”„ Isolated - Fresh database per test
  • 🐘 Real PostgreSQL - JSON, arrays, window functions
  • πŸš€ Any client - SQLAlchemy, Django, psycopg, asyncpg

Install

# Core (framework-agnostic)
pip install py-pglite

# With your stack
pip install py-pglite[sqlalchemy]  # SQLAlchemy + SQLModel
pip install py-pglite[django]      # Django + pytest-django  
pip install py-pglite[asyncpg]     # Pure async client
pip install py-pglite[all]         # Everything

# Extra Features
pip install py-pglite[extensions]  # pglite extensions, like pgvector, fuzzystrmatch etc.

Quick Start

SQLAlchemy (Zero imports needed)

def test_sqlalchemy_just_works(pglite_session):
    user = User(name="Alice", email="[email protected]")  
    pglite_session.add(user)
    pglite_session.commit()
    
    assert user.id is not None
    assert User.query.count() == 1  # Real PostgreSQL!

Django

πŸ”Ή Lightweight/Socket (Minimal setup)

def test_django_socket_pattern(configured_django):
    Post.objects.create(title="Hello", content="World")
    assert Post.objects.count() == 1  # Real PostgreSQL via socket!

πŸ”Έ Full Integration/Backend (Enhanced features)

def test_django_backend_pattern(django_pglite_db):
    Post.objects.create(title="Hello", content="World", metadata={"tags": ["test"]})
    assert Post.objects.count() == 1  # Custom backend with JSON support!

Choose your pattern:

  • Lightweight: Fast, minimal dependencies, standard PostgreSQL backend
  • Full Integration: Advanced features, custom backend, enhanced JSON support

πŸ‘‰ See Django patterns guide for detailed examples and migration guide.

Any PostgreSQL client

def test_any_client_works(pglite_manager):
    # Extract connection details
    engine = pglite_manager.get_engine()
    host, port, database = str(engine.url.host), engine.url.port, engine.url.database
    
    # Use with any PostgreSQL client
    # conn = psycopg.connect(host=host, port=port, dbname=database)
    # conn = await asyncpg.connect(host=host, port=port, database=database)
    # engine = create_async_engine(f"postgresql+asyncpg://{host}:{port}/{database}")

Examples

FastAPI + SQLModel

from fastapi.testclient import TestClient

def test_api_endpoint(client: TestClient):
    response = client.post("/users/", json={"name": "Alice"})
    assert response.status_code == 201
    
    response = client.get("/users/")
    assert len(response.json()) == 1

PostgreSQL Features

def test_postgresql_power(pglite_session):
    pglite_session.execute(text("""
        CREATE TABLE analytics (
            data JSONB,
            tags TEXT[],
            created TIMESTAMP DEFAULT NOW()
        )
    """))
    
    pglite_session.execute(text("""
        INSERT INTO analytics (data, tags) VALUES 
        ('{"clicks": 100}', ARRAY['web', 'mobile'])
    """))
    
    result = pglite_session.execute(text("""
        SELECT data->>'clicks' as clicks,
               array_length(tags, 1) as tag_count
        FROM analytics 
        WHERE data->>'clicks' > '50'
    """)).fetchone()
    
    assert result.clicks == '100'

PostgreSQL Extensions

py-pglite supports PostgreSQL extensions, allowing you to test advanced features like vector similarity search for AI/RAG applications.

πŸš€ pgvector for RAG Applications

Enable pgvector to test vector embeddings and similarity search directly in your test suite.

1. Install with the [extensions] extra:

pip install 'py-pglite[extensions]'

2. Enable pgvector in the configuration:

from py_pglite import PGliteConfig, PGliteManager
from pgvector.psycopg import register_vector
import psycopg
import numpy as np

# Enable the extension
config = PGliteConfig(extensions=["pgvector"])

with PGliteManager(config=config) as db:
    with psycopg.connect(db.get_dsn(), autocommit=True) as conn:
        # Create the extension and register the type
        conn.execute("CREATE EXTENSION IF NOT EXISTS vector")
        register_vector(conn)

        # Create a table and insert a vector
        conn.execute("CREATE TABLE items (embedding vector(3))")
        conn.execute("INSERT INTO items (embedding) VALUES (%s)", (np.array([1, 2, 3]),))
        
        # Perform a similarity search
        result = conn.execute("SELECT * FROM items ORDER BY embedding <-> %s LIMIT 1", (np.array([1, 1, 1]),)).fetchone()
        assert np.array_equal(result[0], np.array([1, 2, 3]))

py-pglite can support many other extensions available in the underlying PGlite extensions β™₯️.


Advanced

πŸ”§ Production Configuration
from py_pglite import PGliteConfig
from py_pglite.sqlalchemy import SQLAlchemyPGliteManager

config = PGliteConfig(
    timeout=60,                    # Extended timeout for CI/CD
    log_level="INFO",              # Balanced logging
    cleanup_on_exit=True,          # Automatic cleanup
    work_dir=Path("./test-data")   # Custom directory
)

with SQLAlchemyPGliteManager(config) as manager:
    engine = manager.get_engine(
        pool_recycle=3600,         # Connection recycling
        echo=False                 # SQL logging
    )
πŸ”„ Client Compatibility
# py-pglite provides a REAL PostgreSQL server - any client works!

with SQLAlchemyPGliteManager() as manager:
    engine = manager.get_engine()
    url = engine.url
    
    # Extract connection details for any PostgreSQL client
    host, port, database = str(url.host), url.port, url.database
    
    # Examples for different clients:
    # psycopg:  psycopg.connect(host=host, port=port, dbname=database)
    # asyncpg:  await asyncpg.connect(host=host, port=port, database=database)
    # Django:   Uses custom py-pglite backend automatically

Installation Matrix:

Client Install Use Case
[sqlalchemy] SQLAlchemy + SQLModel ORM, modern Python
[django] Django + pytest-django Django projects
[psycopg] psycopg (sync/async) Raw SQL, custom
[asyncpg] Pure async client High-performance async
[all] Everything Full compatibility
🎯 Framework Isolation
# Perfect isolation - no framework bleeding
pytest -m sqlalchemy -p no:django     # Pure SQLAlchemy
pytest -m django -p no:sqlalchemy     # Pure Django  
pytest tests/sqlalchemy/              # Directory isolation

Built for developers who want PostgreSQL testing without the complexity.

🎯 Examples β€’ πŸ“š Contributing β€’ πŸ› Issues


py-pglite: Because testing should be simple. ⚑

Powered by the πŸš€ amazing and β™₯️ beloved PGlite.

About

PGlite wrapper in Python for testing. Test your app with Postgres just as lite as SQLite.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published