Skip to main content

Fail-fast schema validation for SQLAlchemy — Hibernate's ddl-auto=validate for Python.

Project description

ormguard

Fail-fast schema validation for SQLAlchemy. Bring Hibernate's hibernate.ddl-auto=validate to Python: at startup, reflect the connected database and verify it matches your ORM entities. Catch entity↔DB drift at boot time instead of as a runtime column does not exist error.

Quickstart (60 seconds)

pip install ormguard
python -m ormguard --selfcheck   # see it catch drift against in-memory SQLite — no DB, no setup

Guard your app at boot so it refuses to start on drift (FastAPI):

from ormguard.integrations.fastapi import schema_guard_lifespan

app = FastAPI(lifespan=schema_guard_lifespan(engine, Base, strict=True))

...or fail CI before you ship (exit code 1 on ERROR findings):

python -m ormguard --url "$DATABASE_URL" --metadata myapp.db:Base

That's it. Details and every mode are below.

The problem

In JPA/Hibernate, ddl-auto=validate checks every entity against the live schema when the app starts and refuses to boot on a mismatch. SQLAlchemy has no equivalent — create_all() only creates missing tables, nothing validates existing ones. So an entity can map a column the database doesn't have (or ignore a column it does have) and the server starts perfectly fine… until the request that touches that column blows up in production.

alembic check and Atlas help, but both compare against migration metadata / a live DB through autogenerate and run in CI. ormguard checks the actual database the app is about to use, at the moment it boots.

Install

pip install ormguard   # (POC: install from source — see below)

Try it in one line (no DB, no host project)

python -m ormguard --selfcheck

Spins up an in-memory SQLite database with deliberate drift and prints exactly what ormguard catches — a zero-setup way to see it work or sanity-check an install.

Docs

  • docs/DESIGN.md — problem, the Hibernate validate analogy, why existing tools don't fit, architecture, roadmap.
  • docs/USAGE.md — every usage mode with examples.
  • docs/V2_OFFLINE_REPLAY.md — spec for the offline multi-tenant Alembic replay mode (the differentiator).

Use it

As a startup guard (FastAPI)

from ormguard.integrations.fastapi import schema_guard_lifespan

app = FastAPI(lifespan=schema_guard_lifespan(engine, Base, strict=True))
# strict=True -> app refuses to start on ERROR-level drift

As a function (any framework)

from ormguard import assert_schema, validate

assert_schema(engine, Base, strict=True)      # raise on drift

report = validate(engine, Base)               # or inspect findings yourself
if not report.ok:
    print(report.format_text())

In CI

python -m ormguard --url "$DATABASE_URL" --metadata myapp.db:Base --schema aivelabs_sv
# exit code 1 on ERROR findings

Or drop in the GitHub Action — no install step needed:

- uses: gogo1414/ormguard@v1
  with:
    database-url: ${{ secrets.DATABASE_URL }}
    metadata: myapp.db:Base
    args: --schema public --check-indexes   # optional

(Until ormguard is on PyPI, set version: to a VCS URL, e.g. version: git+https://github.com/gogo1414/ormguard@main.)

Get a team-channel ping when drift is found — one webhook works for Slack or Discord (add --notify-on any to include warnings):

python -m ormguard --url "$DATABASE_URL" --metadata myapp.db:Base \
  --notify-webhook "$SLACK_OR_DISCORD_WEBHOOK"
- uses: gogo1414/ormguard@v1
  with:
    database-url: ${{ secrets.DATABASE_URL }}
    metadata: myapp.db:Base
    notify-webhook: ${{ secrets.SLACK_WEBHOOK }}

Multi-tenant (one ORM, many databases)

from ormguard import validate_many, format_matrix

reports = validate_many({"larosee": e1, "hmall": e2, "cafe24": e3}, Base)
print(format_matrix(reports))

What it checks (v1)

Finding Default severity Meaning
table_missing ERROR entity declares a table the DB lacks
column_missing ERROR entity maps a column the DB lacks (the crash case)
column_extra WARN DB column not mapped by any entity (silently unused)
nullable_mismatch WARN NOT NULL / NULL disagreement
type_mismatch WARN (opt-in) column type differs — off by default (dialect-dependent)
index_missing WARN (opt-in) ORM declares an index the DB lacks — off by default
index_extra WARN (opt-in) DB has an index not declared in the ORM — off by default
fk_missing WARN (opt-in) ORM declares a foreign key the DB lacks — off by default
fk_extra WARN (opt-in) DB has a foreign key not declared in the ORM — off by default
default_missing WARN (opt-in) ORM sets a server_default the DB column lacks — off by default
default_extra WARN (opt-in) DB column has a default the ORM doesn't declare — off by default

Configurable via Config: restrict schemas, ignore tables/columns, flip severities, toggle nullable/type/index/foreign-key/default/extra checks.

Index checks (--check-indexes / Config(check_indexes=True)) compare by column set and uniqueness — not by name — and skip indexes that merely back a primary key or unique constraint. Foreign-key checks (--check-foreign-keys) compare by local columns, referred table, and referred columns. Server-default checks (--check-defaults) compare only whether a DB default exists — not its value, which is too dialect-dependent — and skip primary keys. All keep false positives low.

Out of scope for v1 (planned): check constraints, enums, and an offline multi-tenant Alembic replay mode that diffs ORM against the schema migrations would produce per tenant — without a database.

Develop

pip install -e ".[dev]"
pytest        # runs against in-memory SQLite, no external DB needed

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

ormguard-0.1.0.tar.gz (37.8 kB view details)

Uploaded Source

Built Distribution

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

ormguard-0.1.0-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for ormguard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4414130b17d3ede560afe777a74dc0114e7600c422993dacab590d7901479203
MD5 a892af069b65c1387be18ba546117f91
BLAKE2b-256 1a0d77de1c858cf2b676e18986b34866822157ad8f1da8ae613781842cf706fb

See more details on using hashes here.

Provenance

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

Publisher: release.yml on gogo1414/ormguard

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

File details

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

File metadata

  • Download URL: ormguard-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ormguard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8c4f67452a10f0e87aae3e068dff92a96a1f8f13700c08628240b336cfb021a8
MD5 b4f36209e6fed0a8a7d65b7fe849e48d
BLAKE2b-256 dc018dadf7e3d3b0c18805ffd89ffd03a002e46634d7f7097b722e037e4b5ba2

See more details on using hashes here.

Provenance

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

Publisher: release.yml on gogo1414/ormguard

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