Skip to main content

Rails annotate_models for modern SQLAlchemy 2.x + Alembic projects.

Project description

sqlalchemy-annotate

Rails annotate_models for modern SQLAlchemy 2.x + Alembic projects.

It keeps a Schema Information comment block at the top of every model file, in sync with your actual SQLAlchemy metadata, so reviewers and editors see the table shape without opening a database or a migration.

# == 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

class User(Base):
    __tablename__ = "users"
    ...

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 generate twice produces no diff, which is what makes check reliable 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"
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.0
    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

License

MIT (c) 2026 Abhinav Saxena

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

sqlalchemy_annotate-0.1.0.tar.gz (21.4 kB view details)

Uploaded Source

Built Distribution

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

sqlalchemy_annotate-0.1.0-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file sqlalchemy_annotate-0.1.0.tar.gz.

File metadata

  • Download URL: sqlalchemy_annotate-0.1.0.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for sqlalchemy_annotate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 424b688bc12f84ca2e09937b53b3150d551922080982ca0e9cd89ec02ed2de2c
MD5 d1bb39d4c7a39fa60c05f9c3a27d4fb5
BLAKE2b-256 c450bffe165511b2f616ebfba624f7d48ddb0a5a31479f6a190e3477b57300b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlalchemy_annotate-0.1.0.tar.gz:

Publisher: release.yml on abhinavs/sqlalchemy-annotate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlalchemy_annotate-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for sqlalchemy_annotate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c358499b9f048a10aed8fab8c1774a63c0ebe99a949db20ef0830ae9f92e6698
MD5 02cae163bfd54b5a2e7066fd93a1677c
BLAKE2b-256 2900bdc2841447216513205f710383a4553f59f43f663fedc325ffc68f9c5e76

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlalchemy_annotate-0.1.0-py3-none-any.whl:

Publisher: release.yml on abhinavs/sqlalchemy-annotate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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