Skip to content

Commit

Permalink
Add option to attach methods to classes extending from Module class
Browse files Browse the repository at this point in the history
* Refactor logic for attaching a `Method` class as a property rather than a method. Instead of implicitly setting `mungers=None`, explicitly set the `is_property` flag on `Method` to `True`. This also facilitates attaching new methods and properties to modules.

* Fix up some tests in test_method.py that were falsely passing to actually test correctly. Add tests for new `is_property` flag for the `Method` class.

* Create `test_module.py` and add tests for `attach_methods()`
  • Loading branch information
fselmo committed Mar 8, 2022
1 parent dd5bd98 commit a3d52f5
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 51 deletions.
44 changes: 32 additions & 12 deletions tests/core/method-class/test_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_method_accepts_callable_for_selector():

def test_method_selector_fn_accepts_str():
method = Method(
mungers=None,
is_property=True,
json_rpc_method='eth_method',
)
assert method.method_selector_fn() == 'eth_method'
Expand Down Expand Up @@ -77,7 +77,7 @@ def test_input_munger_parameter_passthrough_matching_arity():
mungers=[lambda m, z, y: ['success']],
json_rpc_method='eth_method',
)
method.input_munger(object(), ['first', 'second'], {}) == 'success'
assert method.input_munger(object(), ['first', 'second'], {}) == ['success']


def test_input_munger_parameter_passthrough_mismatch_arity():
Expand All @@ -94,16 +94,15 @@ def test_input_munger_falsy_config_result_in_default_munger():
mungers=[],
json_rpc_method='eth_method',
)
method.input_munger(object(), [], {}) == []
assert method.input_munger(object(), [], {}) == []


def test_default_input_munger_with_input_parameters_exception():
def test_default_input_munger_with_input_parameters():
method = Method(
mungers=[],
json_rpc_method='eth_method',
)
with pytest.raises(TypeError):
method.input_munger(object(), [1], {})
assert method.input_munger(object(), [1], {}) == [1]


