From 6d05c2672c5335a5b5aa7559bd983a76d645d8bd Mon Sep 17 00:00:00 2001 From: antazoey Date: Tue, 23 Jul 2024 12:54:44 -0500 Subject: [PATCH] fix: handle implicit relative imports (#128) --- .pre-commit-config.yaml | 2 +- ape_vyper/compiler.py | 74 +++++++++++++++---- setup.py | 2 +- .../zero_four_snekmate_erc20.vy | 11 +++ tests/test_compiler.py | 3 +- 5 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 tests/contracts/passing_contracts/zero_four_snekmate_erc20.vy diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6292b9c..ae7de4e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.11.0 hooks: - id: mypy additional_dependencies: [types-setuptools, pydantic==1.10.4] diff --git a/ape_vyper/compiler.py b/ape_vyper/compiler.py index 96486df..8632547 100644 --- a/ape_vyper/compiler.py +++ b/ape_vyper/compiler.py @@ -379,7 +379,7 @@ def _get_imports( if not path.is_file(): continue - content = path.read_text().splitlines() + content = path.read_text(encoding="utf8").splitlines() source_id = ( str(path.absolute()) if use_absolute_paths @@ -410,7 +410,10 @@ def _get_imports( dots += prefix[0] prefix = prefix[1:] - is_relative = dots != "" + is_relative: Optional[bool] = None + if dots != "": + is_relative = True + # else: we are unsure since dots are not required. # Replace rest of dots with slashes. prefix = prefix.replace(".", os.path.sep) @@ -421,25 +424,70 @@ def _get_imports( continue - local_path = ( - (path.parent / dots / prefix.lstrip(os.path.sep)).resolve() - if is_relative - else (pm.path / prefix.lstrip(os.path.sep)).resolve() + relative_path = None + abs_path = None + if is_relative is True: + relative_path = (path.parent / dots / prefix.lstrip(os.path.sep)).resolve() + elif is_relative is False: + abs_path = (pm.path / prefix.lstrip(os.path.sep)).resolve() + elif is_relative is None: + relative_path = (path.parent / dots / prefix.lstrip(os.path.sep)).resolve() + abs_path = (pm.path / prefix.lstrip(os.path.sep)).resolve() + + local_prefix_relative = ( + None + if relative_path is None + else str(relative_path).replace(f"{pm.path}", "").lstrip(os.path.sep) + ) + local_prefix_abs = ( + None + if abs_path is None + else str(abs_path).replace(f"{pm.path}", "").lstrip(os.path.sep) ) - local_prefix = str(local_path).replace(f"{pm.path}", "").lstrip(os.path.sep) import_source_id = None is_local = True + local_path = None # TBD + local_prefix = None # TBD - # NOTE: Defaults to JSON (assuming from input JSON or a local JSON), - # unless a Vyper file exists. - if (pm.path / f"{local_prefix}{FileType.SOURCE}").is_file(): + if (pm.path / f"{local_prefix_relative}{FileType.SOURCE}").is_file(): + # Relative source. ext = FileType.SOURCE.value - elif (pm.path / f"{local_prefix}{FileType.SOURCE}").is_file(): + local_path = relative_path + local_prefix = local_prefix_relative + + elif (pm.path / f"{local_prefix_relative}{FileType.INTERFACE}").is_file(): + # Relative interface. ext = FileType.INTERFACE.value - elif (pm.path / f"{local_prefix}{FileType.INTERFACE}").is_file(): + local_path = relative_path + local_prefix = local_prefix_relative + + elif (pm.path / f"{local_prefix_relative}.json").is_file(): + # Relative JSON interface. + ext = ".json" + local_path = relative_path + local_prefix = local_prefix_relative + + elif (pm.path / f"{local_prefix_abs}{FileType.SOURCE}").is_file(): + # Absolute source. + ext = FileType.SOURCE.value + local_path = abs_path + local_prefix = local_prefix_abs + + elif (pm.path / f"{local_prefix_abs}{FileType.INTERFACE}").is_file(): + # Absolute interface. ext = FileType.INTERFACE.value + local_path = abs_path + local_prefix = local_prefix_abs + + elif (pm.path / f"{local_prefix_abs}.json").is_file(): + # Absolute JSON interface. + ext = ".json" + local_path = abs_path + local_prefix = local_prefix_abs + else: + # Must be an interface JSON specified in the input JSON. ext = ".json" dep_key = prefix.split(os.path.sep)[0] dependency_name = prefix.split(os.path.sep)[0] @@ -488,7 +536,7 @@ def _get_imports( is_local = False - if is_local: + if is_local and local_prefix is not None and local_path is not None: import_source_id = f"{local_prefix}{ext}" full_path = local_path.parent / f"{local_path.stem}{ext}" diff --git a/setup.py b/setup.py index ca1bc90..6f389ff 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ ], "lint": [ "black>=24.4.2,<25", # Auto-formatter and linter - "mypy>=1.10.1,<2", # Static type analyzer + "mypy>=1.11.0,<2", # Static type analyzer "types-setuptools", # Needed due to mypy typeshed "flake8>=7.1.0,<8", # Style linter "isort>=5.13.2", # Import sorting linter diff --git a/tests/contracts/passing_contracts/zero_four_snekmate_erc20.vy b/tests/contracts/passing_contracts/zero_four_snekmate_erc20.vy new file mode 100644 index 0000000..c38516a --- /dev/null +++ b/tests/contracts/passing_contracts/zero_four_snekmate_erc20.vy @@ -0,0 +1,11 @@ +# pragma version ~=0.4.0 +from snekmate.auth import ownable +from snekmate.tokens import erc20 + +initializes: ownable +initializes: erc20[ownable := ownable] + +@deploy +def __init__(_name: String[25]): + ownable.__init__() + erc20.__init__(_name, "ERC20", 18, "name", "name2") diff --git a/tests/test_compiler.py b/tests/test_compiler.py index 177c355..1b6a6cd 100644 --- a/tests/test_compiler.py +++ b/tests/test_compiler.py @@ -303,6 +303,7 @@ def test_get_version_map(project, compiler, all_versions): "zero_four.vy", "zero_four_module.vy", "zero_four_module_2.vy", + "zero_four_snekmate_erc20.vy", } assert actual4 == expected4 @@ -397,7 +398,7 @@ def test_get_imports(compiler, project): actual_iface_use = actual[use_iface_key] for expected in (local_import, local_from_import, dependency_import, local_nested_import): - assert any(k for k in actual_iface_use if expected in k) + assert any(k for k in actual_iface_use if expected in k), f"{expected} not found" assert actual[use_iface2_key][0].endswith(local_import)