You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 15:35:37