diff --git a/ethpm/backends/base.py b/ethpm/backends/base.py index 566f4ff..9f309f2 100644 --- a/ethpm/backends/base.py +++ b/ethpm/backends/base.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +from typing import Union from ethpm.typing import URI @@ -28,7 +29,7 @@ def can_translate_uri(self, uri: URI) -> bool: pass @abstractmethod - def fetch_uri_contents(self, uri: URI) -> bytes: + def fetch_uri_contents(self, uri: URI) -> Union[bytes, URI]: """ Fetch the contents stored at a URI. """ diff --git a/ethpm/backends/ipfs.py b/ethpm/backends/ipfs.py index 7e8b094..d7f1b2b 100644 --- a/ethpm/backends/ipfs.py +++ b/ethpm/backends/ipfs.py @@ -181,4 +181,9 @@ def get_ipfs_backend(import_path: str = None) -> BaseIPFSBackend: def get_ipfs_backend_class(import_path: str = None) -> Type[BaseIPFSBackend]: if import_path is None: import_path = os.environ.get("ETHPM_IPFS_BACKEND_CLASS", DEFAULT_IPFS_BACKEND) + if not import_path: + raise CannotHandleURI( + "Please provide an import class or set " + "`ETHPM_IPFS_BACKEND_CLASS` environment variable." + ) return import_string(import_path) diff --git a/ethpm/backends/registry.py b/ethpm/backends/registry.py index c514d7e..88b428d 100644 --- a/ethpm/backends/registry.py +++ b/ethpm/backends/registry.py @@ -5,6 +5,7 @@ from ethpm.backends.base import BaseURIBackend from ethpm.constants import INFURA_API_KEY +from ethpm.typing import URI from ethpm.utils.registry import fetch_standard_registry_abi from ethpm.utils.uri import parse_registry_uri from ethpm.validation import is_valid_registry_uri @@ -31,7 +32,7 @@ def can_translate_uri(self, uri: str) -> bool: def can_resolve_uri(self, uri: str) -> bool: return False - def fetch_uri_contents(self, uri: str) -> bytes: + def fetch_uri_contents(self, uri: str) -> URI: """ Return content-addressed URI stored at registry URI. """ diff --git a/ethpm/contract.py b/ethpm/contract.py index db0a0eb..8fa684e 100644 --- a/ethpm/contract.py +++ b/ethpm/contract.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Tuple, Type # noqa: F401 +from typing import Any, Dict, List, Optional, Tuple, Type # noqa: F401 from eth_utils import combomethod, is_canonical_address, to_bytes, to_checksum_address from eth_utils.toolz import assoc, curry, pipe @@ -15,11 +15,11 @@ class LinkableContract(Contract): contract factories with link references in their package's manifest. """ - unlinked_references: Tuple[Dict[str, Any]] = None - linked_references: Tuple[Dict[str, Any]] = None + unlinked_references: Optional[Tuple[Dict[str, Any]]] = None + linked_references: Optional[Tuple[Dict[str, Any]]] = None needs_bytecode_linking = None - def __init__(self, address: bytes = None, **kwargs: Any) -> None: + def __init__(self, address: bytes, **kwargs: Any) -> None: if self.needs_bytecode_linking: raise BytecodeLinkingError( "Contract cannot be instantiated until its bytecode is linked." @@ -90,13 +90,9 @@ def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None: "Unable to validate attr dict, this contract has no linked/unlinked references." ) - all_link_refs: Tuple[Any, ...] - if self.unlinked_references and self.linked_references: - all_link_refs = self.unlinked_references + self.linked_references - elif not self.unlinked_references: - all_link_refs = self.linked_references - else: - all_link_refs = self.unlinked_references + unlinked_refs = self.unlinked_references or ({},) + linked_refs = self.linked_references or ({},) + all_link_refs = unlinked_refs + linked_refs all_link_names = [ref["name"] for ref in all_link_refs] if set(attr_dict_names) != set(all_link_names): diff --git a/ethpm/deployments.py b/ethpm/deployments.py index 192cbe7..141ecaf 100644 --- a/ethpm/deployments.py +++ b/ethpm/deployments.py @@ -32,7 +32,7 @@ def __contains__(self, key: str) -> bool: def get(self, key: str) -> Dict[str, str]: self._validate_name_and_references(key) - return self.deployment_data.get(key) + return self.deployment_data[key] def items(self) -> ItemsView[str, Dict[str, str]]: item_dict = {name: self.get(name) for name in self.deployment_data} diff --git a/ethpm/package.py b/ethpm/package.py index 82710ed..b770276 100644 --- a/ethpm/package.py +++ b/ethpm/package.py @@ -1,6 +1,6 @@ import json from pathlib import Path -from typing import Any, Dict, Generator, Tuple, Union +from typing import Any, Dict, Generator, Optional, Tuple, Union from eth_utils import to_canonical_address, to_text, to_tuple from web3 import Web3 @@ -45,7 +45,9 @@ class Package(object): - def __init__(self, manifest: Dict[str, Any], w3: Web3, uri: str = None) -> None: + def __init__( + self, manifest: Dict[str, Any], w3: Web3, uri: Optional[str] = None + ) -> None: """ A package should be created using one of the available classmethods and a valid w3 instance. @@ -130,7 +132,7 @@ def manifest_version(self) -> str: return self.manifest["manifest_version"] @property - def uri(self) -> str: + def uri(self) -> Optional[str]: """ The uri (local file_path / content-addressed URI) of a ``Package``'s manifest. """ diff --git a/ethpm/utils/backend.py b/ethpm/utils/backend.py index c18cec8..e142529 100644 --- a/ethpm/utils/backend.py +++ b/ethpm/utils/backend.py @@ -32,7 +32,8 @@ def resolve_uri_contents(uri: URI, fingerprint: bool = None) -> bytes: if resolvable_backends: for backend in resolvable_backends: try: - contents = backend().fetch_uri_contents(uri) + # ignore type b/c resolvable backends only return uri contents + contents: bytes = backend().fetch_uri_contents(uri) # type: ignore except CannotHandleURI: continue return contents @@ -44,7 +45,7 @@ def resolve_uri_contents(uri: URI, fingerprint: bool = None) -> bytes: "Registry URIs must point to a resolvable content-addressed URI." ) package_id = RegistryURIBackend().fetch_uri_contents(uri) - return resolve_uri_contents(package_id, True) # type: ignore + return resolve_uri_contents(package_id, True) raise CannotHandleURI( f"URI: {uri} cannot be resolved by any of the available backends." diff --git a/ethpm/utils/deployments.py b/ethpm/utils/deployments.py index 2e4b1ca..547579a 100644 --- a/ethpm/utils/deployments.py +++ b/ethpm/utils/deployments.py @@ -39,8 +39,8 @@ def validate_linked_references( for idx, offset in enumerate(offsets): value = values[idx] # https://github.com/python/mypy/issues/4975 - offset_value = int(offset) # type: ignore - dep_length = len(value) # type: ignore + offset_value = int(offset) + dep_length = len(value) end_of_bytes = offset_value + dep_length # Ignore b/c whitespace around ':' conflict b/w black & flake8 actual_bytes = bytecode[offset_value:end_of_bytes] # noqa: E203 diff --git a/ethpm/utils/ipfs.py b/ethpm/utils/ipfs.py index 46fd75f..420fb20 100644 --- a/ethpm/utils/ipfs.py +++ b/ethpm/utils/ipfs.py @@ -3,6 +3,8 @@ from typing import Dict from urllib import parse +from google.protobuf.descriptor import Descriptor + from ethpm.pb.ipfs_file_pb2 import Data, PBNode from ethpm.utils.base58 import b58encode @@ -69,11 +71,14 @@ def multihash(value: bytes) -> bytes: return multihash_bytes -def serialize_bytes(file_bytes: bytes) -> PBNode: +def serialize_bytes(file_bytes: bytes) -> Descriptor: file_size = len(file_bytes) data_protobuf = Data( - Type=Data.DataType.Value("File"), Data=file_bytes, filesize=file_size + # type ignored b/c DataType is manually attached in ipfs_file_pb2.py + Type=Data.DataType.Value("File"), # type: ignore + Data=file_bytes, + filesize=file_size, ) data_protobuf_bytes = data_protobuf.SerializeToString() @@ -83,7 +88,8 @@ def serialize_bytes(file_bytes: bytes) -> PBNode: def generate_file_hash(content_bytes: bytes) -> str: - file_protobuf = serialize_bytes(content_bytes) - file_protobuf_bytes = file_protobuf.SerializeToString() + file_protobuf: Descriptor = serialize_bytes(content_bytes) + # type ignored b/c SerializeToString is manually attached in ipfs_file_pb2.py + file_protobuf_bytes = file_protobuf.SerializeToString() # type: ignore file_multihash = multihash(file_protobuf_bytes) return b58encode(file_multihash) diff --git a/ethpm/utils/manifest_validation.py b/ethpm/utils/manifest_validation.py index cf92618..788b030 100644 --- a/ethpm/utils/manifest_validation.py +++ b/ethpm/utils/manifest_validation.py @@ -112,7 +112,7 @@ def validate_manifest_exists(manifest_id: str) -> None: ) -def format_manifest(manifest: Manifest, *, prettify: bool) -> str: +def format_manifest(manifest: Manifest, *, prettify: bool = None) -> str: if prettify: return json.dumps(manifest, sort_keys=True, indent=4) return json.dumps(manifest, sort_keys=True, separators=(",", ":")) diff --git a/setup.py b/setup.py index da0e3a9..81810d2 100644 --- a/setup.py +++ b/setup.py @@ -20,10 +20,10 @@ 'tox>=1.8.0,<2', ], 'lint': [ - 'black>=18.6b4,<19', - 'isort>=4.2.15,<5', - 'flake8>=3.5.0,<4', - 'mypy<0.600', + 'black>=19.3b0,<20', + 'isort>=4.3.17,<5', + 'flake8>=3.7.0,<4', + 'mypy<0.800', ], 'doc': [ 'Sphinx>=1.5.5,<2', diff --git a/tox.ini b/tox.ini index 3025e4d..871a390 100644 --- a/tox.ini +++ b/tox.ini @@ -7,8 +7,8 @@ envlist= [isort] combine_as_imports=True force_sort_within_sections=True -include_trailing_comma=True skip=__init__.py +include_trailing_comma=True known_third_party=pytest,requests_mock known_first_party=ethpm line_length=88 @@ -43,6 +43,6 @@ deps=flake8 extras=lint commands= flake8 {toxinidir}/tests {toxinidir}/ethpm - mypy --follow-imports=silent --ignore-missing-imports --check-untyped-defs --disallow-incomplete-defs --disallow-untyped-defs --disallow-any-generics -p ethpm + mypy --follow-imports=silent --ignore-missing-imports --check-untyped-defs --disallow-incomplete-defs --disallow-untyped-defs --disallow-any-generics --warn-unused-ignore -p ethpm black --check --diff {toxinidir}/ethpm/ --check --diff {toxinidir}/tests/ isort --check-only --recursive --diff {toxinidir}/ethpm/ {toxinidir}/tests/