Skip to content

fix(parser): extract Python module-level calls (closes #291)#292

Merged
Wolfvin merged 3 commits into
mainfrom
fix/issue-291-python-module-level-calls
Jul 14, 2026
Merged

fix(parser): extract Python module-level calls (closes #291)#292
Wolfvin merged 3 commits into
mainfrom
fix/issue-291-python-module-level-calls

Conversation

@Wolfvin

@Wolfvin Wolfvin commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Root cause

PythonParser._extract_references_impl gated call-edge emission behind elif node.type == 'call' and fn_id:. A call written as a bare top-level statement (e.g. setup_app()) is walked with fn_id is None (no enclosing def/class), so the branch was skipped entirely — no CALLS edge. Result: a function called only at module level got ref_count=0 / status=dead (false positive) and was invisible to trace --direction up. This is issue #219, already fixed for the TS/JS parsers, but never applied to the Python parser.

Pattern mirrored from #219

The TS/JS fix attributes module-top-level calls to a synthetic source id <file>:0:<module> with no corresponding graph_nodes row (graph_model.is_module_level_source_id() recognises it; _module_level_caller_entry renders it in trace-up). Applied the analogous change in python_parser.py:

  • Compute module_node_id = f"{file_path}:0:<module>" once.
  • Change elif node.type == 'call' and fn_id: -> elif node.type == 'call':, with source_id = fn_id if fn_id else module_node_id, and emit edges from=source_id.

Same synthetic-id format, no new format invented, no fake node created.

Fix location

scripts/parsers/python_parser.py_extract_references_impl (call-edge branch + new module_node_id).

Verification

Repro def setup_app / helper / caller + module-level setup_app() / caller():

Before: setup_app rc=0/dead, caller rc=0/dead (both wrong); only edge caller->helper.
After (parser output):

{'from': 'foo.py:3', 'to_fn': 'helper'}
{'from': 'foo.py:0:<module>', 'to_fn': 'setup_app'}
{'from': 'foo.py:0:<module>', 'to_fn': 'caller'}

Full scan: setup_app rc=1, caller rc=1.

Anti-pollution (#223 constraint): SELECT COUNT(*) FROM graph_nodes WHERE name='<module>' = 0; backend.json has no <module> node.

No regression: helper (called from caller's body) still rc>=1; a genuinely-never-called py_never_called stays rc=0/dead (control).

Tests

Added 3 Python golden cases to tests/test_graph_accuracy_golden.py (mirrors the TS #219/#223 fixtures): rc>=1 + caller-from-file for module-level-called fns, trace-up surfaces the module_level=True caller, and a genuinely-dead control stays dead.

tests/test_graph_accuracy_golden.py  10 passed
pytest -k "python_parser or parser or graph_accuracy or issue291"  247 passed

Closes #291.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Wolfvin and others added 2 commits July 14, 2026 19:22
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@Wolfvin
Wolfvin merged commit 1833d13 into main Jul 14, 2026
2 of 8 checks passed
@Wolfvin
Wolfvin deleted the fix/issue-291-python-module-level-calls branch July 14, 2026 12:30
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.

fix(parser): Python module-level calls not extracted → false dead-code + rc undercount

1 participant