Как справиться с дополнительными требованиями, когда они также используются в качестве подсказок типа? - PullRequest
1 голос
/ 23 марта 2020

Я хочу обновить библиотеку, чтобы требование было необязательным (с использованием extras_require).

К сожалению, импортированные классы из необязательного требования используются повсеместно в библиотеке как тип намеки. Вот пример:

from typing import List
try:
    from extra.sub import ExtraFoo, ExtraBar
except ImportError:
    pass

def optional(foo: ExtraFoo) -> List[ExtraBar]:
    pass

def greeting(name: str) -> str:
    return 'Hello ' + name

if __name__ == '__main__':
    greeting('John Smith')

В этом примере мы не будем использовать optional(), если extra не был установлен. Действительно, такой код вызовет NameError, поскольку ExtraFoo и ExtraBar используются в качестве подсказки типа.

Возможное исправление - объявить class ExtraFoo: pass и class ExtraBar: pass в блоке except , Однако такие ситуации встречаются повсюду в коде с несколькими типами из одного и того же модуля.

Я бы хотел избежать загрязнения кода декларацией фиктивного импорта. Есть ли общий способ справиться с этой ситуацией (например, модуль прокси)?

1 Ответ

0 голосов
/ 23 марта 2020

Вы можете использовать python декоратор для изготовления этого очистителя. Декоратор принимает функцию в качестве аргумента, выполняет некоторые модификации (например, запускает функцию, изменяет вывод и т. Д. c.) И выплевывает другую функцию.

Следующий декоратор

  • принимает вашу функцию decode в качестве входа
  • проверяет, существует ли соответствующий модуль или нет
  • , если модуль существует, затем запускает decode функцию
  • , если модуль не существует, он показывает полезное сообщение и ничего не делает
from functools import wraps


def catch_exception(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            from av.frame import Frame

            return func(*args, **kwargs)
        except ModuleNotFoundError:
            print("Module not found, not applying the function.")

    return wrapper

Вы можете использовать этот декоратор следующим образом:

@catch_exception
def decode(encoded_frame) -> List[Frame]:
    pass

Если соответствующий модуль существует, он запустит функцию, иначе он просто распечатает полезное сообщение об ошибке и ничего не сделает.

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

...