From e2636e1aa0964bbd89cb52cd27244bf74536b8cb Mon Sep 17 00:00:00 2001 From: antazoey Date: Tue, 2 Jul 2024 15:44:21 -0500 Subject: [PATCH] fix: issues with 0.4 imports (#122) --- ape_vyper/compiler.py | 14 +++++++++++--- tests/contracts/passing_contracts/flatten_me.vy | 4 ++-- .../subdir/zero_four_in_subdir.vy | 6 ++++++ tests/contracts/passing_contracts/use_iface.vy | 4 ++-- tests/contracts/passing_contracts/use_iface2.vy | 2 +- tests/contracts/passing_contracts/zero_four.vy | 6 +++++- tests/contracts/templates/reverts.template | 2 +- .../templates/traceback_contract.template | 2 +- tests/test_cli.py | 16 ++++++++++++++++ tests/test_compiler.py | 13 +++++++++++++ 10 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 tests/contracts/passing_contracts/subdir/zero_four_in_subdir.vy diff --git a/ape_vyper/compiler.py b/ape_vyper/compiler.py index 03855f31..b6814a01 100644 --- a/ape_vyper/compiler.py +++ b/ape_vyper/compiler.py @@ -332,16 +332,22 @@ def _get_imports( dots += prefix[0] prefix = prefix[1:] + is_relative = dots != "" + # Replace rest of dots with slashes. prefix = prefix.replace(".", os.path.sep) - if prefix.startswith("vyper/"): + if prefix.startswith("vyper/") or prefix.startswith("ethereum/"): if f"{prefix}.json" not in import_map[source_id]: import_map[source_id].append(f"{prefix}.json") continue - local_path = (path.parent / dots / prefix.lstrip(os.path.sep)).resolve() + local_path = ( + (path.parent / dots / prefix.lstrip(os.path.sep)).resolve() + if is_relative + else (pm.path / prefix.lstrip(os.path.sep)).resolve() + ) local_prefix = str(local_path).replace(f"{pm.path}", "").lstrip(os.path.sep) import_source_id = None @@ -1235,12 +1241,14 @@ def _to_src_id(s): search_paths = [*getsitepackages()] if pm.path == Path.cwd(): search_paths.append(".") + else: + search_paths.append(str(pm.path)) # else: only seem to get absolute paths to work (for compiling deps alone). version_settings[settings_key] = { "optimize": optimization, "outputSelection": selection_dict, - "search_paths": [".", *getsitepackages()], + "search_paths": search_paths, } if evm_version and evm_version not in ("none", "null"): version_settings[settings_key]["evmVersion"] = f"{evm_version}" diff --git a/tests/contracts/passing_contracts/flatten_me.vy b/tests/contracts/passing_contracts/flatten_me.vy index 77b993d2..ccf0f377 100644 --- a/tests/contracts/passing_contracts/flatten_me.vy +++ b/tests/contracts/passing_contracts/flatten_me.vy @@ -2,8 +2,8 @@ from vyper.interfaces import ERC20 -from interfaces import IFace2 as IFaceTwo -import interfaces.IFace as IFace +from .interfaces import IFace2 as IFaceTwo +from .interfaces import IFace as IFace import exampledependency.Dependency as Dep diff --git a/tests/contracts/passing_contracts/subdir/zero_four_in_subdir.vy b/tests/contracts/passing_contracts/subdir/zero_four_in_subdir.vy new file mode 100644 index 00000000..b3091c95 --- /dev/null +++ b/tests/contracts/passing_contracts/subdir/zero_four_in_subdir.vy @@ -0,0 +1,6 @@ +# Show we can import from the root of the project w/o needing relative imports +from contracts.passing_contracts import zero_four_module as zero_four_module + +@external +def callModuleFunctionFromSubdir(role: bytes32) -> bool: + return zero_four_module.moduleMethod() diff --git a/tests/contracts/passing_contracts/use_iface.vy b/tests/contracts/passing_contracts/use_iface.vy index 38f99932..76aa4e8d 100644 --- a/tests/contracts/passing_contracts/use_iface.vy +++ b/tests/contracts/passing_contracts/use_iface.vy @@ -1,12 +1,12 @@ # @version ^0.3.3 # Import a local interface. -import interfaces.IFace as IFace +from .interfaces import IFace as IFace # Import from input JSON (ape-config.yaml). import exampledependency.Dependency as Dep -from interfaces import IFace2 as IFace2 +from .interfaces import IFace2 as IFace2 @external diff --git a/tests/contracts/passing_contracts/use_iface2.vy b/tests/contracts/passing_contracts/use_iface2.vy index c8e530bf..4b888ace 100644 --- a/tests/contracts/passing_contracts/use_iface2.vy +++ b/tests/contracts/passing_contracts/use_iface2.vy @@ -1,6 +1,6 @@ # @version ^0.3.3 -from interfaces import IFace as IFace +from .interfaces import IFace as IFace @external diff --git a/tests/contracts/passing_contracts/zero_four.vy b/tests/contracts/passing_contracts/zero_four.vy index fbab951d..e9ba6150 100644 --- a/tests/contracts/passing_contracts/zero_four.vy +++ b/tests/contracts/passing_contracts/zero_four.vy @@ -1,12 +1,16 @@ # pragma version ~=0.4.0 -import interfaces.IFaceZeroFour as IFaceZeroFour +from .interfaces import IFaceZeroFour as IFaceZeroFour implements: IFaceZeroFour from . import zero_four_module as zero_four_module from snekmate.auth import ownable +# Also show we can import from ethereum namespace. +# (new in Vyper 0.4). +from ethereum.ercs import IERC20 + @external @view def implementThisPlease(role: bytes32) -> bool: diff --git a/tests/contracts/templates/reverts.template b/tests/contracts/templates/reverts.template index a9c5cd5e..b40ff6f6 100644 --- a/tests/contracts/templates/reverts.template +++ b/tests/contracts/templates/reverts.template @@ -1,6 +1,6 @@ # @version {{VYPER_VERSION}} -import interfaces.ISubReverts as ISubReverts +from .interfaces import ISubReverts as ISubReverts sub_reverts: public(ISubReverts) MAX_NUM: constant(uint256) = 32 diff --git a/tests/contracts/templates/traceback_contract.template b/tests/contracts/templates/traceback_contract.template index 451b5486..807721ec 100644 --- a/tests/contracts/templates/traceback_contract.template +++ b/tests/contracts/templates/traceback_contract.template @@ -1,6 +1,6 @@ # @version {{VYPER_VERSION}} -import interfaces.IRegistry as IRegistry +from .interfaces import IRegistry as IRegistry _balance: public(uint256) registry: public(IRegistry) diff --git a/tests/test_cli.py b/tests/test_cli.py index 1e5b3ba9..7bfcf24e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,3 +1,5 @@ +import subprocess + import pytest from ape.utils import create_tempdir @@ -31,3 +33,17 @@ def test_cli_flatten(project, contract_name, expected, cli_runner): output = file.read_text() for expect in expected: assert expect in output + + +def test_compile(): + """ + Integration: Testing the CLI using an actual subprocess because + it is the only way to test compiling the project such that it + isn't treated as a tempdir project. + """ + # Use a couple contracts + cmd_ls = ("ape", "compile", "subdir", "--force") + completed_process = subprocess.run(cmd_ls, capture_output=True) + output = completed_process.stdout.decode(encoding="utf8") + assert "SUCCESS" in output + assert "zero_four_in_subdir.vy" in output diff --git a/tests/test_compiler.py b/tests/test_compiler.py index fc6bbc41..da2eb94b 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -88,6 +88,19 @@ def test_compile_failures(contract_name, compiler): assert isinstance(err.value.base_err, VyperError) +def test_compile_zero_four(compiler, project): + """ + An easy way to test only Vyper 0.4 changes. + """ + paths = ( + project.contracts_folder / "subdir" / "zero_four_in_subdir.vy", + project.contracts_folder / "zero_four.vy", + ) + result = [x.name for x in compiler.compile(paths, project=project)] + assert "zero_four" in result + assert "zero_four_in_subdir" in result + + def test_install_failure(compiler): failing_project = ape.Project(FAILING_BASE) path = FAILING_BASE / "contract_unknown_pragma.vy"