Skip to content

Commit

Permalink
use semantic-version for setting and installing by pragma, update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
iamdefinitelyahuman committed Jul 27, 2019
1 parent 95bdbed commit 1c7d567
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 85 deletions.
128 changes: 44 additions & 84 deletions solcx/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
Install solc
"""
from io import BytesIO
import operator
import os
from pathlib import Path
import re
import requests
from semantic_version import Version, Spec
import shutil
import stat
import subprocess
Expand Down Expand Up @@ -109,37 +109,36 @@ def set_solc_version(version, silent=False):
print("Using solc version {}".format(solc_version))


def set_solc_version_pragma(version, silent=False):
version = version.strip()
comparator_set_range = [i.strip() for i in version.split('||')]
installed_versions = get_installed_solc_versions()
comparator_regex = re.compile(
r'(?P<operator>([<>]?=?|\^))(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+))'
def set_solc_version_pragma(pragma_string, silent=False, check_new=False):
version = _select_pragma_version(
pragma_string,
[Version(i[1:]) for i in get_installed_solc_versions()]
)
range_flag = False
set_version = None
for installed_version in reversed(installed_versions):
for comparator_set in comparator_set_range:
comparators = [m.groupdict() for m in comparator_regex.finditer(comparator_set)]
comparator_set_flag = True
for comparator in comparators:
operator = comparator['operator']
if not _compare_versions(installed_version, comparator['version'], operator):
comparator_set_flag = False
if comparator_set_flag:
range_flag = True
if range_flag:
set_version = installed_version
newer_version = install_solc_pragma(version, install=False)
if not silent and _compare_versions(set_version, newer_version, '<'):
print("Newer compatible solc version exists: {}".format(newer_version))
break
if not set_version:
set_version = install_solc_pragma(version)
if not version:
raise SolcNotInstalled(
"No compatible solc version installed. " +
"Use solcx.install_solc_version_pragma('{}') to install.".format(version)
)
global solc_version
solc_version = set_version
solc_version = version
if not silent:
print("Using solc version {}".format(solc_version))
if check_new:
latest = install_solc_pragma(pragma_string, False)
if Version(latest) > Version(version):
print("Newer compatible solc version exists: {}".format(latest))


def install_solc_pragma(pragma_string, install=True):
version = _select_pragma_version(
pragma_string,
[Version(i[1:]) for i in get_available_solc_versions()]
)
if not version:
raise ValueError("Compatible solc version does not exist")
if install:
install_solc(version)
return version


def get_available_solc_versions(headers={}):
Expand All @@ -154,6 +153,20 @@ def get_available_solc_versions(headers={}):
return versions


def _select_pragma_version(pragma_string, version_list):
comparator_set_range = pragma_string.replace(" ", "").split('||')
comparator_regex = re.compile(r"(([<>]?=?|\^)\d+\.\d+\.\d+)+")
version = None

for comparator_set in comparator_set_range:
spec = Spec(*(i[0] for i in comparator_regex.findall(comparator_set)))
selected = spec.select(version_list)
if selected and (not version or version < selected):
version = selected
if version:
return str(version)


def get_installed_solc_versions():
return sorted(i.name[5:] for i in get_solc_folder().glob('solc-v*'))

Expand All @@ -177,64 +190,11 @@ def install_solc(version, allow_osx=False):
print("solc {} successfully installed at: {}".format(version, binary_path))


def install_solc_pragma(version, install=True):
version = version.strip()
comparator_set_range = [i.strip() for i in version.split('||')]
comparator_regex = re.compile(
r'(?P<operator>([<>]?=?|\^))(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+))'
)
versions_json = requests.get(ALL_RELEASES).json()
range_flag = False
for version_json in versions_json:
for comparator_set in comparator_set_range:
comparators = [m.groupdict() for m in comparator_regex.finditer(comparator_set)]
comparator_set_flag = True
for comparator in comparators:
operator = comparator['operator']
if not _compare_versions(version_json['tag_name'], comparator['version'], operator):
comparator_set_flag = False
if comparator_set_flag:
range_flag = True
if range_flag:
_check_version(version_json['tag_name'])
if install:
install_solc(version_json['tag_name'])
return version_json['tag_name']
raise ValueError("Compatible solc version does not exist")


operator_map = {
'<': operator.lt,
'<=': operator.le,
'>=': operator.ge,
'>': operator.gt,
'^': operator.ge
}


def _compare_versions(v1, v2, comp='='):
v1 = v1.lstrip('v')
v2 = v2.lstrip('v')
v1_split = [int(i) for i in v1.split('.')]
v2_split = [int(i) for i in v2.split('.')]
if comp in ('=', '==', '', None):
return v1_split == v2_split
if comp not in operator_map:
raise ValueError("operator {} not supported".format(comp))
idx = next((i for i in range(3) if v1_split[i] != v2_split[i]), 2)
if comp == '^' and idx != 2:
return False
return operator_map[comp](v1_split[idx], v2_split[idx])


def _check_version(version):
version = "v0." + version.lstrip("v0.")
if version.count('.') != 2:
raise ValueError("Invalid solc version '{}' - must be in the format v0.x.x".format(version))
v = [int(i) for i in version[1:].split('.')]
if v[1] < 4 or (v[1] == 4 and v[2] < 11):
version = Version(version.lstrip('v'))
if version not in Spec('>=0.4.11'):
raise ValueError("py-solc-x does not support solc versions <0.4.11")
return version
return "v" + str(version)


def _check_subprocess_call(command, message=None, verbose=True, **proc_kwargs):
Expand Down
49 changes: 48 additions & 1 deletion tests/test_solc_version.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
#!/usr/bin/python3

import functools
import pytest
from semantic_version import Version

import solcx
from solcx.exceptions import SolcNotInstalled

from semantic_version import Version

@pytest.fixture
def pragmapatch(monkeypatch):
monkeypatch.setattr(
'solcx.install.get_installed_solc_versions',
lambda: ['v0.4.2', 'v0.4.11', 'v0.4.25', 'v0.5.0', 'v0.5.4', 'v0.5.7', 'v1.2.3']
)
monkeypatch.setattr(
'solcx.install.get_available_solc_versions',
lambda: ['v0.4.11', 'v0.4.24', 'v0.4.26', 'v0.5.3', 'v0.6.0']
)


def test_solc_supports_standard_json_interface(monkeypatch):
Expand All @@ -22,3 +37,35 @@ def test_get_solc_version(all_versions):
def test_get_solc_version_string(all_versions):
v = solcx.get_solc_version_string()
assert isinstance(v, str)


def test_set_solc_version_pragma(pragmapatch):
set_pragma = functools.partial(solcx.set_solc_version_pragma, check_new=True)
set_pragma('pragma solidity 0.4.11;')
assert solcx.install.solc_version == '0.4.11'
set_pragma('pragma solidity ^0.4.11;')
assert solcx.install.solc_version == '0.4.25'
set_pragma('pragma solidity >=0.4.0<0.4.25;')
assert solcx.install.solc_version == '0.4.11'
set_pragma('pragma solidity >=0.4.2;')
assert solcx.install.solc_version == '1.2.3'
set_pragma('pragma solidity >=0.4.2<0.5.5;')
assert solcx.install.solc_version == '0.5.4'
set_pragma('pragma solidity ^0.4.2 || 0.5.5;')
assert solcx.install.solc_version == '0.4.25'
set_pragma('pragma solidity ^0.4.2 || >=0.5.4<0.7.0;')
assert solcx.install.solc_version == '0.5.7'
with pytest.raises(SolcNotInstalled):
set_pragma('pragma solidity ^0.7.1;')


def test_install_solc_version_pragma(pragmapatch):
install_pragma = functools.partial(solcx.install_solc_pragma, install=False)
assert install_pragma('pragma solidity ^0.4.11;') == "0.4.26"
assert install_pragma('pragma solidity >=0.4.0<0.4.25;') == "0.4.24"
assert install_pragma('pragma solidity >=0.4.2;') == "0.6.0"
assert install_pragma('pragma solidity >=0.4.2<0.5.5;') == "0.5.3"
assert install_pragma('pragma solidity ^0.4.2 || 0.5.5;') == "0.4.26"
assert install_pragma('pragma solidity ^0.4.2 || >=0.5.4<0.7.0;') == "0.6.0"
with pytest.raises(ValueError):
install_pragma('pragma solidity ^0.7.1;')

0 comments on commit 1c7d567

Please sign in to comment.