From a01987aeaf09fde4bff28d6efafa9cf5e0491466 Mon Sep 17 00:00:00 2001 From: fselmo Date: Tue, 10 Jan 2023 14:13:13 -0700 Subject: [PATCH 1/2] - #2774: prevent ``ethpm`` import issues w/o optional ``ipfshttpclient`` dependency --- ethpm/_utils/backend.py | 22 +++++++++++++--------- ethpm/_utils/ipfs.py | 12 +++++++++--- ethpm/backends/ipfs.py | 8 +++++++- newsfragments/2775.bugfix.rst | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 newsfragments/2775.bugfix.rst diff --git a/ethpm/_utils/backend.py b/ethpm/_utils/backend.py index ec9f44d8a2..abad61e0e8 100644 --- a/ethpm/_utils/backend.py +++ b/ethpm/_utils/backend.py @@ -10,9 +10,6 @@ from eth_utils import ( to_tuple, ) -from ipfshttpclient.exceptions import ( - ConnectionError, -) from ethpm.backends.base import ( BaseURIBackend, @@ -30,8 +27,17 @@ RegistryURIBackend, ) +try: + from ipfshttpclient.exceptions import ( + ConnectionError as IpfsConnectionError, + ) +except ImportError: + pass + logger = logging.getLogger("ethpm.utils.backend") +IPFS_NODE_UNAVAILABLE_MSG = "No local IPFS node available on port 5001." + ALL_URI_BACKENDS = [ InfuraIPFSBackend, DummyIPFSBackend, @@ -50,8 +56,8 @@ def get_translatable_backends_for_uri( try: if backend().can_translate_uri(uri): # type: ignore yield backend - except ConnectionError: - logger.debug("No local IPFS node available on port 5001.", exc_info=True) + except IpfsConnectionError: + logger.debug(IPFS_NODE_UNAVAILABLE_MSG, exc_info=True) @to_tuple @@ -71,7 +77,5 @@ def get_resolvable_backends_for_uri( try: if backend_class().can_resolve_uri(uri): # type: ignore yield backend_class - except ConnectionError: - logger.debug( - "No local IPFS node available on port 5001.", exc_info=True - ) + except IpfsConnectionError: + logger.debug(IPFS_NODE_UNAVAILABLE_MSG, exc_info=True) diff --git a/ethpm/_utils/ipfs.py b/ethpm/_utils/ipfs.py index 9d3f9dd177..91033f60ea 100644 --- a/ethpm/_utils/ipfs.py +++ b/ethpm/_utils/ipfs.py @@ -9,9 +9,6 @@ parse, ) -from base58 import ( - b58encode, -) from eth_utils import ( to_text, ) @@ -24,6 +21,15 @@ PBNode, ) +try: + # `ipfshttpclient` backend is optional. This is only imported if the "web3[ipfs]" + # install extra is installed + from base58 import ( + b58encode, + ) +except ImportError: + pass + def dummy_ipfs_pin(path: Path) -> Dict[str, str]: """ diff --git a/ethpm/backends/ipfs.py b/ethpm/backends/ipfs.py index 2bfc085781..140c116816 100644 --- a/ethpm/backends/ipfs.py +++ b/ethpm/backends/ipfs.py @@ -15,7 +15,6 @@ import_string, to_bytes, ) -import ipfshttpclient from ethpm import ( get_ethpm_spec_dir, @@ -39,6 +38,13 @@ EthPMValidationError, ) +try: + # `ipfshttpclient` backend is optional. This is only imported if the "web3[ipfs]" + # install extra is installed + import ipfshttpclient +except ImportError: + pass + class BaseIPFSBackend(BaseURIBackend): """ diff --git a/newsfragments/2775.bugfix.rst b/newsfragments/2775.bugfix.rst new file mode 100644 index 0000000000..4a81181ccb --- /dev/null +++ b/newsfragments/2775.bugfix.rst @@ -0,0 +1 @@ +Fix ``ethpm`` import issues after making ``ipfshttpclient`` optional. \ No newline at end of file From 2d22c6abbd51e5ec7b94d78c3e6aaefafa06156c Mon Sep 17 00:00:00 2001 From: fselmo Date: Fri, 13 Jan 2023 10:34:13 -0700 Subject: [PATCH 2/2] Handle optional ipfshttpclient exception a bit more gracefully --- ethpm/_utils/backend.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/ethpm/_utils/backend.py b/ethpm/_utils/backend.py index abad61e0e8..75843752f0 100644 --- a/ethpm/_utils/backend.py +++ b/ethpm/_utils/backend.py @@ -28,15 +28,12 @@ ) try: - from ipfshttpclient.exceptions import ( - ConnectionError as IpfsConnectionError, - ) + from ipfshttpclient.exceptions import ConnectionError as IpfsConnectionError except ImportError: pass -logger = logging.getLogger("ethpm.utils.backend") -IPFS_NODE_UNAVAILABLE_MSG = "No local IPFS node available on port 5001." +logger = logging.getLogger("ethpm.utils.backend") ALL_URI_BACKENDS = [ InfuraIPFSBackend, @@ -47,6 +44,19 @@ ] +def _handle_optional_ipfs_backend_exception(e: Exception) -> None: + try: + # if optional `ipfshttpclient` module is present, catch and debug if + # IpfsConnectionError, else raise original exception. + if isinstance(e, IpfsConnectionError): + logger.debug("No local IPFS node available on port 5001.", exc_info=True) + else: + raise e + except NameError: + # if optional `ipfshttpclient` module is not present, raise original exception + raise e + + @to_tuple def get_translatable_backends_for_uri( uri: URI, @@ -56,8 +66,8 @@ def get_translatable_backends_for_uri( try: if backend().can_translate_uri(uri): # type: ignore yield backend - except IpfsConnectionError: - logger.debug(IPFS_NODE_UNAVAILABLE_MSG, exc_info=True) + except Exception as e: + _handle_optional_ipfs_backend_exception(e) @to_tuple @@ -77,5 +87,5 @@ def get_resolvable_backends_for_uri( try: if backend_class().can_resolve_uri(uri): # type: ignore yield backend_class - except IpfsConnectionError: - logger.debug(IPFS_NODE_UNAVAILABLE_MSG, exc_info=True) + except Exception as e: + _handle_optional_ipfs_backend_exception(e)