Поскольку фактический код генерации сообщения на основе параметров и построения объекта ответа довольно повторяется, я использую для этого декоратор. Хотя в 90% случаев пользователь хочет отправитьMessageX() -> ResponseToMessageX, бывают случаи, когда пользователю приходится отправлятьMessageX() -> Future. Поскольку обе функции принимают одни и те же параметры и по сути возвращают одно и то же (
Код: Выделить всё
ResponsToMessageXСамый простой способ сделать это для меня - злоупотреблять питонами, все есть объект и просто добавьте вторую функцию как свойство к объекту функции, т.е.:
Код: Выделить всё
def decorator(f):
def decorated_f():
return f()
def decorated_f_split():
return f'()
decorated_f.second_function = decorated_f_split
return decorated_f
Чтобы упростить работу пользователя Мне бы хотелось, чтобы подсказки по-прежнему работали (чтобы редактор мог подсказывать, какие свойства имеет объект ответа).
Хотя я заставил это работать, пирайт все еще жалуется, и я не могу избавиться от предупреждения. Вот MWE того, что я придумал:
Код: Выделить всё
from functools import wraps
from typing import (
Any,
Awaitable,
Callable,
Concatenate,
ParamSpec,
Protocol,
TypeVar,
cast,
)
T = TypeVar("T")
C = TypeVar("C", covariant=True)
P = ParamSpec("P") # , covariant=True)
class X(Protocol[P, C]):
def split(self, *args: Any, **kwds: Any) -> Awaitable[C]:
...
def __call__(self, *args: Any, **kwds: Any) -> C:
...
def wrap_returntype_awaitable(f: Callable[P, T]) -> Callable[P, Awaitable[T]]:
f.__annotations__["return"] = Awaitable[T]
g = cast(Callable[P, Awaitable[T]], f)
return g
def decorator(f: Callable[Concatenate[Any, P], C]) -> X[P, C]:
class Wrapper:
# @wrap_returntype_awaitable
@wraps(wrap_returntype_awaitable(f))
async def split(self, *args: P.args, **kwds: P.kwargs) -> Awaitable[C]:
await self.send_command()
return self.get_response()
@wraps(f)
async def __call__(self, *args: P.args, **kwds: P.kwargs) -> C:
response_receiver = await Wrapper.split(self, *args, **kwds)
return await response_receiver
Wrapper.split.__annotations__["return"] = Awaitable[C]
return Wrapper()
class A:
async def send_command(self):
pass
async def get_response(self):
pass
async def command(self, a: int) -> int:
await self.send_command()
return a
@decorator
async def x(self, a1: int) -> int:
...
async def demo():
a = A()
response = await a.x()
split_command = await a.x.split()
split_response = await split_command
Код: Выделить всё
get_responsePyright жалуется на следующее:
Код: Выделить всё
p2.py:38:24 - error: Cannot access member "send_command" for type "Wrapper"
Member "send_command" is unknown (reportGeneralTypeIssues)
p2.py:39:25 - error: Cannot access member "get_response" for type "Wrapper"
Member "get_response" is unknown (reportGeneralTypeIssues)
p2.py:48:12 - error: Expression of type "Wrapper" cannot be assigned to return type "X[P@decorator, C@decorator]"
"Wrapper" is incompatible with protocol "X[P@decorator, C@decorator]"
Type parameter "P@X" is invariant, but "P@X" is not the same as "P@decorator"
Type parameter "C@X" is covariant, but "Awaitable[C@decorator]" is not a subtype of "C@decorator"
Type "Awaitable[C@decorator]" cannot be assigned to type "C@decorator" (reportGeneralTypeIssues)
3 errors, 0 warnings, 0 informations
Код: Выделить всё
Base_AКод: Выделить всё
get_responseЯ мог бы попытаться разделить декоратор в декоратор функции, который отмечает мои функции, и декоратор класса (который будет иметь доступ к базовому классу), но тогда я снова буду жаловаться на присвоение свойств функции.
Мой больший проблема связана с двумя последними ошибками:
Код: Выделить всё
"Wrapper" is incompatible with protocol "X[P@decorator, C@decorator]"
Type parameter "P@X" is invariant, but "P@X" is not the same as "P@decorator"
Type parameter "C@X" is covariant, but "Awaitable[C@decorator]" is not a subtype of "C@decorator"
Type "Awaitable[C@decorator]" cannot be assigned to type "C@decorator"
И я думал, что набрал правильно, так что Awaitable[C@decorator] ожидается в нужных местах, однако программа проверки типов явно думает иначе.
(Я не контролирую протокол и не могу изменить его дизайн)
РЕДАКТИРОВАТЬ: я был так сосредоточен на завершении/ вывод работать (т.е. для примера редактор знает, что функции возвращают int/
Код: Выделить всё
Awaitable[int]
Подробнее здесь: https://stackoverflow.com/questions/772 ... t-property