Skip to content

Commit

Permalink
Merge pull request #13 from iamdefinitelyahuman/v0.4.2
Browse files Browse the repository at this point in the history
V0.4.2
  • Loading branch information
iamdefinitelyahuman authored Jul 27, 2019
2 parents 48c8f14 + b1ff12f commit 9f9a6cd
Show file tree
Hide file tree
Showing 24 changed files with 358 additions and 658 deletions.
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ matrix:
- sudo apt-get update
- sudo apt-get install -y solc
- pip install -r requirements-dev.txt
script: python -m pytest tests --cov=solcx
after_success: python -m coveralls
- name: "Python 3.7.3 on Windows"
os: windows # Windows 10.0.17134 N/A Build 17134
language: shell # 'language: python' is an error on Travis CI Windows
Expand All @@ -32,8 +34,8 @@ matrix:
- python -m pip install --upgrade pip
- pip3 install -r requirements-dev.txt
env: PATH=/c/Python37:/c/Python37/Scripts:$PATH
script: python -m pytest tests --cov=solcx
after_success: python -m coveralls

script: python -m pytest tests --cov=solcx
after_success: python -m coveralls
notifications:
email: false
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
0.4.2
-----

- Fix link_code to support 0.5.x
- Remove trailing whitespace on solcx.get_version_string - fixes windows 0.5.x bug

0.4.1
-----

Expand Down
42 changes: 26 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Py-solc-x allows the use of multiple versions of solc and installs them as neede

## Supported Versions

Py-solc-x can install the following `solc` versions:
Py-solc-x can install the following solc versions:

* Linux and Windows: `>=0.4.11`
* OSX: `>=0.5.0`
Expand Down Expand Up @@ -52,18 +52,15 @@ Version('0.5.7+commit.6da8b019.Linux.gpp')
>>>
```

To install the highest compatible version based on the pragma version string:

```python
>>> from solcx import install_solc_pragma
>>> install_solc_pragma('^0.4.20 || >0.5.5 <0.7.0')
```

To set the version based on the pragma version string - this will use the highest compatible version installed, if you have a compatible version installed, or it will install the highest compatible version:
You can also set the version based on the pragma version string. The highest compatible version will be used:

```python
>>> from solcx import set_solc_version_pragma
>>> set_solc_version_pragma('^0.4.20 || >0.5.5 <0.7.0')
Using solc version 0.5.8
>>> set_solc_version_pragma('^0.4.20 || >0.5.5 <0.7.0', check_new=True)
Using solc version 0.5.8
Newer compatible solc version exists: 0.5.10
```

To view available and installed versions:
Expand All @@ -73,7 +70,14 @@ To view available and installed versions:
>>> get_installed_solc_versions()
['v0.4.25', 'v0.5.3']
>>> get_available_solc_versions()
['v0.5.8', 'v0.5.7', 'v0.5.6', 'v0.5.5', 'v0.5.4', 'v0.5.3', 'v0.5.2', 'v0.5.1', 'v0.5.0', 'v0.4.25', 'v0.4.24', 'v0.4.23', 'v0.4.22', 'v0.4.21', 'v0.4.20', 'v0.4.19', 'v0.4.18', 'v0.4.17', 'v0.4.16', 'v0.4.15', 'v0.4.14', 'v0.4.13', 'v0.4.12', 'v0.4.11']
['v0.5.10', 'v0.5.9', 'v0.5.8', 'v0.5.7', 'v0.5.6', 'v0.5.5', 'v0.5.4', 'v0.5.3', 'v0.5.2', 'v0.5.1', 'v0.5.0', 'v0.4.25', 'v0.4.24', 'v0.4.23', 'v0.4.22', 'v0.4.21', 'v0.4.20', 'v0.4.19', 'v0.4.18', 'v0.4.17', 'v0.4.16', 'v0.4.15', 'v0.4.14', 'v0.4.13', 'v0.4.12', 'v0.4.11']
```

To install the highest compatible version based on the pragma version string:

```python
>>> from solcx import install_solc_pragma
>>> install_solc_pragma('^0.4.20 || >0.5.5 <0.7.0')
```

