diff --git a/boa/contracts/vyper/compiler_utils.py b/boa/contracts/vyper/compiler_utils.py index f73cb784..3711d426 100644 --- a/boa/contracts/vyper/compiler_utils.py +++ b/boa/contracts/vyper/compiler_utils.py @@ -1,4 +1,8 @@ import textwrap +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + pass import vyper.ast as vy_ast import vyper.semantics.analysis as analysis diff --git a/boa/interpret.py b/boa/interpret.py index e4707347..40520af8 100644 --- a/boa/interpret.py +++ b/boa/interpret.py @@ -20,7 +20,7 @@ CompilerInput, FileInput, FilesystemInputBundle, - ZipInputBundle, + JSONInputBundle, ) from vyper.compiler.phases import CompilerData from vyper.compiler.settings import Settings, anchor_settings, merge_settings @@ -165,9 +165,6 @@ def get_compiler_data(): # force compilation to happen so DiskCache will cache the compiled artifact: _ = ret.bytecode, ret.bytecode_runtime - if isinstance(ret.input_bundle, ZipInputBundle): - # workaround for `cannot pickle '_thread.RLock' object` - ret.input_bundle.archive._lock = None return ret assert isinstance(deployer, type) or deployer is None @@ -326,39 +323,51 @@ def _create_compiler_data( def _create_archive_compiler_data( zip_contents: str | bytes, settings: Settings ) -> CompilerData: - if isinstance(zip_contents, str): - zip_contents = zip_contents.encode() - try: - buf = BytesIO(zip_contents) - archive = ZipFile(buf, mode="r") - except BadZipFile as e1: - try: - # don't validate base64 to allow for newlines - zip_contents = b64decode(zip_contents, validate=False) - buf = BytesIO(zip_contents) - archive = ZipFile(buf, mode="r") - except (BadZipFile, binascii.Error): - raise NotZipInput() from e1 + with _open_zip(zip_contents) as archive: + # read the whole zip into memory so it can be serialized to the cache + files = {name: archive.read(name).decode() for name in archive.namelist()} - targets = archive.read("MANIFEST/compilation_targets").decode().splitlines() + targets = files["MANIFEST/compilation_targets"].splitlines() if len(targets) != 1: raise BadArchive("Multiple compilation targets not supported!") - input_bundle = ZipInputBundle(archive) + input_bundle = JSONInputBundle( + input_json={ + PurePath(name): {"content": content} for name, content in files.items() + }, + search_paths=[PurePath(p) for p in files["MANIFEST/searchpaths"].splitlines()], + ) + main_path = PurePath(targets[0]) - archive_settings_txt = archive.read("MANIFEST/settings.json").decode() file = input_bundle.load_file(main_path) assert isinstance(file, FileInput) # help mypy + settings_json = json.loads(files["MANIFEST/settings.json"]) settings = merge_settings( settings, - Settings.from_dict(json.loads(archive_settings_txt)), + Settings.from_dict(settings_json), lhs_source="command line", rhs_source="archive settings", ) - integrity_sum = archive.read("MANIFEST/integrity").decode().strip() + integrity_sum = files["MANIFEST/integrity"].strip() return CompilerData(file, input_bundle, settings, integrity_sum) +def _open_zip(zip_contents): + if isinstance(zip_contents, str): + zip_contents = zip_contents.encode() + try: + buf = BytesIO(zip_contents) + return ZipFile(buf, mode="r") + except BadZipFile as e1: + try: + # don't validate base64 to allow for newlines + zip_contents = b64decode(zip_contents, validate=False) + buf = BytesIO(zip_contents) + return ZipFile(buf, mode="r") + except (BadZipFile, binascii.Error): + raise NotZipInput() from e1 + + def from_etherscan( address: Any, name: str = None, uri: str = None, api_key: str = None ):