问题
I'd like to check at runtime, for example, that a given object has methods foo()
and bar()
.
My research system, built in python 3.6, is highly parameterized and can/should accept any kind of object as a replacement of its build in modules. This functionality is very useful because many different students who use this system can easily research different behavior without changing the source code of my system.
The problem is that if they built the module wrong, they might discover this only after their entire experiment is ended (might be hours).
I am looking for a way to check at a very early stage of the runtime that their input module matches a specific interface. Verifying it even before it was instantiated is even better (when the input is only the type, not the instance).
For example some_interface.verify(MyClass)
.
Solutions
I have seen many solutions on the internet (such as this), but none of them is suitable:
The most common solution (
try/catch
) will only fail during runtime and is not applicable in a multi-daemon system because it is hard to shut down when only one of the daemons fail.Checking isinstance() doesn't verify anything. It might be even worse because the developer might forget to implement a function and use base class implementation, which might not fit its current implementation.
Using ABC (Abstract Base Classes) requires the developer to inherit from the base class. If she/he fail to do so, no warning or error will be issued when instantiating the class. On the other hand, if the developer did implement the interface but did not inherit from
base
, thenissubclass()
will return False.Using zope interfaces was my goto, but it has a few shortcomings:
- It requires the developer to explicitly mention that it is implementing the interface. Failing to specify this will result in an error, although the actual implementation is correct.
- It cannot verify a module before it was instantiated. The
implementedBy()
method will only check if the module declared it is implementing the interface, but to actually verify it, you should callverifyObject()
on the actual instance. - It does not support the new typing feature that was added since python 3.5
EDIT: Apparently, zope also supports implicit implementation by calling verifyObject(YourInterface, obj, tentative=True)
which does not force the developer to explicitly defining the class as an implementer of the interface.
回答1:
To my mind, the problem is not a problem of tools. The main problem is that even if some interface is supported, no one can be sure the module really works. What would I do is creating a test for modules and running it when initializing plugins. The test should verify not just types and interfaces (isinstance
, hasattr
and so on are just tools for the task), but (if possible) minimal correctness of the module's functioning. E.g. it would be fine to perform some basic task that does not require much time to complete and verify the results. If a plugin fails during such a test task, then the plugin is not valid.
回答2:
A recent PEP finally partially solves this issue.
PEP-0544 introduce typing.Protocol
which allows defining an interface that can be validated on runtime.
This is currently available via a non-official extension to the typing
module called typing-extensions.
It can be used, for example, as follows:
from typing_extensions import Protocol, runtime
from typing import Any
@runtime
class IMyProtocol(Protocol):
member: int
def foo(self, parameter1: Any, parameter2: Any) -> Any:
pass
def bar(self, parameter1: Any) -> Any:
pass
Then, if we define a class, we could check if it follows the protocol:
class MyClass:
def __init__(self):
self.member = 5
def foo(self, a, b):
return a,b
def bar(self, c):
return c
isinstance(MyClass(), IMyProtocol) # Returns True
If we define it wrong, it will return false:
class MyOtherClass:
def __init__(self):
self.member = 5
def bar(self, c):
return c
isinstance(MyOtherClass(), IMyProtocol) # Returns False
The shortcoming of this solution is that it does not verify the arguments of the methods. Not that the implementation has the correct number of arguments and not the arguments' typing.
来源:https://stackoverflow.com/questions/43830996/verify-that-an-unknown-module-object-is-obliged-to-a-specific-interface-python