Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/core/src/repowise/core/ingestion/graph/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ def build(self, progress: Any | None = None) -> nx.DiGraph:
from ..resolvers.cpp import resolve_cpp_import_all

targets = resolve_cpp_import_all(imp.module_path, path, ctx)
elif _lang == "python":
from ..resolvers.python import resolve_python_import_all

targets = resolve_python_import_all(imp, path, ctx)
else:
single = resolve_import(imp.module_path, path, _lang, ctx)
targets = (single,) if single else ()
Expand Down
21 changes: 20 additions & 1 deletion packages/core/src/repowise/core/ingestion/resolvers/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

from pathlib import Path
from typing import Any

from ..languages.python_modules import build_python_module_index
from .context import ResolverContext
Expand Down Expand Up @@ -67,6 +68,24 @@ def resolve_python_import(module_path: str, importer_path: str, ctx: ResolverCon
if c in ctx.path_set:
return c

# Stem-only fallback
# Stem-only fallback
stem = module_path.split(".")[-1].lower()
return ctx.stem_lookup(stem)


def resolve_python_import_all(imp: Any, importer_path: str, ctx: ResolverContext) -> tuple[str, ...]:
"""Resolve a Python import, additionally probing submodules for package imports."""
targets = []
base = resolve_python_import(imp.module_path, importer_path, ctx)
if base:
targets.append(base)
if base.endswith("__init__.py"):
base_dir = Path(base).parent.as_posix()
for name in (imp.imported_names or []):
c1 = f"{base_dir}/{name}.py"
c2 = f"{base_dir}/{name}/__init__.py"
if c1 in ctx.path_set:
targets.append(c1)
elif c2 in ctx.path_set:
targets.append(c2)
return tuple(targets)
52 changes: 52 additions & 0 deletions tests/unit/ingestion/test_python_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import pytest
from repowise.core.ingestion.models import Import
from repowise.core.ingestion.resolvers.context import ResolverContext
from repowise.core.ingestion.resolvers.python import resolve_python_import, resolve_python_import_all


def test_resolve_python_import_all_submodules():
path_set = {
"src/app.py",
"src/routers/__init__.py",
"src/routers/workspace.py",
"src/routers/git/__init__.py",
}

ctx = ResolverContext(path_set=path_set, stem_map={}, graph=None)

# 1. Single submodule without __init__.py package
imp = Import(
module_path="routers",
module_type="python",
imported_names=("workspace", "git", "overview"),
range_start=0,
range_end=0,
is_type_only=False,
)

targets = resolve_python_import_all(imp, "src/app.py", ctx)
assert "src/routers/__init__.py" in targets
assert "src/routers/workspace.py" in targets
assert "src/routers/git/__init__.py" in targets
assert len(targets) == 3


def test_resolve_python_import_all_no_submodules():
path_set = {
"src/app.py",
"src/routers/__init__.py",
}

ctx = ResolverContext(path_set=path_set, stem_map={}, graph=None)

imp = Import(
module_path="routers",
module_type="python",
imported_names=("missing",),
range_start=0,
range_end=0,
is_type_only=False,
)

targets = resolve_python_import_all(imp, "src/app.py", ctx)
assert targets == ("src/routers/__init__.py",)
Loading