Skip to content

Commit

Permalink
fix[ux]: improve error message on failed imports (#4409)
Browse files Browse the repository at this point in the history
Previously, when an import failed, the error message only displayed
the paths that were attempted, but did not point to the specific import
statement which caused the exception.

To address this, we wrap the main node-handling loop in
`ImportAnalyzer` with `tag_exception`, which propagates information
with the specific line and file to the error message.
  • Loading branch information
sandbubbles authored Jan 3, 2025
1 parent d67e57c commit 4507d2a
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 12 deletions.
9 changes: 6 additions & 3 deletions tests/functional/syntax/test_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ def foo():
)

file_input = input_bundle.load_file("top.vy")
with pytest.raises(ModuleNotFound):
with pytest.raises(ModuleNotFound) as e:
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert "lib0.vy:" in str(e.value)


def test_implicitly_relative_import_crashes_2(make_input_bundle):
Expand All @@ -44,8 +45,9 @@ def foo():
)

file_input = input_bundle.load_file("top.vy")
with pytest.raises(ModuleNotFound):
with pytest.raises(ModuleNotFound) as e:
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert "lib0.vy:" in str(e.value)


def test_relative_import_searches_only_current_path(make_input_bundle):
Expand All @@ -70,8 +72,9 @@ def foo():
input_bundle = make_input_bundle({"top.vy": top, "a.vy": a, "subdir/b.vy": b})
file_input = input_bundle.load_file("top.vy")

with pytest.raises(ModuleNotFound):
with pytest.raises(ModuleNotFound) as e:
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert "b.vy:" in str(e.value)


def test_absolute_import_within_relative_import(make_input_bundle):
Expand Down
15 changes: 10 additions & 5 deletions tests/functional/syntax/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,26 +411,31 @@ def foobar():
assert compiler.compile_code(code, input_bundle=input_bundle) is not None


def test_builtins_not_found():
def test_builtins_not_found(make_input_bundle):
code = """
from vyper.interfaces import foobar
"""
input_bundle = make_input_bundle({"code.vy": code})
file_input = input_bundle.load_file("code.vy")
with pytest.raises(ModuleNotFound) as e:
compiler.compile_code(code)

compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert e.value._message == "vyper.interfaces.foobar"
assert e.value._hint == "try renaming `vyper.interfaces` to `ethereum.ercs`"
assert "code.vy:" in str(e.value)


@pytest.mark.parametrize("erc", ("ERC20", "ERC721", "ERC4626"))
def test_builtins_not_found2(erc):
def test_builtins_not_found2(erc, make_input_bundle):
code = f"""
from ethereum.ercs import {erc}
"""
input_bundle = make_input_bundle({"code.vy": code})
file_input = input_bundle.load_file("code.vy")
with pytest.raises(ModuleNotFound) as e:
compiler.compile_code(code)
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert e.value._message == f"ethereum.ercs.{erc}"
assert e.value._hint == f"try renaming `{erc}` to `I{erc}`"
assert "code.vy:" in str(e.value)


def test_interface_body_check(make_input_bundle):
Expand Down
10 changes: 6 additions & 4 deletions vyper/semantics/analysis/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
ImportCycle,
ModuleNotFound,
StructureException,
tag_exceptions,
)
from vyper.semantics.analysis.base import ImportInfo
from vyper.utils import safe_relpath, sha256sum
Expand Down Expand Up @@ -106,10 +107,11 @@ def _resolve_imports_r(self, module_ast: vy_ast.Module):
return
with self.graph.enter_path(module_ast):
for node in module_ast.body:
if isinstance(node, vy_ast.Import):
self._handle_Import(node)
elif isinstance(node, vy_ast.ImportFrom):
self._handle_ImportFrom(node)
with tag_exceptions(node):
if isinstance(node, vy_ast.Import):
self._handle_Import(node)
elif isinstance(node, vy_ast.ImportFrom):
self._handle_ImportFrom(node)
self.seen.add(id(module_ast))

def _handle_Import(self, node: vy_ast.Import):
Expand Down

0 comments on commit 4507d2a

Please sign in to comment.