py-pglite
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.
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="alice@test.com") 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) # engine = create_async_engine(f"postgresql+asyncpg://{host}:{port}/{database}") # For asyncpg specifically, use TCP mode with proper configuration: async def test_asyncpg_works(pglite_tcp_manager): config = pglite_tcp_manager.config conn = await asyncpg.connect( host=config.tcp_host, port=config.tcp_port, user="postgres", password="postgres", database="postgres", ssl=False, server_settings={} # CRITICAL: Required for PGlite compatibility ) result = await conn.fetchval("SELECT 1") await conn.close()
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 )
🌐 Socket Modes (Unix vs TCP)
py-pglite supports both Unix domain sockets (default) and TCP sockets for different use cases:
Unix Socket Mode (Default)
# Default configuration - uses Unix domain socket for best performance from py_pglite import PGliteManager with PGliteManager() as db: # Connection via Unix socket - fastest for local testing dsn = db.get_dsn() # host=/tmp/... dbname=postgres
TCP Socket Mode
from py_pglite import PGliteConfig, PGliteManager # Enable TCP mode for any TCP-only clients config = PGliteConfig( use_tcp=True, tcp_host="127.0.0.1", # Default: localhost only tcp_port=5432, # Default: PostgreSQL standard port extensions=["pgvector"] ) with PGliteManager(config) as db: # Now compatible with any TCP-only clients uri = db.get_psycopg_uri() # postgresql://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable
When to use TCP mode:
- Any TCP-only clients (doesn't support Unix sockets)
- Cloud-native testing environments
- Docker containers with network isolation
- Testing network-based database tools
- Required for asyncpg: asyncpg only works in TCP mode
Important notes:
- PGlite Socket supports only one active connection at a time
- SSL is not supported - always use
sslmode=disable - Unix sockets are faster for local testing (default)
- TCP mode binds to localhost by default for security
- asyncpg requires
server_settings={}to prevent hanging
🔄 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) # Django: Uses custom py-pglite backend automatically # asyncpg requires TCP mode and specific configuration: config = PGliteConfig(use_tcp=True) with PGliteManager(config) as manager: conn = await asyncpg.connect( host=config.tcp_host, port=config.tcp_port, user="postgres", password="postgres", database="postgres", ssl=False, server_settings={} # Required for PGlite compatibility )
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