From ac4b66bbb1037ca28993ea71a41b78e29b79549a Mon Sep 17 00:00:00 2001 From: Juliya Smith Date: Thu, 20 Jul 2023 10:26:36 -0500 Subject: [PATCH] feat: new exception --- src/ape/exceptions.py | 20 +++++++++++++++++++ src/ape/managers/chain.py | 27 +++++++++++++------------ src/ape/managers/project/manager.py | 2 +- tests/functional/test_chain.py | 31 ++++++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/src/ape/exceptions.py b/src/ape/exceptions.py index 4e5ae3fa52..dbb7bb450d 100644 --- a/src/ape/exceptions.py +++ b/src/ape/exceptions.py @@ -480,6 +480,26 @@ class ChainError(ApeException): """ +class ContractNotFoundError(ChainError): + """ + Raised when a contract is not found at an address. + """ + + def __init__(self, address: "AddressType", has_explorer: bool, provider_name: str): + msg = f"Failed to get contract type for address '{address}'." + msg += ( + " Contract may need verification." + if has_explorer + else ( + f" Current provider '{provider_name}' has no associated " + "explorer plugin. Try installing an explorer plugin using " + f"{click.style(text='ape plugins install etherscan', fg='green')}, " + "or using a network with explorer support." + ) + ) + super().__init__(msg) + + class UnknownSnapshotError(ChainError): """ Raised when given an unknown snapshot ID. diff --git a/src/ape/managers/chain.py b/src/ape/managers/chain.py index f81e5f93f6..39c8123904 100644 --- a/src/ape/managers/chain.py +++ b/src/ape/managers/chain.py @@ -6,7 +6,6 @@ from pathlib import Path from typing import IO, Collection, Dict, Iterator, List, Optional, Set, Type, Union, cast -import click import pandas as pd from ethpm_types import ContractType from rich import get_console @@ -26,6 +25,7 @@ APINotImplementedError, BlockNotFoundError, ChainError, + ContractNotFoundError, ConversionError, CustomError, ProviderNotConnectedError, @@ -967,7 +967,12 @@ def _cache_deployment( def __getitem__(self, address: AddressType) -> ContractType: contract_type = self.get(address) if not contract_type: - raise IndexError(f"No contract type found at address '{address}'.") + # Create error message from custom exception cls. + err = ContractNotFoundError( + address, self.provider.network.explorer is not None, self.provider.name + ) + # Must raise IndexError. + raise IndexError(str(err)) return contract_type @@ -1144,6 +1149,7 @@ def instance_at( Raises: TypeError: When passing an invalid type for the `contract_type` arguments (expects `ContractType`). + :class:`~ape.exceptions.ContractNotFoundError`: When the contract type is not found. Args: address (Union[str, AddressType]): The address of the plugin. If you are using the ENS @@ -1176,17 +1182,12 @@ def instance_at( raise # Current exception if not contract_type: - msg = f"Failed to get contract type for address '{contract_address}'." - if self.provider.network.explorer is None: - msg += ( - f" Current provider '{self.provider.name}' has no associated " - "explorer plugin. Try installing an explorer plugin using " - f"{click.style(text='ape plugins install etherscan', fg='green')}, " - "or using a network with explorer support." - ) - else: - msg += " Contract may need verification." - raise ChainError(msg) + raise ContractNotFoundError( + contract_address, + self.provider.network.explorer is not None, + self.provider.name, + ) + elif not isinstance(contract_type, ContractType): raise TypeError( f"Expected type '{ContractType.__name__}' for argument 'contract_type'." diff --git a/src/ape/managers/project/manager.py b/src/ape/managers/project/manager.py index 92b1cfaf98..af52333a91 100644 --- a/src/ape/managers/project/manager.py +++ b/src/ape/managers/project/manager.py @@ -555,7 +555,7 @@ def get_contract(self, contract_name: str) -> ContractContainer: contract = self._get_contract(contract_name) if not contract: - raise ValueError(f"No contract found with name '{contract_name}'.") + raise ProjectError(f"No contract found with name '{contract_name}'.") return contract diff --git a/tests/functional/test_chain.py b/tests/functional/test_chain.py index 162af856ee..d3764ee485 100644 --- a/tests/functional/test_chain.py +++ b/tests/functional/test_chain.py @@ -8,7 +8,12 @@ import ape from ape.contracts import ContractInstance -from ape.exceptions import APINotImplementedError, ChainError, ConversionError +from ape.exceptions import ( + APINotImplementedError, + ChainError, + ContractNotFoundError, + ConversionError, +) from ape_ethereum.transactions import Receipt, TransactionStatusEnum from tests.conftest import skip_if_plugin_installed @@ -384,6 +389,30 @@ def fn(addr, default=None): assert caplog.records[-1].message == expected_fail_message +def test_instance_at_contract_type_not_found(chain): + new_address = "0x4a986a6dCA6dbf99bC3d17F8D71aFb0d60e740f8" + expected = ( + rf"Failed to get contract type for address '{new_address}'. " + r"Current provider 'test' has no associated explorer plugin. " + "Try installing an explorer plugin using .*ape plugins install etherscan.*, " + r"or using a network with explorer support\." + ) + with pytest.raises(ContractNotFoundError, match=expected): + chain.contracts.instance_at(new_address) + + +def test_contracts_getitem_contract_not_found(chain): + new_address = "0x4a986a6dCA6dbf99bC3d17F8D71aFb0d60e740f8" + expected = ( + rf"Failed to get contract type for address '{new_address}'. " + r"Current provider 'test' has no associated explorer plugin. " + "Try installing an explorer plugin using .*ape plugins install etherscan.*, " + r"or using a network with explorer support\." + ) + with pytest.raises(IndexError, match=expected): + _ = chain.contracts[new_address] + + def test_deployments_mapping_cache_location(chain): # Arrange / Act mapping_location = chain.contracts._deployments_mapping_cache