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

fix: vvm version detection #343

Merged
merged 8 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
13 changes: 0 additions & 13 deletions boa/contracts/vvm/vvm_contract.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
import re
from functools import cached_property

from boa.contracts.abi.abi_contract import ABIContractFactory, ABIFunction
from boa.environment import Env
from boa.util.eip5202 import generate_blueprint_bytecode

# TODO: maybe this doesn't detect release candidates
VERSION_RE = re.compile(r"\s*#\s*(pragma\s+version|@version)\s+(\d+\.\d+\.\d+)")


# TODO: maybe move this up to vvm?
def _detect_version(source_code: str):
res = VERSION_RE.findall(source_code)
if len(res) < 1:
return None
# TODO: handle len(res) > 1
return res[0][1]


class VVMDeployer:
"""
Expand Down
20 changes: 14 additions & 6 deletions boa/interpret.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import vvm
import vyper
from packaging.specifiers import SpecifierSet
from vyper.ast.parse import parse_to_ast
from vyper.cli.vyper_compile import get_search_paths
from vyper.compiler.input_bundle import (
Expand All @@ -23,7 +24,7 @@
from vyper.utils import sha256sum

from boa.contracts.abi.abi_contract import ABIContractFactory
from boa.contracts.vvm.vvm_contract import VVMDeployer, _detect_version
from boa.contracts.vvm.vvm_contract import VVMDeployer
from boa.contracts.vyper.vyper_contract import (
VyperBlueprint,
VyperContract,
Expand All @@ -34,6 +35,7 @@
from boa.rpc import json
from boa.util.abi import Address
from boa.util.disk_cache import DiskCache
from boa.util.version import detect_version

if TYPE_CHECKING:
from vyper.semantics.analysis.base import ImportInfo
Expand Down Expand Up @@ -249,12 +251,11 @@ def loads_partial(
if dedent:
source_code = textwrap.dedent(source_code)

version = _detect_version(source_code)
if version is not None and version != vyper.__version__:
version_set = detect_version(source_code)
if version_set is not None and not version_set.contains(vyper.__version__):
filename = str(filename) # help mypy
# TODO: pass name to loads_partial_vvm, not filename
return _loads_partial_vvm(source_code, version, filename)

return _loads_partial_vvm(source_code, version_set, filename)
compiler_args = compiler_args or {}

deployer_class = _get_default_deployer_class()
Expand All @@ -269,9 +270,16 @@ def load_partial(filename: str, compiler_args=None):
)


def _loads_partial_vvm(source_code: str, version: str, filename: str):
def _loads_partial_vvm(source_code: str, version_set: SpecifierSet, filename: str):
global _disk_cache

available = vvm.get_installable_vyper_versions()
# get the most recent version compatible with the set
version = next(version_set.filter(available), None)

if version is None:
raise ValueError(f"no matching version found for {version_set}")

# install the requested version if not already installed
vvm.install_vyper(version=version)

Expand Down
22 changes: 22 additions & 0 deletions boa/util/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import re
from typing import Optional

from packaging.specifiers import SpecifierSet

VERSION_RE = r"^(#\s*(@version|pragma\s+version)\s+(.*))"


def detect_version(source_code: str) -> Optional[SpecifierSet]:
res = re.findall(VERSION_RE, source_code, re.MULTILINE)
if len(res) < 1:
return None

# If there are multiple version pragmas, use the first one found and let Vyper fail compilation
version_str = res[0][2]

# X.Y.Z or vX.Y.Z => ==X.Y.Z, ==vX.Y.Z
if re.match("[v0-9]", version_str):
version_str = "==" + version_str
# convert npm to pep440
version_str = re.sub("^\\^", "~=", version_str)
return SpecifierSet(version_str)
14 changes: 14 additions & 0 deletions tests/unitary/contracts/vvm/test_vvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ def test_load_vvm():
assert contract.bar() == 43


def test_load_complex_version_vvm():
contracts = [
"# @version ^0.3.1",
"# @version ^0.3.7",
"# @version ==0.3.10",
"# pragma version >=0.3.8, <0.4.0, !=0.3.10",
# "# pragma version ==0.4.0rc3",
# TODO: uncomment when vvm is fixed (https://github.com/vyperlang/vvm/pull/29)
]
for contract in contracts:
contract = boa.loads(contract + "\nfoo: public(uint256)")
assert contract.foo() == 0


def test_loads_vvm():
with open(mock_3_10_path) as f:
code = f.read()
Expand Down
5 changes: 3 additions & 2 deletions tests/unitary/utils/test_cache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from unittest.mock import patch

import pytest
from packaging.specifiers import SpecifierSet
from vyper.compiler import CompilerData

from boa.contracts.vyper.vyper_contract import VyperDeployer
Expand Down Expand Up @@ -34,8 +35,8 @@ def test_cache_vvm():
code = """
x: constant(int128) = 1000
"""
version = "0.2.8"
version2 = "0.3.1"
version = SpecifierSet("==0.2.8")
version2 = SpecifierSet("==0.3.1")
assert _disk_cache is not None

# Mock vvm.compile_source
Expand Down
Loading