Skip to main content

pytest-pglite

Real PostgreSQL in your Python process, for tests. No Docker, no server, no Node.js. pytest-pglite embeds PGlite, the WebAssembly build of PostgreSQL 17.5, through Wasmtime and exposes it as a Postgres wire-protocol server so every Python client just works.

def test_users(pglite_engine):
    with pglite_engine.begin() as conn:
        conn.execute(text("create table users (id serial primary key, name text)"))
        conn.execute(text("insert into users (name) values ('Alice')"))
        assert conn.execute(text("select count(*) from users")).scalar_one() == 1

Features

  • Native WASM - PostgreSQL runs inside the Python process via Wasmtime, with only the standard WebAssembly System Interface as a dependency.
  • Real wire protocol - psycopg (sync and async), asyncpg, SQLAlchemy, Django and anything else that speaks Postgres connect to a local Unix socket or TCP port.
  • pytest first - session-scoped fast startup, function-scoped isolation, first-class support for pytest-xdist (each worker gets its own database).
  • Multiple connections - connections share one single-user Postgres backend through a queue with transaction pinning, mirroring upstream PGlite behaviour.
  • Async ready - the server is asyncio based; async fixtures are provided for FastAPI, httpx and other async test suites.
  • pgvector - CREATE EXTENSION vector works out of the box.
  • Fast - a warm worker starts in well under a second; per-test isolation resets take milliseconds.

Install

pip install pytest-pglite
# optional extras
pip install "pytest-pglite[sqlalchemy]"
pip install "pytest-pglite[async]"
pip install "pytest-pglite[django]"
pip install "pytest-pglite[examples]"

The pytest plugin is registered automatically through the pytest11 entry point, so there is nothing to import or configure.

Fixtures

Fixture Scope Description
pglite_server session Running PGliteServer instance (one per xdist worker)
pglite_dsn session libpq connection string (Unix socket, or TCP)
pglite_url session SQLAlchemy URL string
pglite_conn function psycopg.Connection with a freshly reset schema
pglite_async_conn function psycopg.AsyncConnection with a freshly reset schema
pglite_asyncpg function asyncpg.Connection with a freshly reset schema
pglite_engine function SQLAlchemy Engine with a freshly reset schema
pglite_execute function Callable that runs SQL and returns rows

Options

--pglite-extensions=vector        Comma separated extensions to create
--pglite-isolation=schema         none | schema | transaction | database
--pglite-tcp                      Also listen on TCP (127.0.0.1, ephemeral port)
--pglite-tcp-address=host:port    Explicit TCP bind address (default port 0 = OS assigned)
--pglite-work-dir=PATH            Directory for database files (default: temp dir)
--pglite-wasm=PATH                Use a custom pglite.wasi build
--pglite-keep-tmp                 Keep the temporary database directory
--pglite-log-level=INFO           Engine log level

All options can also be set with environment variables using the PGLITE_ prefix, for example PGLITE_ISOLATION=transaction.

Standalone server

The same engine can run outside pytest, for example for a manual psql session or a development server:

pytest-pglite --tcp-address 127.0.0.1:0 --extensions vector
# PGlite ready: postgresql://postgres:password@127.0.0.1:54321/template1?sslmode=disable
from pytest_pglite import PGliteConfig, PGliteServer

with PGliteServer(PGliteConfig(tcp=True)) as server:
    print(server.dsn)

xdist

Each pytest-xdist worker process starts its own PGlite instance with its own temporary directory, its own Unix socket (or its own OS-assigned TCP port). There are no shared paths and no port collisions, so pytest -n 4 just works.

How it works

The package embeds pglite.wasi, a WebAssembly build of PostgreSQL 17.5 with pgvector statically linked. It is built from electric-sql/pglite-build by the pipeline in build/ (see build/README.md for the patches and provenance). A small engine wrapper drives the WASI module through Wasmtime. On top of the single-user backend, an asyncio Postgres wire-protocol proxy manages client connections: each connection is a logical session, and protocol exchanges are serialised through a query queue. Statements and portals are renamed per connection so prepared-statement clients (psycopg3, asyncpg, SQLAlchemy) can share one backend safely.

PGlite is single user, exactly like embedded SQLite. Concurrent connections work, but work is serialised; a connection holding an open transaction holds the backend until it commits or rolls back.

Performance

Measured on the development machine (Linux x86_64, CPython 3.14):

Measurement Result
Cold start (first ever, compiles WASM + initdb) about 2.5 s
Warm start, new worker (AOT + template cache) about 0.25 s
New connection about 1 ms
Query round trip (pytest-pglite overhead) about 1.3 ms
Per-test schema reset about 1 ms

Compiled-module files and an initialised database template are cached under the platform cache directory; pytest-xdist workers share them.

Limitations

  • Single database, single user (postgres). CREATE DATABASE and connected database switching are not supported.
  • Single backend: work from concurrent connections is queued, and a connection holding an open transaction blocks others until it commits or rolls back. Waiting connections time out with SQLSTATE 57014 after queue_timeout seconds (default 60).
  • Session state is shared: SET search_path, SET timezone and similar run-time parameters affect every connection because they share one backend session. Use SET LOCAL inside a transaction for per-test values.
  • PostgreSQL wire-protocol server features are limited to what test suites need: LISTEN/NOTIFY delivery is not implemented, and cancellation requests are accepted but not acted on.
  • COPY TO STDOUT works; COPY FROM STDIN is rejected with SQLSTATE 0A000 because libpglite's synchronous transport cannot stream CopyData during CopyFrom.
  • SQL errors kill the WebAssembly instance (the upstream WASI build has no setjmp/longjmp). The Python host forwards the real ErrorResponse and SQLSTATE, then restarts the backend over the same data directory when the session cannot be recovered.
  • Extensions are compiled into the WASM module at build time; only the bundled set (plpgsql, vector) can be created.
  • No Windows support for Unix sockets; use --pglite-tcp on Windows.

Development

poetry install --all-extras
poetry run pytest
poetry run black .
poetry run flake8
poetry run mypy

The WASM artifact lives in src/pytest_pglite/_wasm/; see build/README.md for provenance and how to rebuild it. Rebuilding uses the upstream pglite-build pipeline; the orchestrator pins compiler parallelism through PGLITE_JOBS (default 8) because upstream defaults to nproc and can exhaust memory on large machines.

License

Apache License 2.0. See LICENSE and THIRD_PARTY_NOTICES.md.

Metadata

Release files for pytest-pglite 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-pglite 0.1.0
File Size Uploaded
pytest_pglite-0.1.0.tar.gz 5.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-pglite 0.1.0
File Interpreter ABI Platform
pytest_pglite-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 10.4 MB

Release files / pytest_pglite-0.1.0.tar.gz

Download URL pytest_pglite-0.1.0.tar.gz
Size 5.2 MB
Tags Source
SHA-256 checksum
How to use checksums
168a67c74bddfdfa9a381227f3cb995d25e3afd94b5165ce1aac99eecbb744f6
BLAKE2b-256 checksum
How to use checksums
d8cd882c6bfebbf63094e51906cdaa261257177505ae22ce3fad2503dfc2a9ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / pytest_pglite-0.1.0-py3-none-any.whl

Download URL pytest_pglite-0.1.0-py3-none-any.whl
Size 5.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
2fe6a69eee366031b45829f3636abfc9b6c27c78c4664651c3db0e810e4622bb
BLAKE2b-256 checksum
How to use checksums
0749c4c1dd78ece5a19e3f86dba3c052f51be6e619613af57aa1ea8ee16204e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page