mirror of
https://github.com/safishamsi/graphify.git
synced 2026-08-28 01:06:36 +00:00
fix(python): resolve relative subpackage imports to their __init__ (#2688)
This commit is contained in:
committed by
safishamsi
parent
a05b4084d9
commit
51cf1481b6
+15
-2
@@ -124,6 +124,7 @@ from graphify.extractors.resolution import ( # noqa: E402,F401
|
||||
_resolve_js_import_target,
|
||||
_resolve_js_module_path,
|
||||
_resolve_lua_import_target,
|
||||
_probe_python_module_candidate,
|
||||
_resolve_python_module_path,
|
||||
_resolve_tsconfig_alias,
|
||||
_resolve_workspace_import,
|
||||
@@ -350,8 +351,20 @@ def _import_python(node, source: bytes, file_nid: str, stem: str, edges: list, s
|
||||
base = Path(str_path).parent
|
||||
for _ in range(dots - 1):
|
||||
base = base.parent
|
||||
rel = (module_name.replace(".", "/") + ".py") if module_name else "__init__.py"
|
||||
target_path = base / rel
|
||||
# A relative import can name a subpackage (a directory with an
|
||||
# __init__.py), not a module file. Probing the candidate on disk
|
||||
# (mirroring the companion `imports` edge's
|
||||
# _resolve_python_module_path) resolves `graphs` -> graphs/__init__.py
|
||||
# instead of a nonexistent graphs.py: without it the target keeps an
|
||||
# absolute-path-derived slug that the target_file stamp below can't
|
||||
# heal, so it dangles per-checkout (#2455).
|
||||
candidate = base / module_name.replace(".", "/") if module_name else base
|
||||
resolved = _probe_python_module_candidate(candidate)
|
||||
if resolved is not None:
|
||||
target_path = resolved
|
||||
else:
|
||||
rel = (module_name.replace(".", "/") + ".py") if module_name else "__init__.py"
|
||||
target_path = base / rel
|
||||
tgt_nid = _make_id(str(target_path))
|
||||
else:
|
||||
tgt_nid = _make_id(raw)
|
||||
|
||||
@@ -60,6 +60,42 @@ def test_ordinary_relative_import_still_resolves(tmp_path: Path):
|
||||
assert _has_edge(result, source_file, target_symbol, "imports")
|
||||
|
||||
|
||||
def test_relative_subpackage_import_from_targets_package_init(tmp_path: Path):
|
||||
# `from ...graphs import build_graph` where `graphs/` is a package (a dir
|
||||
# with __init__.py, not a graphs.py module). The imports_from edge must
|
||||
# target the package's __init__.py file node, matching the companion
|
||||
# `imports` edge — not an absolute-scan-path slug for a nonexistent
|
||||
# graphs.py that dangles per-checkout (#2455).
|
||||
init = _write(tmp_path / "src/mypkg/__init__.py", "")
|
||||
api_init = _write(tmp_path / "src/mypkg/api/__init__.py", "")
|
||||
routes_init = _write(tmp_path / "src/mypkg/api/routes/__init__.py", "")
|
||||
graphs_init = _write(
|
||||
tmp_path / "src/mypkg/graphs/__init__.py",
|
||||
"def build_graph():\n return {}\n",
|
||||
)
|
||||
health = _write(
|
||||
tmp_path / "src/mypkg/api/routes/health.py",
|
||||
"from ...graphs import build_graph\n",
|
||||
)
|
||||
|
||||
result = extract(
|
||||
[init, api_init, routes_init, graphs_init, health], cache_root=tmp_path
|
||||
)
|
||||
|
||||
health_file = _node_id(result, "health.py", "src/mypkg/api/routes/health.py")
|
||||
graphs_pkg = _node_id(result, "__init__.py", "src/mypkg/graphs/__init__.py")
|
||||
|
||||
assert _has_edge(result, health_file, graphs_pkg, "imports_from")
|
||||
# No imports_from edge out of health may carry an unresolved absolute-path
|
||||
# slug (the pre-fix `<scan>_src_mypkg_graphs_py` target).
|
||||
health_targets = [
|
||||
e["target"]
|
||||
for e in result["edges"]
|
||||
if e["source"] == health_file and e["relation"] == "imports_from"
|
||||
]
|
||||
assert all(t.endswith("graphs_init") for t in health_targets), health_targets
|
||||
|
||||
|
||||
def test_python_package_reexport_resolves_import_and_call_to_origin_symbol(tmp_path: Path):
|
||||
origin = _write(tmp_path / "pkg/foo.py", "def Foo():\n return 1\n")
|
||||
barrel = _write(tmp_path / "pkg/__init__.py", "from .foo import Foo as PublicFoo\n")
|
||||
|
||||
Reference in New Issue
Block a user