Skip to main content

Python bindings for rbatis ORM - high-performance async ORM

Project description

rbatis-py

Python async database client powered by rbdc (Rust Database Connectivity) — a high-performance async database connectivity layer.

Supports SQLite, MySQL, PostgreSQL, MSSQL, Turso/libSQL, and DuckDB.

Installation

# https://pypi.org/project/rbatis-py/
pip install rbatis-py

Requires Python ≥ 3.8.

Quick Start

Raw SQL

import asyncio
from rbatis_py import RBatis

async def main():
    db = RBatis()
    await db.link("sqlite:///path/to/test.db")

    # CREATE / INSERT / UPDATE / DELETE
    await db.exec("CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
    await db.exec("INSERT INTO user (name, age) VALUES (?, ?)", ["Alice", 30])

    # Query — returns List[Dict]
    rows = await db.exec_decode("SELECT * FROM user")
    for row in rows:
        print(row["name"], row["age"])

    # Transaction (auto commit/rollback)
    tx = await db.begin()
    async with tx.auto_commit() as g:
        await g.exec("UPDATE user SET age = ? WHERE name = ?", [31, "Alice"])

asyncio.run(main())

CRUD via Model

from rbatis_py import RBatis, Model

class User(Model):
    __table__ = "user"
    id: int | None = None
    name: str | None = None
    age: int | None = None

async def main():
    db = RBatis()
    await db.link("sqlite:///path/to/test.db")

    await User.insert(db, {"name": "Alice", "age": 30})
    await User.insert_batch(db, [
        {"name": "Bob", "age": 25},
        {"name": "Charlie", "age": 35},
    ], batch_size=1000)
    rows = await User.select_by_map(db, {"name": "Alice"})
    affected = await User.update_by_map(db, {"age": 31}, {"name": "Alice"})
    affected = await User.delete_by_map(db, {"name": "Bob"})

API

RBatis

Method Description
exec(sql, params) Execute INSERT/UPDATE/DELETE
exec_decode(sql, params) Query returns List[Dict]
insert(table, data) Insert one row
insert_batch(table, data_list, batch_size=None) Batch insert (chunked by batch_size)
select_by_map(table, condition) Select by condition
update_by_map(table, data, condition) Update by condition
delete_by_map(table, condition) Delete by condition
link(url) Connect to database
acquire() Acquire a raw connection from pool
begin() Begin transaction (explicit, returns Transaction)
begin_defer() Begin transaction with auto-commit guard
commit() Commit current active transaction
rollback() Rollback current active transaction
ping() Test connection
close() Close connection
set_pool_max_size(n) Set max connections in the pool
set_pool_max_idle(n) Set max idle connections in the pool
set_pool_connect_timeout(s) Set connection timeout (seconds)
set_pool_max_lifetime(s) Set max connection lifetime (seconds)
pool_state() Inspect pool state (returns dict)

Transaction

Method Description
exec(sql, params) Execute SQL on the transaction connection
exec_decode(sql, params) Query returns List[Dict]
commit() Commit the transaction
rollback() Rollback the transaction
auto_commit() Return an AutoCommitGuard context manager

AutoCommitGuard

Method Description
exec(sql, params) Execute SQL on the transaction connection
exec_decode(sql, params) Query returns List[Dict]
commit() Commit explicitly (guard no-ops on exit)
rollback() Rollback explicitly (guard no-ops on exit)

Created by Transaction.auto_commit() or RBatis.begin_defer(). On context exit:

  • No exception → auto-commit
  • Exception → auto-rollback

If commit() or rollback() is called explicitly inside the block, the guard does nothing on exit.

Connection

Method Description
exec(sql, params) Execute SQL on this connection
exec_decode(sql, params) Query returns List[Dict]
begin() Begin a transaction (returns Transaction)
close() Release connection back to the pool (not async)

Transaction Usage

Three transaction modes are supported:

A) Explicit (manual commit/rollback):

tx = await db.begin()
try:
    await tx.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
    await tx.commit()
except Exception:
    await tx.rollback()
    raise

B) Auto-commit via auto_commit():

tx = await db.begin()
async with tx.auto_commit() as g:
    await g.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
    # auto-commit on success, auto-rollback on exception

If you call await g.commit() or await g.rollback() explicitly inside the block, the guard will no-op on exit:

tx = await db.begin()
async with tx.auto_commit() as g:
    await g.exec("INSERT ...")
    await g.commit()   # explicit commit — guard does nothing on exit

