Commit fed05e2
fix(parsers/python): extract block-scoped def/class units
The extractor recursed only into `FunctionDef`/`ClassDef` bodies, never into
block statements, so a `def`/`class` inside an `if`/`elif`/`else`, `try`/
`except`/`finally`, `for`/`while`, `with`, or `match`/`case` block was dropped
from both the inventory and the call graph (call_graph_builder consumes the same
`functions` dict — no independent traversal). A `def` under `if
sys.version_info >= ...`, a `try/except ImportError` fallback, a `with`-guarded
handler, or a class-based-view `if/else` dispatcher was never a unit, never a
reachability node, and its body — including any sink — leaked verbatim into the
synthetic `:__module__` unit (also producing spurious `__module__ -> callee`
edges).
Add `_descend_into_blocks`: walk block-container bodies (incl. except/finally
arms and match cases) at ALL depths and hand any def/class to the existing tree
helpers. Descend ONLY into block nodes — direct `FunctionDef`/`ClassDef` children
are emitted by the caller, so the node sets are disjoint (no double-processing).
Wired into `process_file`, `_process_function_tree`, and `_process_class_tree`.
This matches Python's existing baseline (function-nested defs are already units)
and reuses the existing keep-both (`#L<line>`) machinery, so sibling-block
same-name defs and block-vs-top-level same-name defs both survive. Closes the
`:__module__` leak by covering every def/class span (`ast.walk`, not just
top-level children) in `extract_module_level_code`.
Also adds the canonical per-parser test-infra files (absent for Python until
now): `tests/parsers/python/test_callgraph_symmetry.py` and
`tests/parsers/python/test_python_schema_completeness.py`.
Tests: tests/parsers/python/test_block_scoped_defs.py (if/else/try/except/
finally/for/while/with/match shapes; async + decorated; class-in-block; function-
internal block; sibling keep-both; block-vs-top-level keep-both; no __module__
leak):
$ pytest tests/parsers/python/test_block_scoped_defs.py -q
15 passed
$ pytest tests/parsers/python/ -q
64 passed
Full repo suite: 636 passed, 22 skipped. Flask: both views.py `view`
dispatchers surfaced (view + view#L115, keep-both); all 6 globals.py
`if TYPE_CHECKING:` classes surfaced.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>1 parent ad17f3f commit fed05e2
4 files changed
Lines changed: 530 additions & 45 deletions
File tree
- libs/openant-core
- parsers/python
- tests/parsers/python
0 commit comments