Skip to content

Commit 2e11b33

Browse files
committed
Add hasattr to ContractFunction and ContractCaller
1 parent 9982f13 commit 2e11b33

5 files changed

+72
-26
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import pytest
2+
3+
from web3.exceptions import (
4+
ABIEventFunctionNotFound,
5+
ABIFunctionNotFound,
6+
)
7+
8+
9+
@pytest.fixture()
10+
def abi():
11+
return '''[{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"function"}, {"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]''' # noqa: E501
12+
13+
14+
@pytest.mark.parametrize(
15+
'attribute',
16+
('functions', 'events', 'caller')
17+
)
18+
def test_getattr(web3, abi, attribute):
19+
contract = web3.eth.contract(abi=abi)
20+
contract_attribute = getattr(contract, attribute)
21+
assert getattr(contract_attribute, "Increased")
22+
23+
24+
@pytest.mark.parametrize(
25+
'attribute,error', (
26+
('functions', ABIFunctionNotFound),
27+
('events', ABIEventFunctionNotFound),
28+
('caller', ABIFunctionNotFound),
29+
)
30+
)
31+
def test_getattr_raises_error(web3, abi, attribute, error):
32+
contract = web3.eth.contract(abi=abi)
33+
contract_attribute = getattr(contract, attribute)
34+
35+
with pytest.raises(error):
36+
getattr(contract_attribute, "Decreased")
37+
38+
39+
@pytest.mark.parametrize(
40+
'attribute',
41+
('functions', 'events', 'caller')
42+
)
43+
def test_hasattr(web3, abi, attribute):
44+
contract = web3.eth.contract(abi=abi)
45+
contract_attribute = getattr(contract, attribute)
46+
47+
assert hasattr(contract_attribute, "Increased") is True
48+
assert hasattr(contract_attribute, "Decreased") is False

tests/core/contracts/test_contract_call_interface.py

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
contract_ens_addresses,
2424
)
2525
from web3.exceptions import (
26+
ABIFunctionNotFound,
2627
BadFunctionCallOutput,
2728
BlockNumberOutofRange,
2829
InvalidAddress,

tests/core/contracts/test_extracting_event_data.py

-24
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
get_event_data,
1313
)
1414
from web3.exceptions import (
15-
ABIEventFunctionNotFound,
1615
LogTopicError,
1716
ValidationError,
1817
)
@@ -114,29 +113,6 @@ def dup_txn_receipt(
114113
return wait_for_transaction(web3, dup_txn_hash)
115114

116115

117-
@pytest.fixture()
118-
def single_event_abi():
119-
return '''[{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]''' # noqa: E501
120-
121-
122-
def test_contract_event_getattr(web3, single_event_abi):
123-
contract = web3.eth.contract(abi=single_event_abi)
124-
assert getattr(contract.events, "Increased")
125-
126-
127-
def test_contract_event_getattr_raises_error(web3, single_event_abi):
128-
contract = web3.eth.contract(abi=single_event_abi)
129-
130-
with pytest.raises(ABIEventFunctionNotFound):
131-
getattr(contract.events, "Decreased")
132-
133-
134-
def test_contract_event_hasattr(web3, single_event_abi):
135-
contract = web3.eth.contract(abi=single_event_abi)
136-
assert hasattr(contract.events, "Increased") is True
137-
assert hasattr(contract.events, "Decreased") is False
138-
139-
140116
@pytest.mark.parametrize(
141117
'contract_fn,event_name,call_args,expected_args',
142118
(

web3/contract.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
)
112112
from web3.exceptions import (
113113
ABIEventFunctionNotFound,
114+
ABIFunctionNotFound,
114115
BadFunctionCallOutput,
115116
BlockNumberOutofRange,
116117
FallbackNotFound,
@@ -186,7 +187,7 @@ def __getattr__(self, function_name: str) -> "ContractFunction":
186187
"Are you sure you provided the correct contract abi?"
187188
)
188189
elif function_name not in self.__dict__['_functions']:
189-
raise MismatchedABI(
190+
raise ABIFunctionNotFound(
190191
"The function '{}' was not found in this contract's abi. ".format(function_name),
191192
"Are you sure you provided the correct contract abi?"
192193
)
@@ -196,6 +197,12 @@ def __getattr__(self, function_name: str) -> "ContractFunction":
196197
def __getitem__(self, function_name: str) -> ABIFunction:
197198
return getattr(self, function_name)
198199

200+
def __hasattr__(self, event_name: str) -> bool:
201+
try:
202+
return event_name in self.__dict__['_events']
203+
except ABIFunctionNotFound:
204+
return False
205+
199206

200207
class ContractEvents:
201208
"""Class containing contract event objects
@@ -1360,7 +1367,7 @@ def __getattr__(self, function_name: str) -> Any:
13601367
)
13611368
elif function_name not in set(fn['name'] for fn in self._functions):
13621369
functions_available = ', '.join([fn['name'] for fn in self._functions])
1363-
raise MismatchedABI(
1370+
raise ABIFunctionNotFound(
13641371
"The function '{}' was not found in this contract's ABI. ".format(function_name),
13651372
"Here is a list of all of the function names found: ",
13661373
"{}. ".format(functions_available),
@@ -1369,6 +1376,12 @@ def __getattr__(self, function_name: str) -> Any:
13691376
else:
13701377
return super().__getattribute__(function_name)
13711378

1379+
def __hasattr__(self, event_name: str) -> bool:
1380+
try:
1381+
return event_name in self.__dict__['_events']
1382+
except ABIFunctionNotFound:
1383+
return False
1384+
13721385
def __call__(
13731386
self, transaction: TxParams=None, block_identifier: BlockIdentifier='latest'
13741387
) -> 'ContractCaller':

web3/exceptions.py

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ class ABIEventFunctionNotFound(AttributeError, MismatchedABI):
7878
pass
7979

8080

81+
class ABIFunctionNotFound(AttributeError, MismatchedABI):
82+
"""
83+
Raised when an attempt is made to access a function
84+
that does not exist in the ABI.
85+
"""
86+
pass
87+
88+
8189
class FallbackNotFound(Exception):
8290
"""
8391
Raised when fallback function doesn't exist in contract.

0 commit comments

Comments
 (0)