Skip to content
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

feat: search path resolution for cli #3694

Merged
merged 2 commits into from
Dec 16, 2023
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
36 changes: 24 additions & 12 deletions tests/unit/cli/vyper_compile/test_compile_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

from tests.utils import working_directory
from vyper.cli.vyper_compile import compile_files


Expand All @@ -19,15 +20,15 @@ def test_combined_json_keys(tmp_path, make_file):
"userdoc",
"devdoc",
}
compile_data = compile_files(["bar.vy"], ["combined_json"], root_folder=tmp_path)
compile_data = compile_files(["bar.vy"], ["combined_json"], paths=[tmp_path])

assert set(compile_data.keys()) == {Path("bar.vy"), "version"}
assert set(compile_data[Path("bar.vy")].keys()) == combined_keys


def test_invalid_root_path():
with pytest.raises(FileNotFoundError):
compile_files([], [], root_folder="path/that/does/not/exist")
compile_files([], [], paths=["path/that/does/not/exist"])


CONTRACT_CODE = """
Expand Down Expand Up @@ -74,7 +75,7 @@ def test_import_same_folder(import_stmt, alias, tmp_path, make_file):
make_file("contracts/foo.vy", CONTRACT_CODE.format(import_stmt=import_stmt, alias=alias))
make_file("contracts/IFoo.vyi", INTERFACE_CODE)

assert compile_files([foo], ["combined_json"], root_folder=tmp_path)
assert compile_files([foo], ["combined_json"], paths=[tmp_path])


SUBFOLDER_IMPORT_STMT = [
Expand All @@ -98,7 +99,7 @@ def test_import_subfolder(import_stmt, alias, tmp_path, make_file):
)
make_file("contracts/other/IFoo.vyi", INTERFACE_CODE)

assert compile_files([foo], ["combined_json"], root_folder=tmp_path)
assert compile_files([foo], ["combined_json"], paths=[tmp_path])


OTHER_FOLDER_IMPORT_STMT = [
Expand All @@ -115,7 +116,7 @@ def test_import_other_folder(import_stmt, alias, tmp_path, make_file):
foo = make_file("contracts/foo.vy", CONTRACT_CODE.format(import_stmt=import_stmt, alias=alias))
make_file("interfaces/IFoo.vyi", INTERFACE_CODE)

assert compile_files([foo], ["combined_json"], root_folder=tmp_path)
assert compile_files([foo], ["combined_json"], paths=[tmp_path])


def test_import_parent_folder(tmp_path, make_file):
Expand All @@ -125,10 +126,21 @@ def test_import_parent_folder(tmp_path, make_file):
)
make_file("IFoo.vyi", INTERFACE_CODE)

assert compile_files([foo], ["combined_json"], root_folder=tmp_path)
assert compile_files([foo], ["combined_json"], paths=[tmp_path])

# perform relative import outside of base folder
compile_files([foo], ["combined_json"], root_folder=tmp_path / "contracts")
compile_files([foo], ["combined_json"], paths=[tmp_path / "contracts"])


def test_import_search_paths(tmp_path, make_file):
with working_directory(tmp_path):
contract_code = CONTRACT_CODE.format(import_stmt="from utils import IFoo", alias="IFoo")
contract_filename = "dir1/baz/foo.vy"
interface_filename = "dir2/utils/IFoo.vyi"
make_file(interface_filename, INTERFACE_CODE)
make_file(contract_filename, contract_code)

assert compile_files([contract_filename], ["combined_json"], paths=["dir2"])


META_IMPORT_STMT = [
Expand Down Expand Up @@ -167,7 +179,7 @@ def be_known() -> ISelf.FooStruct:
make_file("contracts/ISelf.vyi", interface_code)
meta = make_file("contracts/Self.vy", code)

assert compile_files([meta], ["combined_json"], root_folder=tmp_path)
assert compile_files([meta], ["combined_json"], paths=[tmp_path])


# implement IFoo in another contract for fun
Expand All @@ -187,7 +199,7 @@ def bar(_foo: address) -> {alias}.FooStruct:
make_file("contracts/IFoo.vyi", INTERFACE_CODE)
baz = make_file("contracts/Baz.vy", baz_code)

assert compile_files([baz], ["combined_json"], root_folder=tmp_path)
assert compile_files([baz], ["combined_json"], paths=[tmp_path])


def test_local_namespace(make_file, tmp_path):
Expand Down Expand Up @@ -215,15 +227,15 @@ def test_local_namespace(make_file, tmp_path):
for file_name in ("foo.vyi", "bar.vyi"):
make_file(file_name, INTERFACE_CODE)

assert compile_files(paths, ["combined_json"], root_folder=tmp_path)
assert compile_files(paths, ["combined_json"], paths=[tmp_path])


def test_compile_outside_root_path(tmp_path, make_file):
# absolute paths relative to "."
make_file("ifoo.vyi", INTERFACE_CODE)
foo = make_file("foo.vy", CONTRACT_CODE.format(import_stmt="import ifoo as IFoo", alias="IFoo"))

assert compile_files([foo], ["combined_json"], root_folder=".")
assert compile_files([foo], ["combined_json"], paths=None)


def test_import_library(tmp_path, make_file):
Expand All @@ -244,4 +256,4 @@ def foo() -> uint256:
make_file("lib.vy", library_source)
contract_file = make_file("contract.vy", contract_source)

assert compile_files([contract_file], ["combined_json"], root_folder=tmp_path) is not None
assert compile_files([contract_file], ["combined_json"], paths=[tmp_path]) is not None
13 changes: 1 addition & 12 deletions tests/unit/compiler/test_input_bundle.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import contextlib
import json
import os
from pathlib import Path, PurePath

import pytest

from tests.utils import working_directory
from vyper.compiler.input_bundle import ABIInput, FileInput, FilesystemInputBundle, JSONInputBundle


Expand Down Expand Up @@ -83,16 +82,6 @@ def test_load_abi(make_file, input_bundle, tmp_path):
assert file == ABIInput(1, "foo.txt", path, "some string")


@contextlib.contextmanager
def working_directory(directory):
tmp = os.getcwd()
try:
os.chdir(directory)
yield
finally:
os.chdir(tmp)


# check that unique paths give unique source ids
def test_source_id_file_input(make_file, input_bundle, tmp_path):
foopath = make_file("foo.vy", "contents")
Expand Down
12 changes: 12 additions & 0 deletions tests/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import contextlib
import os


@contextlib.contextmanager
def working_directory(directory):
tmp = os.getcwd()
try:
os.chdir(directory)
yield
finally:
os.chdir(tmp)
19 changes: 12 additions & 7 deletions vyper/cli/vyper_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def _parse_args(argv):
)
parser.add_argument("--hex-ir", action="store_true")
parser.add_argument(
"-p", help="Set the root path for contract imports", default=".", dest="root_folder"
"--path", "-p", help="Set the root path for contract imports", action="append", dest="paths"
)
parser.add_argument("-o", help="Set the output path", dest="output_path")
parser.add_argument(
Expand Down Expand Up @@ -188,7 +188,7 @@ def _parse_args(argv):
compiled = compile_files(
args.input_files,
output_formats,
args.root_folder,
args.paths,
args.show_gas_estimates,
settings,
args.storage_layout,
Expand Down Expand Up @@ -226,18 +226,23 @@ def exc_handler(contract_path: ContractPath, exception: Exception) -> None:
def compile_files(
input_files: list[str],
output_formats: OutputFormats,
root_folder: str = ".",
paths: list[str] = None,
show_gas_estimates: bool = False,
settings: Optional[Settings] = None,
storage_layout_paths: list[str] = None,
no_bytecode_metadata: bool = False,
experimental_codegen: bool = False,
) -> dict:
root_path = Path(root_folder).resolve()
if not root_path.exists():
raise FileNotFoundError(f"Invalid root path - '{root_path.as_posix()}' does not exist")
paths = paths or []

input_bundle = FilesystemInputBundle([root_path])
# lowest precedence search path is always `.`
search_paths = [Path(".")]

for p in paths:
path = Path(p).resolve(strict=True)
search_paths.append(path)

input_bundle = FilesystemInputBundle(search_paths)

show_version = False
if "combined_json" in output_formats:
Expand Down
Loading