You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
py-v1.15.0 - Catch risky Alembic migrations before they lock a table
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
migration-risk reads Alembic revisions (preview). SQLAlchemy projects
without Django (FastAPI, Flask) get the same static migration review: any versions/ revision is parsed with ast and never run. Eleven rules cover
a NOT NULL add_column without server_default (SQLAlchemy's default= only
applies on INSERT), create_index without CONCURRENTLY, alter_column to NOT
NULL or to a new type, renamed and dropped columns and tables, unique,
foreign-key and check constraints, and multiple heads. batch_alter_table
blocks and Flask-Migrate layouts are covered. The populated-table rules
learned from the Django census apply too: a same-revision create_table or
the base revision means an empty table, and a data operation first makes the
finding a warning. Over ~1170 revisions from Superset, Airflow, Prefect,
Polar, Dispatch, Mealie and the FastAPI template there are 9 critical
findings, each a NOT NULL column that fails on a populated table. Rules: docs/rules/alembic.md.
Fixed
signals / MCP signal_graph missed receivers registered with .connect(). Only @receiver(...) decorators were read, so post_delete.connect(delete_file, sender=Attachment) in an AppConfig.ready()
was invisible, and cascade left it out of the DB_CASCADE skipped_receivers list. signal.connect(handler, sender=Model) is now an
edge like a decorator, with a bare or dotted signal (post_delete, models.signals.post_delete), receiver= as a keyword, and no sender
meaning every model. To keep sockets and database clients out, a .connect()
call only counts when the signal is a Django built-in, a Signal() defined in
the workspace, or the call passes sender=.
(#183) Contributed by @1AbdullahButt.
nplusone read assignments and instance methods as relation reads.
Two more patterns from the real-world census:
device.cluster = cluster inside a loop (NetBox) assigns the attribute
and loads nothing. device.cluster.name = x still loads device.cluster, so that one is still reported.
check.matches_tag_set(tags) (Healthchecks) is a method on the loop
variable, but its name matched the <model>_set reverse-manager
heuristic. A call made directly on the loop variable is never a
relation read; managers are always one attribute deeper.