Python testing library for PGlite - in-memory PostgreSQL for tests
Project description
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 ♥️ beloved PGlite.
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 py_pglite-0.5.3.tar.gz.
File metadata
- Download URL: py_pglite-0.5.3.tar.gz
- Upload date:
- Size: 32.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58c694602b48fa0562588d7d7c70dd05cc75d048b365ddf3e34d76833598194d
|
|
| MD5 |
dcfd535c1b1c1455d7b215aedf956ee1
|
|
| BLAKE2b-256 |
2a12fb2a0b898f0f34b4e98ea2a2158c1e91afbdfb2b4717a77d7840ae44fb9d
|
Provenance
The following attestation bundles were made for py_pglite-0.5.3.tar.gz:
Publisher:
release.yml on wey-gu/py-pglite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_pglite-0.5.3.tar.gz -
Subject digest:
58c694602b48fa0562588d7d7c70dd05cc75d048b365ddf3e34d76833598194d - Sigstore transparency entry: 528101021
- Sigstore integration time:
-
Permalink:
wey-gu/py-pglite@1ab59cb57aece9bbbfddad64f2eab50d64b032a1 -
Branch / Tag:
refs/tags/v0.5.3 - Owner: https://github.com/wey-gu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ab59cb57aece9bbbfddad64f2eab50d64b032a1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file py_pglite-0.5.3-py3-none-any.whl.
File metadata
- Download URL: py_pglite-0.5.3-py3-none-any.whl
- Upload date:
- Size: 42.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0526d3f69de34bfab2073be43f83b5f023b1856af9623d491bda0de5bef3475
|
|
| MD5 |
26a01ace5502faa1e9580e7504a00f1f
|
|
| BLAKE2b-256 |
1be89265f8ffced326468dac06919a1ca1cc7cbf8c4267a4547cddf7ef887602
|
Provenance
The following attestation bundles were made for py_pglite-0.5.3-py3-none-any.whl:
Publisher:
release.yml on wey-gu/py-pglite
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
py_pglite-0.5.3-py3-none-any.whl -
Subject digest:
c0526d3f69de34bfab2073be43f83b5f023b1856af9623d491bda0de5bef3475 - Sigstore transparency entry: 528101036
- Sigstore integration time:
-
Permalink:
wey-gu/py-pglite@1ab59cb57aece9bbbfddad64f2eab50d64b032a1 -
Branch / Tag:
refs/tags/v0.5.3 - Owner: https://github.com/wey-gu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ab59cb57aece9bbbfddad64f2eab50d64b032a1 -
Trigger Event:
push
-
Statement type: