我正在尝试注解一个注入器装饰器,当函数被调用时,它将全局字典中的一个值作为关键字参数注入到装饰函数中。
有没有人能帮我解释一下用参数注解装饰器的经验?尝试过注解,但是遇到了下面的错误:
import functools
import inspect
from typing import Any, Callable, TypeVar, ParamSpec
Type = TypeVar('Type')
Param = ParamSpec('Param')
_INSTANCES = {}
def make_injectable(instance_name: str, instance: object) -> None:
_INSTANCES[instance_name] = instance
def inject(*instances: str) -> Callable[Param, Type]:
def get_function_with_instances(fn: Callable[Param, Type]) -> Callable[Param, Type]:
# This attribute is to easily access which arguments of fn are injectable
fn._injectable_args = instances
def handler(*args: Param.args, **kwargs: Param.kwargs) -> Type:
new_kwargs: dict[str, Any] = dict(kwargs).copy()
for instance in instances:
if instance in new_kwargs:
continue
if instance not in _INSTANCES:
raise ValueError(f"Instance {instance} was not initialized yet")
new_kwargs[instance] = _INSTANCES[instance]
return fn(*args, **new_kwargs)
if inspect.iscoroutinefunction(fn):
@functools.wraps(fn)
async def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> Callable[Param, Type]:
return await handler(*args, **kwargs)
else:
@functools.wraps(fn)
def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> Callable[Param, Type]:
return handler(*args, **kwargs)
return wrapper
return get_function_with_instances
如果我用这些注解运行mypy,我会得到这些错误,如果不创建新的错误,我就无法避免这些错误:
mypy injector.py --strict --warn-unreachable --allow-subclassing-any --ignore-missing-imports --show-error-codes --install-types --non-interactive
injector.py:33: error: "Callable[Param, Type]" has no attribute "_injectable_args" [attr-defined]
injector.py:48: error: Returning Any from function declared to return "Callable[Param, Type]" [no-any-return]
injector.py:48: error: Incompatible types in "await" (actual type "Type", expected type "Awaitable[Any]") [misc]
injector.py:53: error: Incompatible return value type (got "Type", expected "Callable[Param, Type]") [return-value]
injector.py:55: error: Incompatible return value type (got "Callable[Param, Coroutine[Any, Any, Callable[Param, Type]]]", expected "Callable[Param, Type]") [return-value]
injector.py:57: error: Incompatible return value type (got "Callable[[Callable[Param, Type]], Callable[Param, Type]]", expected "Callable[Param, Type]") [return-value]
谢谢你抽出时间。
1条答案
按热度按时间vaqhlq811#
第一个
[attr-defined]
错误是不可避免的IMO,应该直接显式忽略。第二个和第三个错误是关于代码
[no-any-return]
/misc
的,我稍后会回来。[return-value]
代码出现的第四个错误是因为 Package 器的返回注解应该是T
,而不是Callable[P, T]
,它应该返回修饰函数返回的任何内容。第五个错误(也是
[return-value]
)告诉您,wrapper可能是一个可以等待产生T
的协程,但是您声明了get_function_with_instances
以返回一个返回T
的可调用对象(不是一个等待T
的协程)。最后一个
[return-value]
错误出现是因为inject
返回了一个decorator,它接受了一个Callable[P, T]
类型的参数,并再次返回了一个相同类型的对象,所以inject
的返回注解确实应该是Callable[[Callable[P, T]], Callable[P, T]]
,就像mypy
所说的那样。现在来看看
[no-any-return]
/misc
错误,这有点令人困惑,因为您的意图是涵盖fn
作为协程函数的情况和它作为常规函数的情况。您可以对
handler
进行注解,以返回T
,就像fn
一样。但是,T
是什么,并没有进一步缩小范围。由iscoroutinefunction
给出的类型保护适用于fn
,并且不会自动扩展到handler
。从静态类型检查器的Angular 来看,handler
返回 * some object *。并且不能安全地假定该对象是可等待的。因此,不能安全地将其用于await
([misc]
错误)。由于类型检查器甚至不允许在该行中使用
await
表达式,因此它显然无法验证返回值是否确实匹配wrapper
的注解(应该是T
,就像前面提到的错误一样,但在本例中,这两种方式都无关紧要)。我不能100%确定这两个错误的根本原因。
如果我是你,如果一开始就不检查修饰函数,我的工作会轻松很多,修饰器的行为不会改变,唯一的区别是一个调用需要后面跟一个
await
来获取值。您可以让装饰器不知道fn
是否返回一个awatable,并让调用者来处理它。以下是我的建议:
下面是一个快速测试,显示类型都被正确地推断出来了:
输出示例:
mypy --strict
没有任何抱怨,从main
的编写方式来看,我们可以看到返回类型都被正确地推断出来了,但是如果我们想显式地检查,我们可以在脚本的末尾添加reveal_type(f)
和reveal_type(g)
,那么mypy
会告诉我们:这正是我们所期待的。