Skip to content

Issue1560 #1593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions newsfragments/1560.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Fixed hasattr overloader method in the web3.ContractEvent class by implementing a try/except handler
that returns False if an exception is raised in the __getattr__ overloader method
(since __getattr__ HAS to be called in every __hasattr__ call).

Created an Exception class called 'ABIEventFunctionNotFound', which inherits from both AttributeError and
MismatchedABI, and replaced the MismatchedABI raise with a raise to the created class in the __getattr__
overloader method of the web3.ContractEvent object.

Created the test scripts 'test_contract_events_hasattr.py' and 'test_contract_events_getattr.py'
1 change: 1 addition & 0 deletions newsfragments/1588.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added block_identifier parameter to `Contract.estimateGas`
15 changes: 15 additions & 0 deletions tests/core/contracts/test_contract_constructor_estimateGas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest

abi = """[{"anonymous":false,"inputs":[{"indexed":false,"name":"_bar","type":"string"}],"name":"barred","type":"event"},{"constant":false,"inputs":[{"name":"_bar","type":"string"}],"name":"setBar","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"bar","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]""" # noqa: E501
# This bytecode is the output of compiling with
# solc version:0.5.3+commit.10d17f24.Emscripten.clang
bytecode = """608060405234801561001057600080fd5b506040805190810160405280600b81526020017f68656c6c6f20776f726c640000000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6103bb806101166000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c01000000000000000000000000000000000000000000000000000000009004806397bc14aa14610058578063febb0f7e14610113575b600080fd5b6101116004803603602081101561006e57600080fd5b810190808035906020019064010000000081111561008b57600080fd5b82018360208201111561009d57600080fd5b803590602001918460018302840111640100000000831117156100bf57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610196565b005b61011b61024c565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015b578082015181840152602081019050610140565b50505050905090810190601f1680156101885780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101ac9291906102ea565b507f5f71ad82e16f082de5ff496b140e2fbc8621eeb37b36d59b185c3f1364bbd529816040518080602001828103825283818151815260200191508051906020019080838360005b8381101561020f5780820151818401526020810190506101f4565b50505050905090810190601f16801561023c5780820380516001836020036101000a031916815260200191505b509250505060405180910390a150565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102e25780601f106102b7576101008083540402835291602001916102e2565b820191906000526020600020905b8154815290600101906020018083116102c557829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061032b57805160ff1916838001178555610359565b82800160010185558215610359579182015b8281111561035857825182559160200191906001019061033d565b5b509050610366919061036a565b5090565b61038c91905b80821115610388576000816000905550600101610370565b5090565b9056fea165627a7a72305820ae6ca683d45ee8a71bba45caee29e4815147cd308f772c853a20dfe08214dbb50029"""
block_identifier = 'latest'

def test_contract_estimateGas(web3, transaction=None):
contract = web3.eth.contract(abi=abi, bytecode=bytecode)
gas_estimate = contract.constructor().estimateGas(transaction=transaction, block_identifier=block_identifier)
deploy_txn = contract.constructor().transact({'from': Web3.toChecksumAddress(Web3.eth.accounts[0])})
txn_receipt = web3.eth.waitForTransactionReceipt(deploy_txn)
gas_used = txn_receipt.get('gasUsed')
assert abs(gas_estimate - gas_used) < 21000
18 changes: 18 additions & 0 deletions tests/core/contracts/test_contract_events_getattr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest
import json

CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501

def test_contract_event_getattr(Web3, EthereumTesterProvider, event_name, expected_existence):
web3 = Web3(EthereumTesterProvider)
contract = web3.eth.contract(abi=CONTRACT_ABI)
try:
getattr(contract.events, event_name)
result = True
except Exception as e:
result = False
if result == expected_existence:
return True
else:
return False

11 changes: 11 additions & 0 deletions tests/core/contracts/test_contract_events_hasattr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import pytest
import json

CONTRACT_ABI = json.loads('[{"constant":false,"inputs":[],"name":"return13","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[],"name":"counter","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"amt","type":"uint256"}],"name":"increment","outputs":[{"name":"result","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"},{"name":"b","type":"int256"}],"name":"add","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"a","type":"int256"}],"name":"multiply7","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"value","type":"uint256"}],"name":"Increased","type":"event"}]') # noqa: E501

def test_contract_event_hasattr(Web3, EthereumTesterProvider, event_name, expected):
web3 = Web3(EthereumTesterProvider)
contract = web3.eth.contract(abi=CONTRACT_ABI)
if hasattr(contract.events, event_name) == expected:
return True
return False
9 changes: 8 additions & 1 deletion web3/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
InvalidEventABI,
LogTopicError,
MismatchedABI,
ABIEventFunctionNotFound,
NoABIEventsFound,
NoABIFound,
NoABIFunctionsFound,
Expand Down Expand Up @@ -239,7 +240,7 @@ def __getattr__(self, event_name: str) -> "ContractEvent":
"Are you sure you provided the correct contract abi?"
)
elif event_name not in self.__dict__['_events']:
raise MismatchedABI(
raise ABIEventFunctionNotFound(
"The event '{}' was not found in this contract's abi. ".format(event_name),
"Are you sure you provided the correct contract abi?"
)
Expand All @@ -257,6 +258,12 @@ def __iter__(self) -> Iterable["ContractEvent"]:
for event in self._events:
yield self[event['name']]

def __hasattr__(self, event_name: str) -> bool:
try:
return event_name in self.__dict__['_events']
except ABIEventFunctionNotFound:
return False


class Contract:
"""Base class for Contract proxy classes.
Expand Down
6 changes: 6 additions & 0 deletions web3/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class MismatchedABI(Exception):
"""
pass

class ABIEventFunctionNotFound(AttributeError, MismatchedABI):
"""
Raised when an attempt is made to access a function/event
that does not exist in the ABI.
"""
pass

class FallbackNotFound(Exception):
"""
Expand Down