Verify that an unknown module/object is obliged to a specific interface (python)

我是研究僧i 提交于 2019-12-08 14:06:47

问题


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:

  1. 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.

  2. 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.

  3. 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, then issubclass() will return False.

  4. 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 call verifyObject() 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!