Skip to content

plugins: tolerate synthesized methods when reprocessing attrs classes - #22291

Open
inchang-ing wants to merge 2 commits into
python:masterfrom
inchang-ing:fix/attrs-reprocess-order-method
Open

inchang-ing wants to merge 2 commits into
python:masterfrom
inchang-ing:fix/attrs-reprocess-order-method

Conversation

@inchang-ing

Copy link
Copy Markdown
Contributor

Description

The attrs plugin hooks run once per applied decorator, so a class with stacked decorators (e.g. @attrs.define on @attrs.frozen) is analyzed twice. When the class body declares a dunder that the plugin also synthesizes (here __lt__ with the default order=True), the second hook run found the FuncDef synthesized by the first run in the symbol table, and the Var assertion in _analyze_class crashed with an internal error.

Skip the plugin-synthesized FuncDef on reprocessing: the hook that follows re-synthesizes the method anyway.

Fixes #22290

Testing

  • New data-driven case testAttrsStackedDecoratorsWithAnnotatedOrderMethod (stacked decorators + annotated __lt__): crashes on master with AssertionError: FuncDef:5(...), passes with the change.
  • check-plugin-attrs.test suite: 140 passed.
  • mypy self-check on mypy/plugins/attrs.py: no new errors (the reported mypy/util.py import-untyped note is a pre-existing baseline item in this environment).

Disclosure

Assisted by AI coding tools (ZCode/GLM); I have reviewed and verified the change and can explain every line.

A dependency that is suppressed during graph loading (the importing
module cannot resolve it) and up-suppressed afterwards (another module
resolved it) is re-added by appending it to the end of the dependency
list. The order-sensitive freshness check in State.is_fresh() then sees
a changed list and marks the module stale on every run, even though the
dependency set is identical to the cached one.

After the up-suppress pass, restore the cached order when the current
dependency set is unchanged; a genuine dependency change keeps the new
order and the module is still rechecked.

Reproducer (from the issue):

    site/ns/sub/__init__.py   (namespace package, no top-level __init__)
    a.py: import ns.missing   (missing submodule -> suppressed)
    b.py: import ns.sub
    e.py: import b

    PYTHONPATH=site mypy -v a.py e.py   # run twice

On the second run, 'Scheduling SCC singleton (a) as inherently stale'
was always logged: a's cached dependency order is
[os, builtins, ns, abc, typing], but the suppress/add round trip
appends ns at the end. With this change the second run reports no
stale SCCs. The second run also used to lose the 'Skipping analyzing
"ns"' error that the first (and non-incremental) run reports, because
the spurious recheck resolved ns differently; restoring the order fixes
that output inconsistency as well.

Fixes python#22159.
The attrs plugin hooks run once per applied decorator, so a class with
stacked decorators (e.g. @attrs.define on @attrs.frozen) is analyzed
twice. When the class body declares a dunder that the plugin also
synthesizes (e.g. __lt__ with the default order=True), the second run
found the FuncDef synthesized by the first run in the symbol table and
the Var assertion in _analyze_class crashed with an internal error.

Skip the synthesized FuncDef on reprocessing: the hook that follows will
re-synthesize the method anyway.

Fixes python#22290
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

attrs plugin crashes with order comparison methods

1 participant