Способ проверки того, что неизвестный модуль обязан определенному интерфейсу (python)

Моя исследовательская система, построенная на Python 3.6, имеет высокие параметры и может / должна принимать любой тип объекта в качестве замены своих встроенных модулей. Эта функциональность очень полезна, потому что многие разные студенты, которые используют эту систему, могут легко исследовать различное поведение, не меняя исходный код моей системы.

Проблема в том, что если они построили модуль неправильно, они могут обнаружить это только после того, как весь их эксперимент будет завершен (может быть, часы).

Я ищу способ проверить в очень раннем состоянии времени выполнения, что их модуль ввода соответствует определенному интерфейсу. Проверять его еще до того, как он был создан, еще лучше (когда входными данными является только тип, а не экземпляр). Например some_interface.verify(MyClass),

Решения

Я видел много решений в Интернете (например, это), но ни одно из них не подходит:

  1. Наиболее распространенное решение (try/catch) будет давать сбой только во время выполнения и неприменимо в системе с несколькими демонами, потому что трудно завершить работу, когда выйдет из строя только один из демонов.

  2. проверка isinstance() ничего не проверяет. Это может быть еще хуже, потому что разработчик может забыть реализовать функцию и использовать реализацию базового класса, которая может не соответствовать его текущей реализации.

  3. Использование ABC (абстрактных базовых классов) требует, чтобы разработчик наследовал от базового класса. Если она / он не сделает этого, при создании экземпляра класса не будет выдано предупреждение или ошибка. С другой стороны, если разработчик реализовал интерфейс, но не унаследовал от base, затем issubclass() вернет Ложь.

  4. Использование интерфейсов zope было моей идеей, но у нее есть несколько недостатков:

    • Это требует от разработчика явного упоминания о том, что он реализует интерфейс. Если вы не укажете это, это приведет к ошибке, хотя фактическая реализация верна. Кроме того, если реализация неверна, но разработчик упоминает, что он реализует этот интерфейс, ошибки не будет.
    • Он не может проверить модуль до его создания. implementedBy() Метод будет только проверять, если модуль объявил, что он реализует интерфейс, но для фактической проверки, вы должны вызвать verifyObject() на самом деле.
    • Он не поддерживает новую функцию ввода текста, которая была добавлена ​​с python 3.5

1 ответ

На мой взгляд, проблема не в инструментах. Основная проблема заключается в том, что даже если какой-то интерфейс поддерживается, никто не может быть уверен, что модуль действительно работает. Что бы я сделал, это создал тест для модулей и запустил его при инициализации плагинов. Тест должен проверять не только типы и интерфейсы (isinstance, hasattr и так далее, это всего лишь инструменты для выполнения задачи), но (если возможно) минимальная корректность работы модуля. Например, было бы хорошо выполнить некоторые основные задачи, которые не требуют много времени для завершения и проверки результатов. Если плагин дает сбой во время такой тестовой задачи, то плагин недействителен.

Недавний ПКП наконец-то частично решает эту проблему. ПКП-0544 представить typing.Protocol который позволяет определить интерфейс, который может быть проверен во время выполнения. В настоящее время это доступно через неофициальное расширение typing Модуль называется Typing-Extension.

Его можно использовать, например, следующим образом:

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

Затем, если мы определим класс, мы можем проверить, соответствует ли он протоколу:

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

Если мы определим это неправильно, он вернет false:

class MyOtherClass:
    def __init__(self):
        self.member = 5

    def bar(self, c):
        return c

isinstance(MyOtherClass(), IMyProtocol)  # Returns False

Недостатком этого решения является то, что оно не проверяет аргументы методов. Не то чтобы реализация имела правильное количество аргументов, а не типизацию аргументов.

Другие вопросы по тегам