Как написать декоратор, который принимает любую функцию и передает mypy —disallow-any-decorated?

#python #decorator #python-decorators #mypy #typechecking

#python #декоратор #python-декораторы #mypy #проверка типов

Вопрос:

Моя последняя попытка написать декоратор, который принимает любую возможную функцию python и передает проверку mypy с --disallow-any-decorated флагом, выглядела так:

 from typing import Any, Callable, TypeVar


T = TypeVar('T')


def decorator(func: Callable[..., T]) -> Callable[..., T]:
    def decorated(*args: Any, **kwargs: Any) -> Any:
        print('decorated')
        return func(*args, **kwargs)

    return decorated


@decorator
def foo() -> int:
    print('foo')
    return 42


print(foo())
  

Однако он по-прежнему терпит неудачу с Type of decorated function contains type "Any" ("Callable[..., int]")

Что я делаю не так? Я также пытался использовать VarArg и KwArg из mypy_extensions вместо ... , но это не помогло.

Ответ №1:

—запретить любое оформление:

Запрещает функции, которые имеют Any в своей подписи после преобразования декоратора.

... Callable[..., T] это способ сказать, что вызываемый объект принимает ноль или более Any аргументов. Таким образом, даже если объявление не содержит Any самого себя, оно все равно преобразуется в объявление, содержащее Any .

Комментарии:

1. Да, я это понимаю. Есть ли у вас какое-либо решение, как аннотировать такой декоратор?

Ответ №2:

Используйте TypeVar, чтобы поглотить весь вызываемый объект, а не только возвращаемый тип. Затем вы разъясняете MyPy, что тип оформленной функции совпадает с исходной, освобождая его от необходимости беспокоиться «О, но возвращаемый тип здесь может быть практически любым».

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

 FuncT = TypeVar(FuncT, bound=Callable)

def decorator(func: FuncT) -> FuncT:
   ...
   return cast(FuncT, decorated)
  

Комментарии:

1. Спасибо за ответ. Я не могу протестировать это прямо сейчас, но, похоже, это может решить мою проблему.