如何使用ParamSpec为mock.patch装饰器工厂追加参数类型标注?
为装饰器工厂实现追加参数的类型标注
问题核心:Concatenate的设计是用来前置参数到ParamSpec的开头,而非追加到末尾,所以你原来的Concatenate[P, ClientsMock]写法不符合语法要求——它要求最后一个参数必须是ParamSpec。
下面是两种可行的解决方案:
方案1:Python 3.11+ 原生实现(推荐)
利用PEP 646引入的参数元组拼接语法,直接将ClientsMock追加到原参数列表的末尾:
from typing import ParamSpec, TypeVar, Callable import functools import unittest.mock as mock # 定义类型变量与参数规范 P = ParamSpec('P') R = TypeVar('R') # 替换为你的真实ClientsMock类 class ClientsMock: pass def patch_clients(*targets: str): # 被装饰函数的参数 = 原参数(P) + 末尾追加的ClientsMock def decorator(func: Callable[P.args + (ClientsMock,), P.kwargs, R]) -> Callable[P, R]: clients_mock = ClientsMock() for target in targets: func = mock.patch(target, clients_mock)(func) @functools.wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: return func(*args, clients_mock, **kwargs) return wrapper return decorator # 类型检查正常工作的使用示例 class MyTestCase(unittest.TestCase): @patch_clients('some.module.import') def test_foo(self, clients_mock: ClientsMock): pass
方案2:兼容Python 3.10及以下
使用typing_extensions提供的扩展语法来实现参数追加:
from typing_extensions import ParamSpec, TypeVar, Callable, Unpack import functools import unittest.mock as mock P = ParamSpec('P') R = TypeVar('R') class ClientsMock: pass def patch_clients(*targets: str): def decorator(func: Callable[[Unpack[P.args], ClientsMock, **P.kwargs], R]) -> Callable[P, R]: clients_mock = ClientsMock() for target in targets: func = mock.patch(target, clients_mock)(func) @functools.wraps(func) def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: return func(*args, clients_mock, **kwargs) return wrapper return decorator # 使用示例 class MyTestCase(unittest.TestCase): @patch_clients('some.module.import') def test_foo(self, clients_mock: ClientsMock): pass
两种方案的核心逻辑一致:明确标注被装饰函数的参数是原参数列表(比如测试方法的self)加上末尾的clients_mock,装饰后的函数只需要接收原参数。类型检查器会正确识别测试方法中的参数类型,不会破坏原有self参数的位置。
内容的提问来源于stack exchange,提问作者tibbe
相关产品推荐
相关产品推荐

