fix(python): resolve relative subpackage import to package __init__ (#2455) - #2688
fix(python): resolve relative subpackage import to package __init__ (#2455)#2688ousamabenyounes wants to merge 1 commit into
Conversation
…raphify-Labs#2455) A relative import of a sibling subpackage (from ...graphs import x, where graphs/ is a package) built its imports_from target from a naive <module>.py path that does not exist, so the target_file stamp never fired and the edge kept an absolute-scan-path-derived slug that the Graphify-Labs#2169 remap could not heal — dangling and machine-specific per checkout. Probe the candidate on disk via _probe_python_module_candidate (the same resolver the companion imports edge already uses), resolving graphs -> graphs/__init__.py so the target canonicalizes to the package file node. A genuinely nonexistent module still falls back to the prior dangling behaviour.
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
This PR modifies Python relative import resolution in graphify/extract.py. In _import_python, when handling a relative import target, it now probes the candidate path on disk via the newly imported _probe_python_module_candidate helper so that a relative import naming a subpackage (a directory with __init__.py) resolves to that package's __init__.py rather than defaulting to a .py module path. The change adds a corresponding test verifying that from ...graphs import build_graph (where graphs/ is a package) produces an imports_from edge to the package __init__.py node, plus a CHANGELOG entry. The diff also lists many changelog/symbol names, but the substantive edits are confined to the extract logic and this one test.
No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1615 functions depend on the 396 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
extract()— 439 callers, 41 callees
Verification — 1615 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1475 function(s) in the blast radius were not formally verified this run
· 1 more finding(s) on lines outside this diff (see the check run).
|
Shipped in v0.9.42 ( |
Summary
Fix #2455
A Python relative import of a sibling subpackage (
from ...graphs import build_graph, wheregraphs/is a package — a directory with__init__.py, not agraphs.pymodule) emitted animports_fromedge whose target was an absolute-scan-path-derived slug for a nonexistentgraphs.py, instead of resolving to the package's__init__.pyfile node.Root cause:
_import_pythonbuilt the relative target asbase/<module>.pyunconditionally. For a subpackage that file does not exist, so the existence-gatedtarget_filestamp never fired, and the#2169remap had nothing to canonicalize — the edge kept a per-checkout absolute-path slug (<scan>_src_mypkg_graphs_py) that matches no node and dangles. This is the same reproducibility class as #2262/#2273/#2457.Fix: probe the candidate on disk with
_probe_python_module_candidate— the exact resolver the companionimportsedge already uses via_resolve_python_module_path— sographsresolves tographs/__init__.py. Thetarget_filestamp then fires and the edge canonicalizes to the real package file node. A genuinely nonexistent module still falls back to the prior dangling behaviour, unchanged.Reproduces with
Before (on
v8):After:
Test verification (RED → GREEN)
New test
test_relative_subpackage_import_from_targets_package_initintests/test_python_import_resolution.py.RED — unmodified
v8base, new test applied, no prod fix:GREEN — with the fix:
Full local suite
uv run --frozen pytest tests/ -q→4328 passed, 3 skipped(+1 new). The 4 failing tests (test_ollama.py::*, onetest_labelingordering artifact) are pre-existing and reproduce identically on the unmodifiedv8base — they are backend-detection env leakage (GEMINI_API_KEY), unrelated to this change (which touches only_import_python). skillgen validators (--check,--monolith-roundtrip,--always-on-roundtrip) andgraphify --helpsmoke all green.Files changed
graphify/extract.py_import_python: probe the relative-import candidate on disk so a subpackage resolves to its__init__.pytests/test_python_import_resolution.pyimports_fromresolutionCHANGELOG.md