Skip to content

Commit

Permalink
Merge pull request #96 from iamdefinitelyahuman/refactor-wrapper
Browse files Browse the repository at this point in the history
Refactor wrapper and main compile functions
  • Loading branch information
iamdefinitelyahuman authored Jul 25, 2020
2 parents ac8d405 + ce113ed commit f077941
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 188 deletions.
2 changes: 1 addition & 1 deletion solcx/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class SolcError(Exception):

def __init__(
self,
command: str,
command: list,
return_code: int,
stdin_data: Optional[str],
stdout_data: str,
Expand Down
142 changes: 105 additions & 37 deletions solcx/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import functools
import json
import re
Expand Down Expand Up @@ -31,7 +29,13 @@ def get_solc_version() -> Version:
return Version(strip_zeroes_from_month_and_day(version_string))


def _parse_compiler_output(stdoutdata):
def _get_combined_json_outputs() -> str:
help_str = solc_wrapper(help=True)[0].split("\n")
combined_json_args = next(i for i in help_str if i.startswith(" --combined-json"))
return combined_json_args.split(" ")[-1]


def _parse_compiler_output(stdoutdata) -> dict:
output = json.loads(stdoutdata)

contracts = output.get("contracts", {})
Expand All @@ -47,29 +51,47 @@ def _parse_compiler_output(stdoutdata):
return contracts


ALL_OUTPUT_VALUES = (
"abi",
"asm",
"ast",
"bin",
"bin-runtime",
"clone-bin",
"devdoc",
"opcodes",
"userdoc",
)


def compile_source(source, allow_empty=False, output_values=ALL_OUTPUT_VALUES, **kwargs):
if "stdin" in kwargs:
raise ValueError("The `stdin` keyword is not allowed in the `compile_source` function")
if "combined_json" in kwargs:
raise ValueError(
"The `combined_json` keyword is not allowed in the `compile_source` function"
)

combined_json = ",".join(output_values)
compiler_kwargs = dict(stdin=source, combined_json=combined_json, **kwargs)
def compile_source(
source: str,
output_values: list = None,
import_remappings: list = None,
base_path: str = None,
allow_paths: list = None,
output_dir: str = None,
overwrite: bool = False,
evm_version: str = None,
revert_strings: bool = False,
metadata_hash: str = None,
metadata_literal: bool = False,
optimize: bool = False,
optimize_runs: int = None,
no_optimize_yul: bool = False,
yul_optimizations: int = None,
allow_empty: bool = False,
) -> dict:

if output_values is None:
combined_json = _get_combined_json_outputs()
else:
combined_json = ",".join(output_values)

compiler_kwargs = dict(
stdin=source,
combined_json=combined_json,
import_remappings=import_remappings,
base_path=base_path,
allow_paths=allow_paths,
output_dir=output_dir,
overwrite=overwrite,
evm_version=evm_version,
revert_strings=revert_strings,
metadata_hash=metadata_hash,
metadata_literal=metadata_literal,
optimize=optimize,
optimize_runs=optimize_runs,
no_optimize_yul=no_optimize_yul,
yul_optimizations=yul_optimizations,
)

stdoutdata, stderrdata, command, proc = solc_wrapper(**compiler_kwargs)

Expand All @@ -86,14 +108,46 @@ def compile_source(source, allow_empty=False, output_values=ALL_OUTPUT_VALUES, *
return contracts


def compile_files(source_files, allow_empty=False, output_values=ALL_OUTPUT_VALUES, **kwargs):
if "combined_json" in kwargs:
raise ValueError(
"The `combined_json` keyword is not allowed in the `compile_files` function"
)

combined_json = ",".join(output_values)
compiler_kwargs = dict(source_files=source_files, combined_json=combined_json, **kwargs)
def compile_files(
source_files: str,
output_values: list = None,
import_remappings: list = None,
base_path: str = None,
allow_paths: list = None,
output_dir: str = None,
overwrite: bool = False,
evm_version: str = None,
revert_strings: bool = False,
metadata_hash: str = None,
metadata_literal: bool = False,
optimize: bool = False,
optimize_runs: int = None,
no_optimize_yul: bool = False,
yul_optimizations: int = None,
allow_empty: bool = False,
) -> dict:
if output_values is None:
combined_json = _get_combined_json_outputs()
else:
combined_json = ",".join(output_values)

compiler_kwargs = dict(
source_files=source_files,
combined_json=combined_json,
import_remappings=import_remappings,
base_path=base_path,
allow_paths=allow_paths,
output_dir=output_dir,
overwrite=overwrite,
evm_version=evm_version,
revert_strings=revert_strings,
metadata_hash=metadata_hash,
metadata_literal=metadata_literal,
optimize=optimize,
optimize_runs=optimize_runs,
no_optimize_yul=no_optimize_yul,
yul_optimizations=yul_optimizations,
)

stdoutdata, stderrdata, command, proc = solc_wrapper(**compiler_kwargs)

Expand All @@ -110,7 +164,14 @@ def compile_files(source_files, allow_empty=False, output_values=ALL_OUTPUT_VALU
return contracts


def compile_standard(input_data, allow_empty=False, **kwargs):
def compile_standard(
input_data,
base_path: str = None,
allow_paths: list = None,
output_dir: str = None,
overwrite: bool = False,
allow_empty=False,
):
if not input_data.get("sources") and not allow_empty:
raise ContractsNotFound(
command=None,
Expand All @@ -120,10 +181,17 @@ def compile_standard(input_data, allow_empty=False, **kwargs):
stderr_data=None,
)

stdoutdata, stderrdata, command, proc = solc_wrapper(
stdin=json.dumps(input_data), standard_json=True, **kwargs
compiler_kwargs = dict(
stdin=json.dumps(input_data),
standard_json=True,
base_path=base_path,
allow_paths=allow_paths,
output_dir=output_dir,
overwrite=overwrite,
)

stdoutdata, stderrdata, command, proc = solc_wrapper(**compiler_kwargs)

compiler_output = json.loads(stdoutdata)
if "errors" in compiler_output:
has_errors = any(error["severity"] == "error" for error in compiler_output["errors"])
Expand Down
175 changes: 37 additions & 138 deletions solcx/wrapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import absolute_import

import subprocess
from typing import Any

from semantic_version import Version

Expand All @@ -9,155 +8,44 @@


def solc_wrapper(
solc_binary=None,
stdin=None,
help=None,
version=None,
combined_json=None,
optimize=None,
optimize_runs=None,
libraries=None,
output_dir=None,
gas=None,
assemble=None,
link=None,
source_files=None,
import_remappings=None,
ast=None,
ast_json=None,
asm=None,
asm_json=None,
opcodes=None,
bin=None,
bin_runtime=None,
clone_bin=None,
abi=None,
hashes=None,
userdoc=None,
devdoc=None,
formal=None,
allow_paths=None,
base_path=None,
standard_json=None,
success_return_code=None,
evm_version=None,
solc_binary: str = None,
stdin: str = None,
source_files: list = None,
import_remappings: list = None,
success_return_code: int = None,
**kwargs: Any,
):
if solc_binary is None:
solc_binary = get_executable()

command = [solc_binary]

solc_version = Version(solc_binary.rsplit("-v")[-1].split("\\")[0])
solc_minor = solc_version.minor

if help:
command.append("--help")
if success_return_code is None:
success_return_code = 1
if "help" in kwargs:
success_return_code = 1
elif success_return_code is None:
success_return_code = 0

if version:
command.append("--version")

if optimize:
command.append("--optimize")

if optimize_runs is not None:
command.extend(("--optimize-runs", str(optimize_runs)))

if link:
command.append("--link")

if libraries is not None:
command.extend(("--libraries", libraries))

if output_dir is not None:
command.extend(("--output-dir", output_dir))

if combined_json:
if solc_minor >= 5:
combined_json = combined_json.replace(",clone-bin", "")
command.extend(("--combined-json", combined_json))

if gas:
command.append("--gas")

if allow_paths:
command.extend(("--allow-paths", allow_paths))

if standard_json:
command.append("--standard-json")

if assemble:
command.append("--assemble")

if import_remappings is not None:
command.extend(import_remappings)

if source_files is not None:
command.extend(source_files)

# Output configuration
if ast_json:
command.append("--ast-json")

if asm:
command.append("--asm")

if asm_json:
command.append("--asm-json")

if opcodes:
command.append("--opcodes")

if bin:
command.append("--bin")

if bin_runtime:
command.append("--bin-runtime")

if abi:
command.append("--abi")

if hashes:
command.append("--hashes")

if userdoc:
command.append("--userdoc")

if devdoc:
command.append("--devdoc")

if evm_version:
command.extend(("--evm-version", evm_version))

# unsupported by <0.6.9
if base_path:
if solc_version <= Version("0.6.8"):
raise AttributeError(
"solc {} does not support the --base-path flag".format(solc_version)
)
command.extend(("--base-path", base_path))

# unsupported by >=0.6.0
if ast:
if solc_minor >= 6:
raise AttributeError(f"solc 0.{solc_minor}.x does not support the --ast flag")
command.append("--ast")

# unsupported by >=0.5.0
if clone_bin:
if solc_minor >= 5:
raise AttributeError(f"solc 0.{solc_minor}.x does not support the --clone-bin flag")
command.append("--clone-bin")

if formal:
if solc_minor >= 5:
raise AttributeError(f"solc 0.{solc_minor}.x does not support the --formal flag")
command.append("--formal")
if import_remappings is not None:
command.extend(import_remappings)

if not standard_json and not source_files:
for key, value in kwargs.items():
if value is None or value is False:
continue

key = f"--{key.replace('_', '-')}"
if value is True:
command.append(key)
elif isinstance(value, (int, str)):
command.extend([key, str(value)])
elif isinstance(value, (list, tuple)):
command.extend([key, ",".join(str(i) for i in value)])
else:
raise TypeError(f"Invalid type for {key}: {type(value)}")

if "standard_json" not in kwargs and not source_files:
# indicates that solc should read from stdin
command.append("-")

Expand All @@ -175,6 +63,17 @@ def solc_wrapper(
stdoutdata, stderrdata = proc.communicate(stdin)

if proc.returncode != success_return_code:
solc_version = Version(solc_binary.rsplit("-v")[-1].split("\\")[0])
if stderrdata.startswith("unrecognised option"):
# unrecognised option '<FLAG>'
flag = stderrdata.split("'")[1]
raise AttributeError(f"solc {solc_version} - unsupported flag: {flag}")
if stderrdata.startswith("Invalid option"):
# Invalid option to <FLAG>: <OPTION>
flag, option = stderrdata.split(": ")
flag = flag.split(" ")[-1]
raise ValueError(f"solc {solc_version} - invalid option for {flag} flag: {option}")

raise SolcError(
command=command,
return_code=proc.returncode,
Expand Down
Loading

0 comments on commit f077941

Please sign in to comment.