From bc1581f477c7344c89180874ebdadda82bdc953d Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 13 Sep 2024 01:20:43 +0200 Subject: [PATCH] feat: `output_format` (#21) * Add `output_format` kwarg - Also deletes the temporary file after compilation * Update tests & support RCs Fix tests for vyper 0.4 --- CHANGELOG.md | 1 + tests/conftest.py | 24 +++++++------- tests/test_compile_source.py | 22 ++++++++++--- tests/test_install.py | 6 ++-- vvm/main.py | 64 +++++++++++++++++++++++------------- 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e1fb8c..6629a96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update contact information in `CONTRIBUTING.md` - Update dependencies. Minimum python version is now 3.8 ([#22](https://github.com/vyperlang/vvm/pull/22)) +- Add `output_format` argument to `compile_source` and `compile_files` ([#21](https://github.com/vyperlang/vvm/pull/21)) ## [0.1.0](https://github.com/vyperlang/vvm/tree/v0.1.0) - 2020-10-07 ### Added diff --git a/tests/conftest.py b/tests/conftest.py index 0e39069..5f90cb3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,19 +43,19 @@ def pytest_collection(session): vvm.install_vyper(version) -# auto-parametrize the all_versions fixture with all target vyper versions +# auto-parametrize the vyper_version fixture with all target vyper versions def pytest_generate_tests(metafunc): - if "all_versions" in metafunc.fixturenames: + if "vyper_version" in metafunc.fixturenames: versions = VERSIONS.copy() for marker in metafunc.definition.iter_markers(name="min_vyper"): versions = [i for i in versions if i >= Version(marker.args[0])] for marker in metafunc.definition.iter_markers(name="max_vyper"): versions = [i for i in versions if i <= Version(marker.args[0])] - metafunc.parametrize("all_versions", versions, indirect=True) + metafunc.parametrize("vyper_version", versions, indirect=True) @pytest.fixture -def all_versions(request): +def vyper_version(request): """ Run a test against all vyper versions. """ @@ -65,11 +65,13 @@ def all_versions(request): @pytest.fixture -def foo_source(all_versions): - visibility = "external" if all_versions >= Version("0.2.0") else "public" - interface = "IERC20" if all_versions >= Version("0.4.0a") else "ERC20" - import_path = "ethereum.ercs" if all_versions >= Version("0.4.0a") else "vyper.interfaces" +def foo_source(vyper_version): + visibility = "external" if vyper_version >= Version("0.2.0") else "public" + interface = "IERC20" if vyper_version >= Version("0.4.0a") else "ERC20" + import_path = "ethereum.ercs" if vyper_version >= Version("0.4.0a") else "vyper.interfaces" + pragma_version = "pragma version" if vyper_version >= Version("0.3.8") else "@version" yield f""" +#{pragma_version} {vyper_version} from {import_path} import {interface} @{visibility} @@ -79,8 +81,8 @@ def foo() -> int128: @pytest.fixture -def foo_path(tmp_path_factory, foo_source, all_versions): - source = tmp_path_factory.getbasetemp().joinpath(f"Foo-{all_versions}.sol") +def foo_path(tmp_path_factory, foo_source, vyper_version): + source = tmp_path_factory.getbasetemp().joinpath(f"Foo-{vyper_version}.vy") if not source.exists(): with source.open("w") as fp: fp.write(foo_source) @@ -88,7 +90,7 @@ def foo_path(tmp_path_factory, foo_source, all_versions): @pytest.fixture -def input_json(all_versions): +def input_json(vyper_version): json = { "language": "Vyper", "sources": {}, diff --git a/tests/test_compile_source.py b/tests/test_compile_source.py index ff7f400..1669369 100644 --- a/tests/test_compile_source.py +++ b/tests/test_compile_source.py @@ -4,15 +4,15 @@ import vvm -def test_compile_source(foo_source, all_versions): - if Version("0.4.0b1") <= all_versions <= Version("0.4.0b5"): +def test_compile_source(foo_source, vyper_version): + if Version("0.4.0b1") <= vyper_version <= Version("0.4.0b5"): pytest.skip("vyper 0.4.0b1 to 0.4.0b5 have a bug with combined_json") output = vvm.compile_source(foo_source) assert "" in output -def test_compile_files(foo_path, all_versions): - if Version("0.4.0b1") <= all_versions <= Version("0.4.0b5"): +def test_compile_files(foo_path, vyper_version): + if Version("0.4.0b1") <= vyper_version <= Version("0.4.0b5"): pytest.skip("vyper 0.4.0b1 to 0.4.0b5 have a bug with combined_json") output = vvm.compile_files([foo_path]) assert foo_path.as_posix() in output @@ -35,3 +35,17 @@ def foo() -> int128: return 42 """ vvm.compile_source(source, vyper_version=version_str) + + +def test_compile_metadata(foo_source, vyper_version): + if vyper_version <= Version("0.3.1"): + pytest.skip("metadata output not supported in vyper < 0.3.2") + output = vvm.compile_source(foo_source, output_format="metadata") + assert "function_info" in output + + +def test_compile_metadata_from_file(foo_path, vyper_version): + if vyper_version <= Version("0.3.1"): + pytest.skip("metadata output not supported in vyper < 0.3.2") + output = vvm.compile_files([foo_path], output_format="metadata") + assert "function_info" in output diff --git a/tests/test_install.py b/tests/test_install.py index 21dea44..719104a 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,6 +1,6 @@ import vvm -def test_get_installed_vyper_versions(all_versions): - assert "exe" not in str(all_versions) - assert all_versions in vvm.install.get_installed_vyper_versions() +def test_get_installed_vyper_versions(vyper_version): + assert "exe" not in str(vyper_version) + assert vyper_version in vvm.install.get_installed_vyper_versions() diff --git a/vvm/main.py b/vvm/main.py index 8e6ea50..29aed4a 100644 --- a/vvm/main.py +++ b/vvm/main.py @@ -28,8 +28,9 @@ def compile_source( base_path: Union[Path, str] = None, evm_version: str = None, vyper_binary: Union[str, Path] = None, - vyper_version: Version = None, -) -> Dict: + vyper_version: Union[str, Version, None] = None, + output_format: str = None, +) -> Any: """ Compile a Vyper contract. @@ -51,25 +52,32 @@ def compile_source( vyper_version: Version, optional `vyper` version to use. If not given, the currently active version is used. Ignored if `vyper_binary` is also given. + output_format: str, optional + Output format of the compiler. See `vyper --help` for more information. Returns ------- - Dict - Compiler output. The source file name is given as ``. + Any + Compiler output (depends on `output_format`). + For JSON output the return type is a dictionary, otherwise it is a string. """ - source_path = tempfile.mkstemp(suffix=".vy", prefix="vyper-", text=True)[1] - with open(source_path, "w") as fp: - fp.write(source) - compiler_data = _compile( - vyper_binary=vyper_binary, - vyper_version=vyper_version, - source_files=[source_path], - base_path=base_path, - evm_version=evm_version, - ) + with tempfile.NamedTemporaryFile(suffix=".vy", prefix="vyper-") as source_file: + source_file.write(source.encode()) + source_file.flush() + + compiler_data = _compile( + vyper_binary=vyper_binary, + vyper_version=vyper_version, + source_files=[source_file.name], + base_path=base_path, + evm_version=evm_version, + output_format=output_format, + ) - return {"": list(compiler_data.values())[0]} + if output_format in ("combined_json", None): + return {"": list(compiler_data.values())[0]} + return compiler_data def compile_files( @@ -77,8 +85,9 @@ def compile_files( base_path: Union[Path, str] = None, evm_version: str = None, vyper_binary: Union[str, Path] = None, - vyper_version: Version = None, -) -> Dict: + vyper_version: Union[str, Version, None] = None, + output_format: str = None, +) -> Any: """ Compile one or more Vyper source files. @@ -100,11 +109,14 @@ def compile_files( vyper_version: Version, optional `vyper` version to use. If not given, the currently active version is used. Ignored if `vyper_binary` is also given. + output_format: str, optional + Output format of the compiler. See `vyper --help` for more information. Returns ------- - Dict - Compiler output + Any + Compiler output (depends on `output_format`). + For JSON output the return type is a dictionary, otherwise it is a string. """ return _compile( vyper_binary=vyper_binary, @@ -112,24 +124,30 @@ def compile_files( source_files=source_files, base_path=base_path, evm_version=evm_version, + output_format=output_format, ) def _compile( base_path: Union[str, Path, None], vyper_binary: Union[str, Path, None], - vyper_version: Optional[Version], + vyper_version: Union[str, Version, None], + output_format: Optional[str], **kwargs: Any, -) -> Dict: +) -> Any: if vyper_binary is None: vyper_binary = get_executable(vyper_version) + if output_format is None: + output_format = "combined_json" stdoutdata, stderrdata, command, proc = wrapper.vyper_wrapper( - vyper_binary=vyper_binary, f="combined_json", p=base_path, **kwargs + vyper_binary=vyper_binary, f=output_format, p=base_path, **kwargs ) - return json.loads(stdoutdata) + if output_format in ("combined_json", "standard_json", "metadata"): + return json.loads(stdoutdata) + return stdoutdata def compile_standard(