Введите подсказку декоратора, чтобы обеспечить соответствие подписи в качестве декорируемой функции.Python

Программы на Python
Anonymous
Введите подсказку декоратора, чтобы обеспечить соответствие подписи в качестве декорируемой функции.

Сообщение Anonymous »

Как реализовать DecoratorFactory, чтобы он проверял типы следующим образом:

Код: Выделить всё

def accepts_foo(foo: int): ...
def accepts_bar(bar: int): ...

decorator_foo = DecoratorFactory(foo=1)

decorator_foo(accepts_foo)  # okay because they both accept foo
decorator_foo(accepts_bar)  # type error because the params are different.
Я полагаю, что __init__ DecortorFactor должен принимать только kwargs.

Ближе всего получилось:

Код: Выделить всё

from __future__ import annotations

from typing import Callable, Generic, ParamSpec, TypeVar

from typing_extensions import reveal_type

T = TypeVar("T", covariant=True)
P = ParamSpec("P")

_NOT_SET = object()

class _Decorator(Generic[P, T]):
def __init__(self, f: Callable[P, T]) -> None:
self.f = f
self._result: T = _NOT_SET  # type: ignore

def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
result = self.f(*args, **kwargs)
self._result = result
return result

@property
def result(self) -> T:
assert self._result is not _NOT_SET
return self._result

class _DecoratorFactory(Generic[P]):
def __init__(self, *a: P.args, **k: P.kwargs) -> None:
self.a = a
self.k = k

def __call__(self, func: Callable[P, T]) -> _Decorator[P, T]:
return _Decorator(func)

class DecoratorFactoryFactory(Generic[P]):
def __init__(self) -> None:
pass

def __call__(self, *a: P.args, **k: P.kwargs) -> _DecoratorFactory[P]:
return _DecoratorFactory(*a, **k)

def accepts_foo(foo: int) -> float: ...
def accepts_bar(bar: int) -> str: ...

DecoratorFactory = DecoratorFactoryFactory()
decorator_foo = DecoratorFactory(foo=1)

accepts_foo = decorator_foo(accepts_foo)
reveal_type(accepts_foo(42))
reveal_type(accepts_foo.result)

accepts_bar = decorator_foo(accepts_bar)
reveal_type(accepts_bar(42))
reveal_type(accepts_bar.result)
(Мне пришлось сделать «фабрику-фабрику», иначе я получал кучу ошибок, таких как ошибка: аргументы для ParamSpec «P@Kallable» отсутствуют (reportCallIssue))
pyright:

Код: Выделить всё

$ pyright so.py
/so.py
/so.py:54:13 - information: Type of "accepts_foo(42)" is "float"
/so.py:55:13 - information: Type of "accepts_foo.result" is "float"
/so.py:58:13 - information: Type of "accepts_bar(42)" is "str"
/so.py:59:13 - information: Type of "accepts_bar.result" is "str"
0 errors, 0 warnings, 4 informations
Я бы хотел, чтобы декоратор_foo(accepts_bar) напечатал error, потому что подписи не совпадают.

Подробнее здесь: https://stackoverflow.com/questions/797 ... d-function

Вернуться в «Python»