Skip to content
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

Remove module #1805

Merged
merged 1 commit into from
Apr 26, 2021
Merged
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
1 change: 1 addition & 0 deletions newsfragments/1805.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove Module class in favor of ModuleV2. ModuleV2 can handle both async and sync calls.
84 changes: 84 additions & 0 deletions tests/core/utilities/test_attach_modules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import pytest

from web3 import Web3
from web3._utils.module import (
attach_modules,
)
from web3.exceptions import (
ValidationError,
)
from web3.module import (
ModuleV2,
)
from web3.providers.eth_tester import (
EthereumTesterProvider,
)


class MockEth(ModuleV2):
def block_number(self):
return 42


class MockGeth(ModuleV2):
pass


class MockGethAdmin(ModuleV2):
def start_ws(self):
return True


class MockGethPersonal(ModuleV2):
def unlock_account(self):
return True


def test_attach_modules():
mods = {
"geth": (MockGeth, {
"personal": (MockGethPersonal,),
"admin": (MockGethAdmin,),
}),
"eth": (MockEth,),
}
w3 = Web3(EthereumTesterProvider(), modules={})
attach_modules(w3, mods)
assert w3.eth.block_number() == 42
assert w3.geth.personal.unlock_account() is True
assert w3.geth.admin.start_ws() is True


def test_attach_modules_multiple_levels_deep():
mods = {
"eth": (MockEth,),
"geth": (MockGeth, {
"personal": (MockGethPersonal, {
"admin": (MockGethAdmin,),
}),
}),
}
w3 = Web3(EthereumTesterProvider(), modules={})
attach_modules(w3, mods)
assert w3.eth.block_number() == 42
assert w3.geth.personal.unlock_account() is True
assert w3.geth.personal.admin.start_ws() is True


def test_attach_modules_with_wrong_module_format():
mods = {
"eth": (MockEth, MockGeth, MockGethPersonal)
}
w3 = Web3(EthereumTesterProvider, modules={})
with pytest.raises(ValidationError, match="Module definitions can only have 1 or 2 elements"):
attach_modules(w3, mods)


