我有以下代码片段
from contextlib import _GeneratorContextManager, contextmanager
GoodWrapperType = Callable[[int, str], _GeneratorContextManager[None]]
BadWrapperType = Callable[[int, str], Generator[None, None, None]]
def wrapper() -> GoodWrapperType:
@contextmanager
def inner(some_int: int, some_str: str) -> Generator[None, None, None]:
# Do things with some_int, some_str
yield
return inner
我想在
pytest
测试套件的上下文中执行此操作,其中包装器将一些固定装置注入到 inner
中。
我上面有
GoodWrapperType
和BadWrapperType
。 Pylance 告诉我,我无法将 BadWrapperType
指定为 wrapper
的返回类型。我已经找到了 GoodWrapperType
的解决方案,使用 _GeneratorContextManager
,但由于它带有 _
前缀,所以我不应该导入它。
有更好的方法吗?这样做的正确方法是什么?
我想知道没有直接的解决方案(无论如何我都找到了)这一事实是否可能暗示我不应该在Python中这样做。
wrapper
不返回可返回生成器函数的可调用对象,它返回可返回上下文管理器的可调用对象,这是一个具有 __enter__
和 __exit__
方法的类。
@contextmanager
装饰器从它所应用的生成器函数构建此类,但最终结果不是生成器。
因此,我们希望输入
inner
作为返回生成器函数,但 wrapper
作为返回可调用函数,该可调用函数返回上下文管理器,该上下文管理器是应用装饰器的结果。
这是一个类型检查的版本:
from contextlib import contextmanager, AbstractContextManager
from typing import Callable, Iterator, ContextManager
WrapperType = Callable[[int, str], AbstractContextManager[None]]
def wrapper() -> WrapperType:
@contextmanager
def inner(some_int: int, some_str: str) -> Iterator[None]:
# Do things with some_int, some_str
yield
return inner
https://mypy-play.net/?mypy=latest&python=3.11&flags=strict&gist=6b2b3360a4916ec654a84ac84fcea544
如果我们不使用生成器的发送和返回功能,我们可以将其类型简化为如上所述的
Iterator[<yield type>]
。
AbstractContextManager[<enter return type>]
是上下文管理器的通用类型。 (注意:Python 3.9 之前您应该使用 from typing import ContextManager
代替)
由于我们的内部函数没有产生任何结果,因此两种情况下的类型参数都是
None
。
但是
contextmanager
返回一个“上下文装饰器”,这是一个双重用途的类,可以充当上下文管理器或装饰器。使用 AbstractContextManager
键入将隐藏下游类型检查结果的装饰性。
如果您需要该属性,那么您最初使用
_GeneratorContextManager
的方法可能是最好的方法。 (有一个公共 contextlib.ContextDecorator
基类,但 mypy 缺乏交集类型,因此没有方便的方法仅使用公开导出的类型将某些内容同时键入 AbstractContextManager
和 ContextDecorator
)。