fix(python): keep missing relative-import targets out of the checkout path

A relative import of a module with no file behind it (`from .absent
import x`) built its target from the attempted absolute path, so the id
became e.g. `c_users_<user>_..._pkg_absent_py`. The root-relative id
remap never rewrites it (there is no node to anchor on), so the checkout
location and the OS username ended up in graph.json and every clone got
a different id.

Use the dotted module name relative to the scan root instead
(`pkg_absent`), the id an unresolved absolute import of the same module
already gets, so `from .absent` and `from pkg.absent` now meet on one
node. An import that climbs above the scan root falls back to a `ref`
id. Imports that resolve, and attempted paths that exist, are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B8gwEpjajiWHVW3c9smuKf
(cherry picked from commit 8cdbb0ea4f)
This commit is contained in:
rohit-jsfreaky authored and safishamsi committed 2026-10-06 20:54:15 +01:00
1 parent ac467b0c00
commit 0083ee78e5
2 files changed
+54 -1

No files matched your search

+25
View File
@@ -147,6 +147,31 @@ def test_overdeep_relative_import_is_unresolved_not_fatal(tmp_path: Path):
assert _node_id(result, "ok()", "pkg/mod.py")
def _missing_import_targets(checkout: Path) -> set[str]:
_write(checkout / "pkg" / "__init__.py", "")
source = _write(
checkout / "pkg" / "app.py",
"from .absent import helper\n"
"from pkg.absent import other\n\n"
"def run():\n"
" return helper(), other()\n",
)
result = extract([source, checkout / "pkg" / "__init__.py"], cache_root=checkout)
return {e["target"] for e in result["edges"] if e["relation"] == "imports_from"}
def test_missing_relative_import_target_id_does_not_depend_on_the_checkout(tmp_path: Path):
"""A relative import of a module with no file behind it minted its target id
from the attempted absolute path (``..._pkg_absent_py``), which the
root-relative remap never rewrites: the checkout location and OS username
ended up in the graph. It now gets the dotted module name, the id an
unresolved absolute import of the same module already gets."""
first = _missing_import_targets(tmp_path / "clone_one")
second = _missing_import_targets(tmp_path / "elsewhere" / "clone_two")
assert first == second == {"pkg_absent"}
def test_ordinary_relative_import_still_resolves(tmp_path: Path):
target = _write(tmp_path / "pkg" / "sibling.py", "def helper():\n return 1\n")
source = _write(tmp_path / "pkg" / "mod.py", "from .sibling import helper\n")