- +
+
-

- PostPyro -

-

- Async PostgreSQL Driver for Python, Built with Rust -

+ + + Beta — async rewrite, breaking changes expected before 1.0 + +

Async PostgreSQL for Python, built in Rust

- Built with PyO3 and sqlx, PostPyro is a fully async driver - every I/O method is - async def and releases the GIL while waiting on Postgres - with - DB-API 2.0-flavored exception handling. + PostPyro wraps sqlx in a PyO3/pyo3-asyncio binding. Every I/O method + is async def and releases the GIL while waiting on Postgres, parameters bind + natively — datetimes, UUIDs, Decimal, JSON, arrays, bytes — and errors surface + through a DB-API 2.0-flavored exception hierarchy.

-
-
- 🔥 - Rust Powered -
-
- ⚡ - High Performance -
-
- 🛡️ - Memory Safe -
-
- 📦 - Zero Dependencies -
+
+ + + Memory-safe Rust core + + + + Fully async, GIL released on I/O + + + + Zero Python dependencies +
- - - + + +
quick_example.py
@@ -102,24 +98,19 @@

import PostPyro async def main(): - # Connect to PostgreSQL (returns a Pool) pool = await PostPyro.connect("postgresql://user:pass@localhost/db") - # Query data - users = await pool.query("SELECT * FROM users WHERE active = $1", [True]) + users = await pool.query( + "SELECT * FROM users WHERE active = $1", [True] + ) for user in users: print(f"User: {user['name']} ({user['email']})") - # Execute updates - affected = await pool.execute( - "UPDATE users SET last_login = NOW() WHERE id = $1", - [user_id] - ) - - # Use transactions tx = await pool.transaction() async with tx: - await tx.execute("INSERT INTO orders (user_id) VALUES ($1)", [user_id]) + await tx.execute( + "INSERT INTO orders (user_id) VALUES ($1)", [user_id] + ) await tx.execute("UPDATE inventory SET stock = stock - 1") await pool.close() @@ -131,89 +122,93 @@

- -
+ +
-

Key Features

+
+

Key features

+

Everything the driver handles for you, out of the box.

+
-
🔥
-

Rust Core

-

- The driver itself is a PyO3 extension module - query execution and type - conversion happen in compiled Rust, not interpreted Python. -

+
+ +
+

Rust core

+

The driver is a PyO3 extension module — query execution and type conversion happen in compiled Rust, not interpreted Python.

-
🛡️
-

Memory Safe

-

- Rust's ownership system prevents memory leaks and segfaults. -

+
+ +
+

Memory safe

+

Rust's ownership system rules out the leaks and segfaults hand-rolled C bindings are prone to.

-
🌐
-

Broad Type Support

-

- Booleans, integers, floats, numeric, text, dates/times, UUIDs, and JSON/JSONB - convert automatically. See the Type System reference below for the full list. -

+
+ +
+

Broad type support

+

Booleans, numerics, text, dates/times, UUIDs, Decimal, JSON/JSONB, BYTEA, and arrays convert automatically — see the type reference.

-
⚡
-

Fully Async

-

- Every I/O method is async def, built on sqlx's connection pooling - and Tokio runtime, and releases the GIL while waiting on Postgres. -

+
+ +
+

Fully async

+

Every I/O method is async def, built on sqlx's pooling and the Tokio runtime, releasing the GIL while waiting on Postgres.

-
🔒
-

Type Conversion

-

- Postgres values decode to native Python types automatically. Bound parameters other than - bool/int/float/str/None need - an explicit ::type cast in the SQL text - see the Type System reference below. -

+
+ +
+

Native parameter binding

+

datetime, uuid.UUID, Decimal, dict, homogeneous list/tuple (as a real array), and bytes bind directly — no manual ::type cast in the SQL text.

-
🎯
-

DB-API 2.0 Compliant

-

- Standard Python database interface ensures compatibility with existing code. -

+
+ +
+

DB-API 2.0 exceptions

+

Standard exception hierarchy (IntegrityError, OperationalError, ...) mapped from Postgres SQLSTATE codes.

- -
+ +
-

Installation

-
-

⚠️ Pre-release

-

- This page documents the async 2.0.0-dev API (python/PostPyro/__init__.pyi - in the repo). It hasn't been published to PyPI yet - pip install PostPyro - currently installs the older, synchronous 1.0.0 driver, which does not have this API. - Build from source (below) to use the async driver described here. -

+
+

Installation

-
-
-

📦 Install from PyPI (1.0.0, sync - not this API)

+ +
+ +
+

Pre-release

+

This page documents the async 2.0.0-dev API (python/PostPyro/__init__.pyi in the repo). It hasn't shipped to PyPI yet — pip install PostPyro currently installs the older, synchronous 1.0.0 driver, which doesn't have this API. Build from source below to use the async driver described here.

+
+
+ +
+
+

+ + From PyPI (1.0.0, sync — not this API) +

pip install PostPyro
-
-
-

🔧 Build from Source

+
+

+ + Build from source (async, this API) +

# Install Rust
 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
@@ -222,20 +217,16 @@ 

🔧 Build from Source

git clone https://github.com/magi8101/PostPyro.git cd PostPyro pip install -e .
-
+

Requirements

    @@ -247,28 +238,26 @@

    Requirements

- -
+ +
-

Quick Start

-
-
- - - - -
- -
-
-
import asyncio
+                
+

Quick start

+
+
+ + + + +
+ +
+
import asyncio
 import PostPyro
 
 async def main():
-    # Connect to database (returns a Pool)
     pool = await PostPyro.connect("postgresql://user:password@localhost:5432/mydb")
 
-    # Create a table
     await pool.execute("""
         CREATE TABLE users (
             id SERIAL PRIMARY KEY,
@@ -278,840 +267,445 @@ 

Quick Start

) """) - # Insert data affected = await pool.execute( "INSERT INTO users (name, email, age) VALUES ($1, $2, $3)", ["Alice", "alice@example.com", 30] ) print(f"Inserted {affected} rows") - # Close the pool await pool.close() asyncio.run(main())
-
-
+
-
-
-
# Query multiple rows
+                
+
# Query multiple rows
 users = await pool.query("SELECT * FROM users WHERE age > $1", [25])
 for user in users:
     print(f"ID: {user['id']}, Name: {user['name']}, Age: {user['age']}")
 
-# Query single row (raises ProgrammingError on zero matches;
-# extra matches beyond the first are silently discarded)
+# Query a single row - raises ProgrammingError on zero matches;
+# extra matches beyond the first are silently discarded
 user = await pool.query_one("SELECT * FROM users WHERE id = $1", [1])
 print(f"Found user: {user['name']}")
 
 # There's no execute_batch() - run each statement individually
-names_and_ages = [("Bob", 25), ("Charlie", 35), ("Diana", 28)]
-inserted = 0
-for name, age in names_and_ages:
-    inserted += await pool.execute(
+for name, age in [("Bob", 25), ("Charlie", 35), ("Diana", 28)]:
+    await pool.execute(
         "INSERT INTO users (name, age) VALUES ($1, $2)", [name, age]
     )
-print(f"Inserted {inserted} rows")
 
-# Pool status check
 if not pool.is_closed():
-    print("✅ Pool is open")
-
-
+ print("Pool is open")