sqlalchemy-annotate
Rails annotate_models for modern SQLAlchemy 2.x + Alembic projects.
It keeps a Schema Information comment block alongside every model class, in
sync with your actual SQLAlchemy metadata, so reviewers and editors see the
table shape without opening a database or a migration. The block sits below
the class by default; flip position = "top" to put it above instead.
class User(Base):
__tablename__ = "users"
...
# == Schema Information
#
# Table name: users
#
# id : integer, primary key
# email : varchar, not null
# created_at : timestamp
#
# Indexes
# ix_users_email (email) UNIQUE
#
# Foreign Keys
# profile_id -> profiles.id (ON DELETE CASCADE)
#
# == End Schema Information
Why it is safe
- No database required. Everything is read from
Base.metadata/__table__. A connection URL, if you pass one, is parsed only for its dialect so types compile correctly offline. No engine is ever connected. - No regex rewriting. Files are edited through a single libcst transformer. Imports, comments, blank lines and Black formatting are preserved byte-for-byte; only the region between the markers is touched.
- Idempotent. Running
generatetwice produces no diff, which is what makescheckreliable in CI and pre-commit.
Install
pip install sqlalchemy-annotate # or: uv pip install sqlalchemy-annotate
Usage
sqlalchemy-annotate generate
sqlalchemy-annotate generate --models app.models --engine postgresql://localhost/db
sqlalchemy-annotate check # exit 1 if stale (CI)
sqlalchemy-annotate remove # strip every block
sqlalchemy-annotate generate --dry-run # show the diff, write nothing
--models points at the dotted package path. Subpackages are imported
recursively (app/models/user.py, app/models/post.py, ...), so every model
registers regardless of how many files you split them across. A broken or
circularly-importing module is reported as a warning and skipped, never fatal.
Configuration
Put defaults in pyproject.toml; CLI flags override them.
[tool.sqlalchemy-annotate]
models = "app.models"
engine = "postgresql+asyncpg://..." # dialect only, never connected
include_indexes = true
include_foreign_keys = true
include_relationships = false
normalize_types = false # true -> Rails-style (bigint, varchar(255))
sort = "definition" # or "alphabetical"
position = "bottom" # or "top" to place the block above the class
exclude = ["audit_*", "*_history"]
Type rendering
By default types are truthful: rendered exactly as SQLAlchemy compiles
them for the resolved dialect (Mapped[int] PK -> integer, Mapped[str] ->
varchar). Set normalize_types = true for Rails-style fabrication
(autoincrement int PK -> bigint, unlengthed string -> varchar(255)).
The dialect is --dialect if given, else the backend of engine
(async drivers collapse to their sync backend), else PostgreSQL.
Alembic / pre-commit
Run it right after a schema change:
alembic revision --autogenerate -m "add users"
alembic upgrade head
sqlalchemy-annotate generate
.pre-commit-config.yaml:
repos:
- repo: https://github.com/abhinavs/sqlalchemy-annotate
rev: v0.1.1
hooks:
- id: sqlalchemy-annotate
CI gate:
- run: sqlalchemy-annotate check
How it works
| Module | Responsibility |
|---|---|
discovery.py |
import-walk the package, collect mapped classes -> files |
schema.py |
Table/Mapper -> ModelSchema value objects (pure) |
formatter.py |
ModelSchema -> comment lines (pure, pluggable renderer) |
parser.py |
libcst helpers: build lines, locate the existing block |
writer.py |
one libcst transformer: insert / replace / remove |
config.py |
defaults <- pyproject.toml <- CLI |
cli.py |
typer commands; heavy imports stay lazy |
Roadmap (designed, not built)
The formatter is already a swappable Renderer behind the
sqlalchemy_annotate.renderers entry-point group, leaving room for: Markdown
schema export, ER diagram generation, a schema diff command, watch mode, and
an IDE/VSCode integration.
Project
- Contributing - dev setup, the idempotency invariant, release steps
- Changelog
- Security policy - note: the tool imports your model package, like
pytest/alembic - Ships with PEP 561 type information (
py.typed)
License
MIT (c) 2026 Abhinav Saxena
Metadata
Release files for sqlalchemy-annotate 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlalchemy_annotate-0.1.1.tar.gz | 23.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlalchemy_annotate-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.1 kB
Release files / sqlalchemy_annotate-0.1.1.tar.gz
| Download URL | sqlalchemy_annotate-0.1.1.tar.gz |
|---|---|
| Size | 23.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d1695360994fd04e3886a2f798e76bf1c143e35022241246035fd295e1d8e959
|
|
BLAKE2b-256 checksum How to use checksums |
7238d6b346e481bf1ffec781ebe040fcdee050b2b51c1316044c76e29fa8f564
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jun 3, 2026.
Transparency logRelease files / sqlalchemy_annotate-0.1.1-py3-none-any.whl
| Download URL | sqlalchemy_annotate-0.1.1-py3-none-any.whl |
|---|---|
| Size | 20.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
009b422d65ae111101c84d926592a3c11290cef5d7498982524e3ebd22e50631
|
|
BLAKE2b-256 checksum How to use checksums |
c3d644023b3dfa729a9557abfc004da002c0821c8da59abb803e8aa21f0f7aa4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jun 3, 2026.
Transparency log