Skip to content

[CG-10829] fix: import wildcard doesn't preserve import resolution #502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
2 changes: 2 additions & 0 deletions src/codegen/sdk/core/import_resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
imports_file: bool = False # True when we import the entire file (e.g. `from a.b.c import foo`)


TSourceFile = TypeVar("TSourceFile", bound="SourceFile")

Check failure on line 57 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Cannot redefine "TSourceFile" as a type variable [misc]

Check failure on line 57 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Invalid assignment target [misc]


@apidoc
Expand All @@ -76,7 +76,7 @@
module: Editable | None
symbol_name: Editable | None
alias: Editable | None
node_type: ClassVar[Literal[NodeType.IMPORT]] = NodeType.IMPORT

Check failure on line 79 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Cannot override instance variable (previously declared on base class "Expression") with class variable [misc]
import_type: ImportType
import_statement: ImportStatement

Expand All @@ -96,7 +96,7 @@
self.module = self.ctx.parser.parse_expression(module_node, self.file_node_id, ctx, self, default=Name) if module_node else None
self.alias = self.ctx.parser.parse_expression(alias_node, self.file_node_id, ctx, self, default=Name) if alias_node else None
self.symbol_name = self.ctx.parser.parse_expression(name_node, self.file_node_id, ctx, self, default=Name) if name_node else None
self._name_node = self._parse_expression(name_node, default=Name)

Check failure on line 99 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible types in assignment (expression has type "Expression[Import[TSourceFile]] | None", variable has type "Name[Any] | ChainedAttribute[Any, Any, Any] | DefinedName[Any] | None") [assignment]
self.import_type = import_type

def __rich_repr__(self) -> rich.repr.Result:
Expand All @@ -109,7 +109,7 @@
yield "wildcard", self.is_wildcard_import(), False
yield from super().__rich_repr__()

__rich_repr__.angular = ANGULAR_STYLE

Check failure on line 112 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: "Callable[[Import[TSourceFile]], Iterable[Any | tuple[Any] | tuple[str, Any] | tuple[str, Any, Any]]]" has no attribute "angular" [attr-defined]

@noapidoc
@abstractmethod
Expand Down Expand Up @@ -140,7 +140,7 @@
# =====[ Case: Can resolve the filepath ]=====
elif resolution.symbol:
if resolution.symbol.node_id == self.node_id:
return [] # Circular to self

Check failure on line 143 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: No return value expected [return-value]
self.ctx.add_edge(
self.node_id,
resolution.symbol.node_id,
Expand All @@ -148,7 +148,7 @@
)

elif resolution.imports_file:
self.ctx.add_edge(self.node_id, resolution.from_file.node_id, type=EdgeType.IMPORT_SYMBOL_RESOLUTION)

Check failure on line 151 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Item "None" of "TSourceFile | None" has no attribute "node_id" [union-attr]
# for symbol in resolution.from_file.symbols:
# usage = SymbolUsage(parent_symbol_name=self.name, child_symbol_name=self.name, type=SymbolUsageType.IMPORTED, match=self, usage_type=UsageType.DIRECT)
# self.ctx.add_edge(self.node_id, symbol.node_id, type=EdgeType.SYMBOL_USAGE, usage=usage)
Expand All @@ -160,7 +160,7 @@
# - an indirect import of an external module
# TODO: add as external module only if it resolves to an external module from resolution.from_file
# Solution: return the resolution object to be processed in a separate loop in `compute_codebase_graph`
return []

Check failure on line 163 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: No return value expected [return-value]

@property
@reader
Expand Down Expand Up @@ -271,9 +271,9 @@
elif imported.node_type == NodeType.EXTERNAL:
return None
elif imported.__class__.__name__.endswith("SourceFile"): # TODO - this is a hack for when you import a full file/module
return imported

Check failure on line 274 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "Symbol[Any, Any] | TSourceFile | Import[Any]", expected "TSourceFile | None") [return-value]
else:
return imported.file

Check failure on line 276 in src/codegen/sdk/core/import_resolution.py

View workflow job for this annotation

GitHub Actions / mypy

error: Incompatible return value type (got "SourceFile[Any, Any, Any, Any, Any, Any]", expected "TSourceFile | None") [return-value]

@property
@reader
Expand Down Expand Up @@ -607,6 +607,8 @@
imp.file.invalidate()

return
elif self.resolved_symbol is None:
self._resolving_wildcards = False
yield self.name, self

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,30 @@ def some_func():
# Verify usages are detected
assert len(some_func.usages) > 0
assert len(some_func.symbol_usages) > 0


def test_import_wildcard_preserves_import_resolution(tmpdir: str) -> None:
"""Tests importing from a file that contains a wildcard import doesn't break further resolution.
This could occur depending on to_resolve ordering, if the outer file is processed first _wildcards will not be filled in time.
"""
# language=python
with get_codebase_session(
tmpdir,
files={
"testdir/sub/file.py": """
test_const=5
b=2
""",
"testdir/file.py": """
from testdir.sub.file import *
c=b
""",
"file.py": """
from testdir.file import test_const
test = test_const
""",
},
) as codebase:
mainfile: SourceFile = codebase.get_file("file.py")

assert len(mainfile.ctx.edges) == 5
Loading