## Standard JSON Compilation
Expand Down Expand Up @@ -105,7 +109,7 @@ Use the `solcx.compile_standard` function to make use of the [standard-json](htt
## Legacy Combined JSON compilation

```python
>>> from solcx import compile_source, compile_files, link_code
>>> from solcx import compile_source, compile_files
>>> compile_source("contract Foo { function Foo() {} }")
{
'Foo': {
Expand Down Expand Up @@ -145,8 +149,14 @@ Use the `solcx.compile_standard` function to make use of the [standard-json](htt
},
},
}
>>> unlinked_code = "606060405260768060106000396000f3606060405260e060020a6000350463e7f09e058114601a575b005b60187f0c55699c00000000000000000000000000000000000000000000000000000000606090815273__TestA_________________________________90630c55699c906064906000906004818660325a03f41560025750505056"
>>> link_code(unlinked_code, {'TestA': '0xd3cda913deb6f67967b99d67acdfa1712c293601'})
```

## Unlinked Libraries

```python
>>> from solcx import link_code
>>> unlinked_bytecode = "606060405260768060106000396000f3606060405260e060020a6000350463e7f09e058114601a575b005b60187f0c55699c00000000000000000000000000000000000000000000000000000000606090815273__TestA_________________________________90630c55699c906064906000906004818660325a03f41560025750505056"
>>> link_code(unlinked_bytecode, {'TestA': '0xd3cda913deb6f67967b99d67acdfa1712c293601'})
... "606060405260768060106000396000f3606060405260e060020a6000350463e7f09e058114601a575b005b60187f0c55699c00000000000000000000000000000000000000000000000000000000606090815273d3cda913deb6f67967b99d67acdfa1712c29360190630c55699c906064906000906004818660325a03f41560025750505056"
```

Expand All @@ -157,7 +167,7 @@ Use the `solcx.compile_standard` function to make use of the [standard-json](htt
You can use this like:

```python
>>> from solcx import compile_source, compile_files, link_code
>>> from solcx import compile_files

>>> compile_files([source_file_path], import_remappings=["zeppeling=/my-zeppelin-checkout-folder"])
```
Expand All @@ -170,14 +180,14 @@ This project was recently forked from [py-solc](https://github.com/ethereum/py-s

### Tests

Py-solc-x is tested on Linux and Windows with solc versions ``>=0.4.11``.

To run the test suite:

```bash
$ pytest tests/
```

Tests are still a work in progress. The old (some failing) ``py-solc`` tests are available [here](https://github.com/iamdefinitelyahuman/py-solc-x/tree/master/tests-old).

## License

This project is licensed under the [MIT license](LICENSE).
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setup(
name='py-solc-x',
version='0.4.1',
version='0.4.2',
description="""Python wrapper around the solc binary with 0.5.x support""",
long_description_markdown_filename='README.md',
author='Ben Hauser (forked from py-solc by Piper Merriam)',
Expand Down
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
8 changes: 4 additions & 4 deletions solcx/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get_solc_version_string(**kwargs):
stderr_data=stderrdata,
message="Unable to extract version string from command output",
)
return version_string
return version_string.rstrip()


def get_solc_version(**kwargs):
Expand Down Expand Up @@ -182,15 +182,15 @@ def compile_standard(input_data, allow_empty=False, **kwargs):
return compiler_output


def link_code(unlinked_data, libraries):
def link_code(unlinked_bytecode, libraries):
libraries_arg = ','.join((
':'.join((lib_name, lib_address))
for lib_name, lib_address in libraries.items()
))
stdoutdata, stderrdata, _, _ = solc_wrapper(
stdin=unlinked_data,
stdin=unlinked_bytecode,
link=True,
libraries=libraries_arg,
)

return stdoutdata.strip()
return stdoutdata.replace("Linking completed.", "").strip()
4 changes: 2 additions & 2 deletions solcx/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ def solc_wrapper(solc_binary=None,
command.extend(('--evm-version', evm_version))

if (
standard_json is None and
source_files is None and
not standard_json and
not source_files and
"v0.5" in command[0]
):
command.append('-')
Expand Down
Loading

0 comments on commit 9f9a6cd

Please sign in to comment.