From 5fd4ab650b909150f2f4c0468b100f2414fb7d0a Mon Sep 17 00:00:00 2001 From: SinghAman21 Date: Tue, 11 Aug 2026 16:44:41 +0530 Subject: [PATCH] fix(python): avoid crash resolving overdeep relative imports --- graphify/extractors/resolution.py | 2 ++ tests/test_python_import_resolution.py | 30 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/graphify/extractors/resolution.py b/graphify/extractors/resolution.py index 380633865..552f44110 100644 --- a/graphify/extractors/resolution.py +++ b/graphify/extractors/resolution.py @@ -1724,6 +1724,8 @@ def _probe_python_module_candidate(candidate: Path) -> Path | None: return init_path if candidate.is_file(): return candidate + if not candidate.name: + return None py_candidate = candidate.with_suffix(".py") if py_candidate.is_file(): return py_candidate diff --git a/tests/test_python_import_resolution.py b/tests/test_python_import_resolution.py index 2a517aaea..754de21ac 100644 --- a/tests/test_python_import_resolution.py +++ b/tests/test_python_import_resolution.py @@ -3,6 +3,7 @@ from __future__ import annotations from pathlib import Path from graphify.extract import extract +from graphify.extractors.resolution import _resolve_python_module_path def _write(path: Path, text: str) -> Path: @@ -30,6 +31,35 @@ def _has_edge(result: dict, source: str, target: str, relation: str) -> bool: ) +def test_overdeep_relative_import_is_unresolved_not_fatal(tmp_path: Path): + source = _write( + tmp_path / "pkg" / "mod.py", + "from ........................... import missing\n\n" + "def ok():\n" + " return 1\n", + ) + + assert _resolve_python_module_path("", source, tmp_path, level=27) is None + + result = extract([source], cache_root=tmp_path) + + assert _node_id(result, "mod.py", "pkg/mod.py") + assert _node_id(result, "ok()", "pkg/mod.py") + + +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") + + assert _resolve_python_module_path("sibling", source, tmp_path, level=1) == target + + result = extract([source, target], cache_root=tmp_path) + source_file = _node_id(result, "mod.py", "pkg/mod.py") + target_symbol = _node_id(result, "helper()", "pkg/sibling.py") + + assert _has_edge(result, source_file, target_symbol, "imports") + + 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")