Skip to content

Track imports in stripped function bodies - #22298

Open
bniladridas wants to merge 4 commits into
python:masterfrom
bniladridas:fix/track-imports-stripped-function-bodies
Open

bniladridas wants to merge 4 commits into
python:masterfrom
bniladridas:fix/track-imports-stripped-function-bodies

Conversation

@bniladridas

@bniladridas bniladridas commented Oct 25, 2026 •

Copy link
Copy Markdown

Fixes #22280

When ignore_errors is set, mypy strips function bodies to speed up type checking. However, Import/ImportFrom nodes inside those bodies were never registered in tree.imports, so the imported modules were never tracked as dependencies and never type-checked.

The native parser deserializes tree.imports directly from raw data in load_from_raw(), which replaces the MypyFile object entirely. Any imports collected before deserialization are therefore lost. The fix restores stripped-body imports after load_from_raw() in both the threaded (BuildManager.parse_all) and non-threaded (State.parse_file) build paths, using Python's ast module to scan the source for imports inside function bodies.

A matching fix was already applied to the Python (fastparse) parser path via _collect_imports_from_stmts().

Verification

  • Native parser (default): pkg/b.py error correctly reported when imported inside a stripped function body
  • Python parser (--no-native-parser): same error correctly reported (fastparse path fix)
  • Without ignore_errors: no regression
  • ruff lint: clean
  • Test suite: 9,580 passed, 135 skipped, 7 xfailed across testcheck, testdeps, testparse, testfinegrained

Regression tests

Added two test cases to test-data/unit/check-ignore.test:

  • testIgnoreErrorsFollowsImportInsideStrippedFunctionBody (native parser)
  • testIgnoreErrorsFollowsImportInsideStrippedFunctionBodyNativeParser (--no-native-parser)

Both fail without the fix and pass with it.

@github-actions

This comment has been minimized.

When ignore_errors is set, mypy strips function bodies to speed up
type checking. However, Import/ImportFrom nodes inside those bodies
were never registered in tree.imports, so the imported modules were
never tracked as dependencies and never type-checked.

The native parser deserializes tree.imports directly from raw data in
load_from_raw(), which replaces the MypyFile object entirely. Any
imports collected before deserialization are therefore lost. The fix
restores stripped-body imports after load_from_raw() in both the
threaded (BuildManager.parse_all) and non-threaded
(State.parse_file) build paths, using Python's ast module to scan the
source for imports inside function bodies.

A matching fix was already applied to the Python (fastparse) parser
path via _collect_imports_from_stmts().

Adds regression tests for both parser configurations, verifying that
a module imported inside a stripped function body is still type-checked.

Fixes python#22280
@bniladridas
bniladridas force-pushed the fix/track-imports-stripped-function-bodies branch from 908ca4a to 135a306 Compare October 25, 2026 23:49
@github-actions

This comment has been minimized.

Change the stmts parameter from list[ast3.stmt] to Sequence[ast3.stmt]
to match the caller's type. The function only reads the sequence and
never mutates it, so Sequence is the correct type. This fixes a mypy
self-check failure reported in CI for PR python#22298.

Fixes python#22298

Signed-off-by: Niladri Das <125604915+bniladridas@users.noreply.github.com>
mypyc cannot compile nested class definitions, so defining
ImportCollector inside _restore_stripped_body_imports fails the
compiled build. The class uses no closure state, so hoisting it to
module scope is behavior preserving.

Signed-off-by: Niladri Das <125604915+bniladridas@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

pyodide (https://github.com/pyodide/pyodide)
- pyproject.toml: note: unused section(s): module = ['PIL.*', 'docutils.parsers.rst', 'loky', 'matplotlib.*', 'pytest_pyodide', 'pytest_pyodide.runner', 'pytest_pyodide.utils', 'termcolor', 'test', 'tomli', 'typer']
+ pyproject.toml: note: unused section(s): module = ['PIL.*', 'docutils.parsers.rst', 'loky', 'matplotlib.*', 'pytest_pyodide', 'pytest_pyodide.runner', 'pytest_pyodide.utils', 'termcolor', 'test', 'typer']

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ddtrace/contrib/internal/langchain/patch.py:312: error: Module has no attribute "output_parsers"  [attr-defined]

paasta (https://github.com/yelp/paasta)
+ <unknown>:227: SyntaxWarning: "\&" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\&"? A raw string is also an option.

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.

[2.4 regression] modules imported inside a method of an ignore_errors module aren't checked anymore

1 participant