Validation-first SQLAlchemy queries with Pydantic row validation.
Project description
RowGuard
RowGuard makes every SQLAlchemy query return validated Pydantic models—or explicit rejected rows. It does not silently drop bad data.
Use it when you already have SQLAlchemy Core tables/selects and need typed reads with deterministic rejection handling. It is not an ORM and does not replace SQLAlchemy or Pydantic.
Status
Current release: 0.4.0 (sync + async Core, streaming). See Supported vs planned for what is shipped versus deferred (ORM 0.5, callback/quarantine 0.6).
Install
pip install rowguard
Requires Python 3.10+, Pydantic v2, SQLAlchemy 2.x, and SQLRules ≥0.4. See the installation guide.
Quickstart
Full walkthrough: Quickstart.
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:
# Disable SQLRules pushdown so invalid rows reach Pydantic and appear in rejected.
result = rowguard.select(
session=session,
table=users,
model=UserRead,
on_reject="collect",
use_sqlrules=False,
)
print(result.models)
print(result.rejected)
with rowguard.stream(
session=session,
table=users,
model=UserRead,
on_reject="skip",
use_sqlrules=False,
) as stream:
for model in stream:
print(model)
With use_sqlrules=True (the default), supported constraints such as age >= 18
are pushed into SQL, so invalid candidate rows may never be returned. See
SQLRules pushdown
and the FAQ.
Public API (0.4.0)
Full reference: API guide · Python autodoc · Error catalog.
| Function | Purpose |
|---|---|
select(...) |
Build and execute a table query with validation |
execute(...) |
Validate rows from an existing Select |
validate_rows(...) |
Validate mappings without SQL |
compile_plan(...) |
Compile an ExecutionPlan without executing |
stream(...) |
Stream validated models without buffering accepted rows |
aselect(...) |
Async select for AsyncSession / AsyncConnection |
aexecute(...) |
Async execute for AsyncSession / AsyncConnection |
astream(...) |
Async stream (AsyncStreamResult) without buffering accepted rows |
Rejection policies: raise (default), collect, skip — see
rejection policies.
Optional planning knobs: compiled_rules= (precompiled SQLRules), strict=
(Pydantic), field_map= / column_map= (validated at plan time).
Streaming knobs: yield_per=, observers= (StreamObserver / BaseStreamObserver).
Observers remain sync callables in 0.4. See the
streaming guide.
Async note: only DB I/O is awaited. Pydantic validation runs on the event loop;
heavy models can block. Prefer async with rowguard.astream(...) for cleanup.
Install async extras with pip install rowguard[async]. Details:
async guide.
Architecture
Pydantic Model
│
▼
SQLRules
│
▼
SQLAlchemy Query
│
▼
Database
│
▼
Row Adapter
│
▼
Pydantic Validation
│
├── Accepted Model
└── Rejected Row
More detail: architecture overview · design philosophy · specification.
Documentation
Build docs locally:
pip install -e ".[docs]"
make docs
# open docs/_build/html/index.html
Development
See Contributing, Security, and Releasing.
pip install -e ".[dev,async]"
make all # ruff + mypy + pytest --cov
python examples/basic.py
python examples/streaming.py
python examples/async_basic.py
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
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 rowguard-0.4.0.tar.gz.
File metadata
- Download URL: rowguard-0.4.0.tar.gz
- Upload date:
- Size: 219.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5965b20a0053a7e83c67b79e25304fc9ae8fa5d8c5b6000085cf0a814e2fc425
|
|
| MD5 |
96d55b7a663e19b28832c80f3effde78
|
|
| BLAKE2b-256 |
f1a38ea2f8e534f473b5f674e091a06e09ad4f0bed9dac08f2582904aef46f6f
|
File details
Details for the file rowguard-0.4.0-py3-none-any.whl.
File metadata
- Download URL: rowguard-0.4.0-py3-none-any.whl
- Upload date:
- Size: 36.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a48283e572a6c6d6c97afa5a5a3c44cc4bf09f9365d0c4895a6faa492140a67c
|
|
| MD5 |
0d03db3092d8611b655e513741c13f8b
|
|
| BLAKE2b-256 |
e08cd21fd04b5f059a235941208691891fc613b93aa3ad5a7854c6362234d5ca
|