def test_attach_modules_with_existing_modules():
mods = {
"eth": (MockEth,),
}
w3 = Web3(EthereumTesterProvider, modules=mods)
with pytest.raises(AttributeError,
match="The web3 object already has an attribute with that name"):
attach_modules(w3, mods)
4 changes: 2 additions & 2 deletions web3/_utils/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
default_root_munger,
)
from web3.module import (
Module,
ModuleV2,
)
from web3.types import (
EnodeURI,
Expand All @@ -26,7 +26,7 @@


def admin_start_params_munger(
module: Module, host: str = 'localhost', port: int = 8546, cors: str = '',
module: ModuleV2, host: str = 'localhost', port: int = 8546, cors: str = '',
apis: str = 'eth,net,web3'
) -> Tuple[str, int, str, str]:
return (host, port, cors, apis)
Expand Down
4 changes: 0 additions & 4 deletions web3/_utils/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@

if TYPE_CHECKING:
from web3 import Web3 # noqa: F401
from web3.module import ( # noqa: F401
Module,
ModuleV2,
)
from web3.eth import Eth # noqa: F401


Expand Down
9 changes: 3 additions & 6 deletions web3/_utils/method_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@

if TYPE_CHECKING:
from web3 import Web3 # noqa: F401
from web3.module import ( # noqa: F401
Module,
ModuleV2,
)
from web3.module import ModuleV2 # noqa: F401
from web3.eth import Eth # noqa: F401


Expand Down Expand Up @@ -659,7 +656,7 @@ def filter_wrapper(
@to_tuple
def apply_module_to_formatters(
formatters: Tuple[Callable[..., TReturn]],
module: Union["Module", "ModuleV2"],
module: "ModuleV2",
method_name: Union[RPCEndpoint, Callable[..., RPCEndpoint]],
) -> Iterable[Callable[..., TReturn]]:
for f in formatters:
Expand All @@ -668,7 +665,7 @@ def apply_module_to_formatters(

def get_result_formatters(
method_name: Union[RPCEndpoint, Callable[..., RPCEndpoint]],
module: Union["Module", "ModuleV2"],
module: "ModuleV2",
) -> Dict[str, Callable[..., Any]]:
formatters = combine_formatters(
(PYTHONIC_RESULT_FORMATTERS,),
Expand Down
29 changes: 24 additions & 5 deletions web3/_utils/module.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
from typing import (
TYPE_CHECKING,
Any,
Dict,
Optional,
Sequence,
TypeVar,
Union,
)

from web3.exceptions import (
ValidationError,
)

T = TypeVar("T")
if TYPE_CHECKING:
from web3 import Web3 # noqa: F401
from web3.module import ModuleV2 # noqa: F401


def attach_modules(parent_module: T, module_definitions: Dict[str, Sequence[Any]]) -> None:
def attach_modules(
parent_module: Union["Web3", "ModuleV2"],
module_definitions: Dict[str, Sequence[Any]],
w3: Optional[Union["Web3", "ModuleV2"]] = None
) -> None:
for module_name, module_info in module_definitions.items():
module_class = module_info[0]
module_class.attach(parent_module, module_name)

if hasattr(parent_module, module_name):
raise AttributeError(
f"Cannot set {parent_module} module named '{module_name}'. The web3 object "
"already has an attribute with that name"
)

if w3 is None:
setattr(parent_module, module_name, module_class(parent_module))
w3 = parent_module
else:
setattr(parent_module, module_name, module_class(w3))

if len(module_info) == 2:
submodule_definitions = module_info[1]
module = getattr(parent_module, module_name)
attach_modules(module, submodule_definitions)
attach_modules(module, submodule_definitions, w3)
elif len(module_info) != 1:
raise ValidationError("Module definitions can only have 1 or 2 elements.")
4 changes: 2 additions & 2 deletions web3/beacon/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import requests

from web3.module import (
Module,
ModuleV2,
)


class Beacon(Module):
class Beacon(ModuleV2):
def __init__(
self,
base_url: str,
Expand Down
3 changes: 1 addition & 2 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
default_root_munger,
)
from web3.module import (
Module,
ModuleV2,
)
from web3.types import (
Expand All @@ -105,7 +104,7 @@
)


class Eth(ModuleV2, Module):
class Eth(ModuleV2):
account = Account()
_default_account: Union[ChecksumAddress, Empty] = empty
_default_block: BlockIdentifier = "latest"
Expand Down
3 changes: 1 addition & 2 deletions web3/geth.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
status,
)
from web3.module import (
Module,
ModuleV2,
)

Expand Down Expand Up @@ -137,6 +136,6 @@ class GethMiner(ModuleV2):
stopAutoDag = stopAutoDag


class Geth(Module):
class Geth(ModuleV2):
personal: GethPersonal
admin: GethAdmin
2 changes: 1 addition & 1 deletion web3/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def pm(self) -> "PM":
def enable_unstable_package_management_api(self) -> None:
from web3.pm import PM # noqa: F811
if not hasattr(self, '_pm'):
PM.attach(self, '_pm')
attach_modules(self, {'_pm': (PM,)})

def enable_strict_bytes_type_checking(self) -> None:
self.codec = ABICodec(build_strict_registry())
13 changes: 5 additions & 8 deletions web3/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@

if TYPE_CHECKING:
from web3 import Web3 # noqa: F401
from web3.module import ( # noqa: F401
Module,
ModuleV2,
)
from web3.module import ModuleV2 # noqa: F401

Munger = Callable[..., Any]

Expand All @@ -62,14 +59,14 @@ def inner(args: Any) -> TReturn:
return inner


def default_munger(module: Union["Module", "ModuleV2"], *args: Any, **kwargs: Any) -> Tuple[()]:
def default_munger(module: "ModuleV2", *args: Any, **kwargs: Any) -> Tuple[()]:
if not args and not kwargs:
return ()
else:
raise TypeError("Parameters passed to method without parameter mungers defined.")


def default_root_munger(module: Union["Module", "ModuleV2"], *args: Any) -> List[Any]:
def default_root_munger(module: "ModuleV2", *args: Any) -> List[Any]:
return [*args]


Expand Down Expand Up @@ -152,7 +149,7 @@ def method_selector_fn(self) -> Callable[..., Union[RPCEndpoint, Callable[..., R
raise ValueError("``json_rpc_method`` config invalid. May be a string or function")

def input_munger(
self, module: Union["Module", "ModuleV2"], args: Any, kwargs: Any
self, module: "ModuleV2", args: Any, kwargs: Any
) -> List[Any]:
# This function takes the "root_munger" - the first munger in
# the list of mungers) and then pipes the return value of the
Expand All @@ -171,7 +168,7 @@ def input_munger(
return munged_inputs

def process_params(
self, module: Union["Module", "ModuleV2"], *args: Any, **kwargs: Any
self, module: "ModuleV2", *args: Any, **kwargs: Any
) -> Tuple[Tuple[Union[RPCEndpoint, Callable[..., Any]], Any], Tuple[Any, Any]]:
params = self.input_munger(module, args, kwargs)

Expand Down
36 changes: 3 additions & 33 deletions web3/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
Any,
Callable,
Coroutine,
Optional,
TypeVar,
Union,
)
Expand Down Expand Up @@ -47,7 +46,7 @@ def apply_result_formatters(

@curry
def retrieve_blocking_method_call_fn(
w3: "Web3", module: Union["Module", "ModuleV2"], method: Method[Callable[..., TReturn]]
w3: "Web3", module: "ModuleV2", method: Method[Callable[..., TReturn]]
) -> Callable[..., Union[TReturn, LogFilter]]:
def caller(*args: Any, **kwargs: Any) -> Union[TReturn, LogFilter]:
try:
Expand All @@ -62,7 +61,7 @@ def caller(*args: Any, **kwargs: Any) -> Union[TReturn, LogFilter]:

@curry
def retrieve_async_method_call_fn(
w3: "Web3", module: Union["Module", "ModuleV2"], method: Method[Callable[..., Any]]
w3: "Web3", module: "ModuleV2", method: Method[Callable[..., Any]]
) -> Callable[..., Coroutine[Any, Any, RPCResponse]]:
async def caller(*args: Any, **kwargs: Any) -> RPCResponse:
(method_str, params), response_formatters = method.process_params(module, *args, **kwargs)
Expand All @@ -72,40 +71,11 @@ async def caller(*args: Any, **kwargs: Any) -> RPCResponse:
return caller


# TODO: Replace this with ModuleV2 when ready.
class Module:
web3: "Web3" = None

def __init__(self, web3: "Web3") -> None:
self.web3 = web3

@classmethod
def attach(cls, target: "Web3", module_name: Optional[str] = None) -> None:
if not module_name:
module_name = cls.__name__.lower()

if hasattr(target, module_name):
raise AttributeError(
"Cannot set {0} module named '{1}'. The web3 object "
"already has an attribute with that name".format(
target,
module_name,
)
)

if isinstance(target, Module):
web3 = target.web3
else:
web3 = target

setattr(target, module_name, cls(web3))


# Module should no longer have access to the full web3 api.
# Only the calling functions need access to the request methods.
# Any "re-entrant" shinanigans can go in the middlewares, which do
# have web3 access.
class ModuleV2(Module):
class ModuleV2:
is_async = False

def __init__(self, web3: "Web3") -> None:
Expand Down
4 changes: 2 additions & 2 deletions web3/pm.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
PMError,
)
from web3.module import (
Module,
ModuleV2,
)
from web3.types import (
ENS,
Expand Down Expand Up @@ -312,7 +312,7 @@ def deploy_new_instance(cls, w3: Web3) -> 'SimpleRegistry':
return cls(tx_receipt["contractAddress"], w3)


class PM(Module):
class PM(ModuleV2):
"""
The PM module will work with any subclass of ``ERC1319Registry``, tailored to a particular
implementation of `ERC1319 <https://github.com/ethereum/EIPs/issues/1319>`__, set as
Expand Down
4 changes: 2 additions & 2 deletions web3/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
RPC,
)
from web3.module import (
Module,
ModuleV2,
)


class Testing(Module):
class Testing(ModuleV2):
def timeTravel(self, timestamp: int) -> None:
return self.web3.manager.request_blocking(RPC.testing_timeTravel, [timestamp])

Expand Down
Loading