Explore alternative for current configuration approach
What is wrong?
The way I understand it, we currently use Configurable as a way to dynamically generate classes (or maybe better: class like objects?). We use that all over the place e.g.
FrontierVM = VM.configure(
# class name
__name__='FrontierVM',
# fork name
fork='frontier',
# classes
_state_class=FrontierState,
# helpers
create_header_from_parent=staticmethod(create_frontier_header_from_parent),
compute_difficulty=staticmethod(compute_frontier_difficulty),
configure_header=configure_frontier_header,
)
The way I understand the mechanism, it basically lets you take a class as a blueprint and generate a new one with several properties and sub properties altered.
I'm not entirely sure if all of its functionality could be achieved with regular style inheritance or if it contains things that would not work with classical inheritance. In other words, I'm not sure if it is just syntactic sugar to have a more concise mechanism or if contains stuff that would be hard to achieve the classical way. I'll assume that it does have some special power for now.
While there is no big problem with this approach per se I noticed a couple of minor things that I considered worth enough to write down.
-
the current mechanism doesn't play nice with
mypyor types in general. In the above exampleFrontierVMreally acts as a class but isn't recognized as such from the type system -
as an extension to the previous point, the type system doesn't know that
FrontierVMinherits fromVM -
not having proper
classsyntax makes it harder to document as one can not simple write e.g.
class FrontierVM
"""
Frontier anyone?
"""
...
- just from looking at the code it is less obvious that this thing really is meant to be treated as a class
How can it be fixed
-
If we assume that there is nothing
Configurableachieves that the classical inheritance style could not achieve than obviously we could simply consider using the classical style if we think it would be worthwhile. -
Alternatively, I guess we could come up with some kind of decorator based approach that might translate the example above into something roughly equivalent to the following.
@configure(
# fork name
fork='frontier',
# classes
_state_class=FrontierState,
# helpers
create_header_from_parent=staticmethod(create_frontier_header_from_parent),
compute_difficulty=staticmethod(compute_frontier_difficulty),
configure_header=configure_frontier_header,
)
class FrontierVM(VM):
"""
Definitely Frontier!
"""
pass
This isn't a real proposal for something I think we should do. I don't know much about the reasoning behind the current mechanism so I'm just putting this out as something to discuss.
It would be fine to replace all of the hard coded classes like FrontierVM with regular style class inheritance.
It's possible we can do away with the Configurable API completely, and probably worth exploring.
Let me copy over your comment from https://github.com/ethereum/py-evm/issues/592#issuecomment-386040975
Also worth pointing out that this is the reason for the
ConfigurableAPI.
Instead of having to define all of these classes just for testing you can do this.
@pytest.fixture(params=[FrontierVM, HomesteadVM, ...])
def chain_for_testing(request):
return Chain.configure(vm_configuration=((0, request.param),))
I have a couple of thoughts on that but I think I'll let them mature for a while and maybe come back with a bit of code at some point to discuss further. It's not something that needs to be addressed but rather just worth exploring if it can be improved. Thanks for leaving your thoughts and providing the background information about why we use that pattern!