py-v1.10.0 - drift reported a false blocking failure when two apps shared a name
Three defects found by running the CLI over real checkouts of django-oscar,
django-guardian, django-allauth and django-cms rather than over fixtures. Each
one was invisible to a green test suite, and two of them made the tool answer
confidently with something false.
Fixed
-
driftreported a false blocking failure when two apps shared a name.
The third finding from the same real-world run, on django-guardian, which
ships bothexample_project/coreandexample_project_custom_group/core.
The parser labels an app by its directory, so the declared side merged the
two; the migration side replayed each directory on its own. One project's
migrations were therefore compared against both projects' models, and the
report contradicted itself —core.customgroupcame out as "declared, but
no migration creates it" and "migrated but no longer declared", with
core.customuserprinted twice. The first of those is a blocking verdict,
so anyone runningdriftin CI over a repo with two same-named app
directories — every monorepo — could have a build failed by a model that was
migrated perfectly well. Replayed state is now merged per app name, matching
how the declared side is already keyed. On the real guardian checkout the
blocking count goes from 1 to 0 and the duplicate row disappears, while a
genuinely unmigrated field still blocks. -
Abstract bases in
abstract_models.pywere never read. The other half of
the same django-oscar run: once its models were found, 72 of the 83 had zero
fields between them, because every pluggable framework keeps the abstract
base inabstract_models.pyand leavesmodels.pyholding only the
concrete subclass. A model reported with no columns reads as a schema that
lost them, which is a worse answer than admitting the file was not read.
The workspace walk now takesabstract_models.pyalongsidemodels.py;
the bases are still dropped from the results, they only become available for
inheritance. oscar goes from 72 empty models to 8 — and those 8 are correct:
they subclass concrete models, where multi-table inheritance leaves the
columns on the parent's table. -
Models declared inside a module-level block were invisible. Running the
CLI over a real django-oscar checkout — not a fixture — showed 12 models for
the whole framework, and every one of them came from oscar'stests/
directory: 21 of its 22 appmodels.pyfiles parsed to nothing. Catalogue,
order, offer, partner, payment, shipping, voucher, customer, address,
analytics, reviews and wishlists were all missing. Reporting a project's
test fixtures as its schema is worse than reporting none of it.
The cause is the swappable-model idiom every pluggable Django framework
uses —if not is_model_registered(...):and then an indentedclass—
against class discovery anchored on^class. A class is now matched on its
dedented view and accepted when everything enclosing it is a block statement
(if,try,with,for); adeforclassoutwards still rejects it,
soMeta, nested helpers and factory-local models stay out. django-oscar
now yields 82 models. All six golden snapshots are byte-identical: a
column-0 class parses exactly as before.
Added
- A sixth golden fixture: Read the Docs. The vendored
projects/models.py,
its generated snapshot, reproducible fetch metadata and MIT attribution, next
to Zulip, Saleor, Wagtail, django-CMS and Mezzanine — 16 models and 142
fields more, putting the parser under 75 models and 537 fields of real-world
Django. Verified byte-identical to upstream blobcf1e913dbefore merge:
fixtures are parser input for byte-stable snapshots, so an edited copy
would quietly rewrite what those snapshots assert. Contributed by
@JJordan0C in
#62, closing
#51.