@pytest.mark.parametrize(
Expand All @@ -125,7 +124,7 @@ def test_default_input_munger_with_input_parameters_exception():
},
['unexpected_argument'],
{},
TypeError,
IndexError,
2
),
(
Expand Down Expand Up @@ -184,6 +183,15 @@ def test_default_input_munger_with_input_parameters_exception():
('eth_getBalance', ('0x0000000000000000000000000000000000000000', '0x3')),
2,
),
(
{
'json_rpc_method': 'eth_getBalance',
},
('0x0000000000000000000000000000000000000000', 3),
{},
('eth_getBalance', ('0x0000000000000000000000000000000000000000', '0x3')),
2,
),
(
{
'mungers': [
Expand All @@ -206,7 +214,17 @@ def test_default_input_munger_with_input_parameters_exception():
{},
('eth_chainId', ()),
2,
)
),
(
{
'is_property': True,
'json_rpc_method': 'eth_chainId',
},
[],
{},
('eth_chainId', ()),
2,
),
),
ids=[
'raises-error-no-rpc-method',
Expand All @@ -215,9 +233,11 @@ def test_default_input_munger_with_input_parameters_exception():
'test-rpc-method-as-callable',
'test-arg-munger',
'test-munger-wrong-length-arg',
'test-request-formatters',
'test-request-formatters-default-root-munger-explicit',
'test-request-formatters-default-root-munger-implicit',
'test-mungers-and-request-formatters',
'test-response-formatters',
'test-set-as-property-default-munger-implicit',
]
)
def test_process_params(
Expand All @@ -230,7 +250,7 @@ def test_process_params(
if isclass(expected_request_result) and issubclass(expected_request_result, Exception):
with pytest.raises(expected_request_result):
method = Method(**method_config)
request_params, output_formatter = method.process_params(object(), *args, **kwargs)
method.process_params(object(), *args, **kwargs)
else:
method = Method(**method_config)
request_params, output_formatter = method.process_params(object(), *args, **kwargs)
Expand All @@ -248,8 +268,8 @@ class Success(Exception):
pass


def return_exception_raising_formatter(method):
def formatter(params):
def return_exception_raising_formatter(_method):
def formatter(_params):
raise Success()
return compose(formatter)

Expand Down
75 changes: 75 additions & 0 deletions tests/core/module-class/test_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import pytest

from web3 import (
EthereumTesterProvider,
Web3,
)
from web3.method import (
Method,
)


@pytest.fixture
def web3_with_external_modules(module1, module2, module3):
return Web3(
EthereumTesterProvider(),
external_modules={
'module1': module1,
'module2': (module2, {
'submodule1': module3,
}),
}
)


def test_attach_methods_to_module(web3_with_external_modules):
w3 = web3_with_external_modules

w3.module1.attach_methods({
# set `property1` on `module1` with `eth_chainId` RPC endpoint
'property1': Method('eth_chainId', is_property=True),
# set `method1` on `module1` with `eth_getBalance` RPC endpoint
'method1': Method('eth_getBalance'),
})

assert w3.eth.chain_id == 61
assert w3.module1.property1 == 61

coinbase = w3.eth.coinbase
assert w3.eth.get_balance(coinbase, 'latest') == 1000000000000000000000000
assert w3.module1.method1(coinbase, 'latest') == 1000000000000000000000000

w3.module2.submodule1.attach_methods({
# set `method2` on `module2.submodule1` with `eth_blockNumber` RPC endpoint
'method2': Method('eth_blockNumber', is_property=True)
})

assert w3.eth.block_number == 0
assert w3.module2.submodule1.method2 == 0

w3.eth.attach_methods({'get_block2': Method('eth_getBlockByNumber')})

assert w3.eth.get_block('latest')['number'] == 0
assert w3.eth.get_block('pending')['number'] == 1

assert w3.eth.get_block2('latest')['number'] == 0
assert w3.eth.get_block2('pending')['number'] == 1


def test_attach_methods_with_mungers(web3_with_external_modules):
w3 = web3_with_external_modules

w3.module1.attach_methods({
'method1': Method('eth_getBlockByNumber', mungers=[
lambda _method, block_id, f, _z: (block_id, f),
lambda _m, block_id, _f: (block_id - 1,),
]),
})

assert w3.eth.get_block(0)['baseFeePerGas'] == 1000000000
assert w3.eth.get_block(1)['baseFeePerGas'] == 875000000

# `method1` should take a higher block number than `eth_getBlockByNumber` due to mungers and no
# other params should matter
assert w3.module1.method1(1, False, '_is_never_used_')['baseFeePerGas'] == 1000000000
assert w3.module1.method1(2, '_will_be_overridden_', None)['baseFeePerGas'] == 875000000
10 changes: 5 additions & 5 deletions web3/_utils/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ def admin_start_params_munger(

datadir: Method[Callable[[], str]] = Method(
RPC.admin_datadir,
mungers=None,
is_property=True,
)


node_info: Method[Callable[[], NodeInfo]] = Method(
RPC.admin_nodeInfo,
mungers=None,
is_property=True,
)


peers: Method[Callable[[], List[Peer]]] = Method(
RPC.admin_peers,
mungers=None,
is_property=True,
)


Expand All @@ -77,13 +77,13 @@ def __call__(

stop_rpc: Method[Callable[[], bool]] = Method(
RPC.admin_stopRPC,
mungers=None,
is_property=True,
)


stop_ws: Method[Callable[[], bool]] = Method(
RPC.admin_stopWS,
mungers=None,
is_property=True,
)

#
Expand Down
6 changes: 3 additions & 3 deletions web3/_utils/miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,19 @@

stop: Method[Callable[[], bool]] = Method(
RPC.miner_stop,
mungers=None,
is_property=True,
)


start_auto_dag: Method[Callable[[], bool]] = Method(
RPC.miner_startAutoDag,
mungers=None,
is_property=True,
)


stop_auto_dag: Method[Callable[[], bool]] = Method(
RPC.miner_stopAutoDag,
mungers=None,
is_property=True,
)


Expand Down
4 changes: 2 additions & 2 deletions web3/_utils/personal.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@

list_accounts: Method[Callable[[], List[ChecksumAddress]]] = Method(
RPC.personal_listAccounts,
mungers=None,
is_property=True,
)


list_wallets: Method[Callable[[], List[GethWallet]]] = Method(
RPC.personal_listWallets,
mungers=None,
is_property=True,
)


Expand Down
6 changes: 3 additions & 3 deletions web3/_utils/txpool.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@

content: Method[Callable[[], TxPoolContent]] = Method(
RPC.txpool_content,
mungers=None,
is_property=True,
)


inspect: Method[Callable[[], TxPoolInspect]] = Method(
RPC.txpool_inspect,
mungers=None,
is_property=True,
)


status: Method[Callable[[], TxPoolStatus]] = Method(
RPC.txpool_status,
mungers=None,
is_property=True,
)
22 changes: 11 additions & 11 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class BaseEth(Module):

_gas_price: Method[Callable[[], Wei]] = Method(
RPC.eth_gasPrice,
mungers=None,
is_property=True,
)

@property
Expand Down Expand Up @@ -244,7 +244,7 @@ def estimate_gas_munger(

_max_priority_fee: Method[Callable[..., Wei]] = Method(
RPC.eth_maxPriorityFeePerGas,
mungers=None,
is_property=True,
)

def get_block_munger(
Expand All @@ -267,12 +267,12 @@ def get_block_munger(

get_block_number: Method[Callable[[], BlockNumber]] = Method(
RPC.eth_blockNumber,
mungers=None,
is_property=True,
)

get_coinbase: Method[Callable[[], ChecksumAddress]] = Method(
RPC.eth_coinbase,
mungers=None,
is_property=True,
)

def block_id_munger(
Expand Down Expand Up @@ -315,27 +315,27 @@ def call_munger(

_get_accounts: Method[Callable[[], Tuple[ChecksumAddress]]] = Method(
RPC.eth_accounts,
mungers=None,
is_property=True,
)

_get_hashrate: Method[Callable[[], int]] = Method(
RPC.eth_hashrate,
mungers=None,
is_property=True,
)

_chain_id: Method[Callable[[], int]] = Method(
RPC.eth_chainId,
mungers=None,
is_property=True,
)

_is_mining: Method[Callable[[], bool]] = Method(
RPC.eth_mining,
mungers=None,
is_property=True,
)

_is_syncing: Method[Callable[[], Union[SyncStatus, bool]]] = Method(
RPC.eth_syncing,
mungers=None,
is_property=True,
)

_get_transaction_receipt: Method[Callable[[_Hash32], TxReceipt]] = Method(
Expand Down Expand Up @@ -565,7 +565,7 @@ def icapNamereg(self) -> NoReturn:

_protocol_version: Method[Callable[[], str]] = Method(
RPC.eth_protocolVersion,
mungers=None,
is_property=True,
)

@property
Expand Down Expand Up @@ -983,7 +983,7 @@ def getCompilers(self) -> NoReturn:

get_work: Method[Callable[[], List[HexBytes]]] = Method(
RPC.eth_getWork,
mungers=None,
is_property=True,
)

@deprecated_for("generate_gas_price")
Expand Down
Loading

0 comments on commit a3d52f5

Please sign in to comment.