C) One-liner via begin_defer():

async with db.begin_defer() as g:
    await g.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
    # auto-commit on success, auto-rollback on exception

Connection

Acquire a raw connection from the pool to run queries in isolation:

conn = await db.acquire()
try:
    rows = await conn.exec_decode("SELECT * FROM user WHERE age > ?", [20])

    # You can also begin a transaction on this specific connection
    tx = await conn.begin()
    async with tx.auto_commit() as g:
        await g.exec("INSERT INTO user (name) VALUES (?)", ["Alice"])
finally:
    conn.close()  # return to pool (not async)

Model

Define a table model:

class User(Model):
    __table__ = "user"
    id: int | None = None
    name: str | None = None
    age: int | None = None

Then use classmethods for CRUD. The db parameter accepts RBatis, Connection, or Transaction — all support exec() / exec_decode():

await User.insert(db, {...})
await User.select_by_map(db, {condition})
await User.update_by_map(db, {set_data}, {condition})
await User.delete_by_map(db, {condition})

For example, with a raw connection:

conn = await db.acquire()
try:
    await User.insert(conn, {"name": "Alice"})
    rows = await User.select_by_map(conn, {"name": "Alice"})
finally:
    conn.close()

Type Conversion

Python types are automatically converted to/from rbdc extended types:

Python type Database type
datetime.datetime DateTime
datetime.date Date
datetime.time Time
decimal.Decimal Decimal
uuid.UUID Uuid
dict / list Json

Note: SQLite stores all types as TEXT; the conversion to Python types works for all databases. PostgreSQL returns timestamps as integer (milliseconds) rather than datetime objects.

Connection URLs

# SQLite
await db.link("sqlite:///path/to/db.sqlite")

# MySQL
await db.link("mysql://user:pass@localhost:3306/db")

# PostgreSQL
await db.link("postgres://user:pass@localhost:5432/db")

# MSSQL
await db.link("jdbc:sqlserver://localhost:1433;User=SA;Password=xxx;Database=db")

Connection Pool

Configure the connection pool after calling link(). These methods must be called after a connection is established.

await db.link("mysql://user:pass@localhost:3306/mydb")

# Configure pool
await db.set_pool_max_size(20)          # Max 20 connections
await db.set_pool_max_idle(5)           # Keep at most 5 idle connections
await db.set_pool_connect_timeout(30)   # Timeout waiting for a connection (seconds)
await db.set_pool_max_lifetime(3600)    # Max connection lifetime (seconds)

# Inspect pool state
state = await db.pool_state()
print(state)
# e.g. {"max_open": 20, "connections": 3, "in_use": 1, "idle": 2, "waits": 0, ...}

Development

git clone https://github.com/rbatis/rbatis-py.git
cd rbatis-py
pip install maturin
maturin develop  # build and install in current venv

Running examples

uv run python examples/basic_usage.py     # exec, exec_decode, transactions
uv run python examples/tx.py              # transaction patterns
uv run python examples/crud_usage.py      # Model CRUD

Publishing to PyPI

# Install maturin if not already installed
pip install maturin

# Build wheel
maturin build

# Publish to PyPI (requires PyPI account and API token)
maturin publish

# Or use the official GitHub Action:
# https://github.com/PyO3/maturin-action

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rbatis_py-0.1.4.tar.gz (49.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rbatis_py-0.1.4-cp311-abi3-macosx_11_0_arm64.whl (16.8 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

File details

Details for the file rbatis_py-0.1.4.tar.gz.

File metadata

  • Download URL: rbatis_py-0.1.4.tar.gz
  • Upload date:
  • Size: 49.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.13.2

File hashes

Hashes for rbatis_py-0.1.4.tar.gz
Algorithm Hash digest
SHA256 256fdbd3e8bdfaa94c2d3df460710554e1a90a3a50367637b06583203afec80e
MD5 0df2364a02fb338dfc44be1466dabba6
BLAKE2b-256 b6b754e41b7744cbb25403ea4758cd8b995ab8edc69e5bbc955efdc7a1f3d8fe

See more details on using hashes here.

File details

Details for the file rbatis_py-0.1.4-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rbatis_py-0.1.4-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7b132b854a92b1e74faad5b4260286c1a2155176a063cc70f72c786a48669d5e
MD5 9662ef029170998f74eeba7fba9da3ca
BLAKE2b-256 e03b24bc040b31bc7694e903f48aa7f6e8a8c9bccf6d263c71bc608cb35b5336

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page