Skip to main content

RowGuard

CI PyPI Documentation Python Versions License: MIT

RowGuard turns SQLAlchemy query results into validated Pydantic models.

By default, SQLRules (a required dependency) pushes supported model constraints into SQL so invalid candidates are filtered before fetch. Every row that is fetched is either an accepted model or an explicit rejection—never ignored after fetch.

Need to inspect invalid rows in Python? Pass use_sqlrules=False and on_reject="collect".

Use it when you already have SQLAlchemy Core tables/selects or ORM / SQLModel mapped classes and need typed reads with deterministic rejection handling. It is not an ORM and does not replace SQLAlchemy, Pydantic, or SQLModel—it validates reads over those stacks.

Status

Current release: 0.6.0 (Core + async + ORM/SQLModel + rejection platform). See Supported vs planned for what is shipped versus deferred.

Install

pip install rowguard                 # Core (includes SQLRules)
pip install "rowguard[async]"        # aiosqlite + greenlet for async
pip install "rowguard[sqlmodel]"     # SQLModel table-source support
pip install "rowguard[postgresql]"   # psycopg driver helper
Extra When you need it
(none) Sync Core / ORM reads
async aselect / astream examples and async tests
sqlmodel SQLModel mapped classes as table= / source=
postgresql Optional PostgreSQL driver
dev Contributors: pytest, ruff, mypy, …
docs Sphinx documentation build

Requires Python 3.10+, Pydantic v2, SQLAlchemy 2.x, and SQLRules ≥1.0 (3.10–3.12 tested in CI; 3.13 untested). See the installation guide.

Quickstart

Full walkthrough: Quickstart.

1. Default path (library defaults)

Invalid candidates are filtered in SQL. rejected is empty.

from typing import Annotated

from pydantic import BaseModel, Field
from sqlalchemy import Column, Integer, MetaData, String, Table, create_engine
from sqlalchemy.orm import Session

import rowguard


class UserRead(BaseModel):
    id: int
    name: str
    age: Annotated[int, Field(ge=18)]


metadata = MetaData()
users = Table(
    "users",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String),
    Column("age", Integer),
)

engine = create_engine("sqlite+pysqlite:///:memory:")
metadata.create_all(engine)

with engine.begin() as connection:
    connection.execute(
        users.insert(),
        [
            {"id": 1, "name": "Ada", "age": 37},
            {"id": 2, "name": "Legacy", "age": 12},
        ],
    )

with Session(engine) as session:
    result = rowguard.select(session=session, table=users, model=UserRead)
    print(result.models)    # (UserRead(id=1, name='Ada', age=37),)
    print(result.rejected)  # ()

2. Inspect rejections in Python

with Session(engine) as session:
    result = rowguard.select(
        session=session,
        table=users,
        model=UserRead,
        on_reject="collect",      # default is "raise"
        use_sqlrules=False,       # default is True
    )
    print(result.models)    # Ada
    print(result.rejected)  # Legacy failed age >= 18

See SQLRules pushdown and the FAQ.

Public API (0.6.0)

Function Purpose
select / execute Buffered validated reads
stream Stream accepted models (no accepted-row buffer)
aselect / aexecute / astream Async counterparts
validate_rows Validate mappings without SQL
compile_plan Inspect an ExecutionPlan without executing

Rejection policies: raise (default), collect, skip, log, callback, quarantine — plus optional max_rejections / max_rejection_rate. See Rejection policies.

table= vs source=: use table= on select/stream (Core Table or mapped class). On execute with a projected Select, pass the mapped class as source=. Full parameter contracts: API guide · Python autodoc · Errors.

ORM / SQLModel guide · Streaming · Async · Performance · Upgrading.

Documentation

Start here → Installation → Quickstart.

Development

See Contributing and Security.

make install      # .[dev,async,sqlmodel] — matches CI
make all
python examples/sqlrules_default.py
python examples/basic.py

License

MIT

Metadata

Release files for rowguard 0.6.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 rowguard 0.6.0
File Size Uploaded
rowguard-0.6.0.tar.gz 260.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rowguard 0.6.0
File Interpreter ABI Platform
rowguard-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 316.4 kB

Release files / rowguard-0.6.0.tar.gz

Download URL rowguard-0.6.0.tar.gz
Size 260.9 kB
Tags Source
SHA-256 checksum
How to use checksums
6ff46a4bd006483a80f452efcf1c2bc58d579e1e2e4dca7529720b7ecb4edd27
BLAKE2b-256 checksum
How to use checksums
88181c825b6b14bb7eb50d7a5ff6a292e0799f4cf22bff497684c91f12262119
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / rowguard-0.6.0-py3-none-any.whl

Download URL rowguard-0.6.0-py3-none-any.whl
Size 55.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c3f30219fc13522d16ca160ee40ad1a62f62e64b5d83d94ff8f73b7352ddf54d
BLAKE2b-256 checksum
How to use checksums
23039d6703b24e334c4c4b1ed1b8b0957b348b8d23c1240962163f4692d64bf0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

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