autospecced Mock无法匹配原函数类型提示的问题排查
问题描述
我希望通过create_autospec生成的autospecced mock能提供与原函数一致的类型提示,但未能实现。以下是测试的最小示例代码:
from __future__ import annotations import typing from unittest.mock import create_autospec import pandas as pd def func(a: int, c: pd.Timestamp, d: typing.Literal['e', 'f']) -> None: pass func_annotations = func.__annotations__ mock_func = create_autospec(func) type_hints = typing.get_type_hints(func) # {'a': <class 'int'>, 'c': <class 'pandas._libs.tslibs.timestamps.Timestamp'>, 'return': <class 'NoneType'>} # 期望此结果与type_hints一致 type_hints_mocked = typing.get_type_hints(mock_func) # {} # 尝试复制__annotations__但无效 mock_func.__annotations__ = func_annotations type_hints_mocked_2 = typing.get_type_hints(mock_func) # NameError: name 'pd' is not defined
请问为何mock函数无法获取与原函数相同的类型提示,以及如何解决该问题?
原因分析
create_autospec生成的Mock对象默认不会继承原函数的__annotations__属性,调用typing.get_type_hints()自然返回空字典。- 因为启用了
from __future__ import annotations,函数注解会以字符串形式存储。直接复制原函数的__annotations__给Mock后,get_type_hints()需要解析这些字符串,但Mock对象的__globals__命名空间里没有pd这类导入的名称,解析时就会抛出NameError。
解决方法
方法一:同步Mock的全局命名空间
把原函数的__globals__赋值给Mock对象,让解析字符串注解时能找到所需名称:
from __future__ import annotations import typing from unittest.mock import create_autospec import pandas as pd def func(a: int, c: pd.Timestamp, d: typing.Literal['e', 'f']) -> None: pass mock_func = create_autospec(func) # 同步原函数的全局命名空间 mock_func.__globals__ = func.__globals__ # 复制原函数的注解 mock_func.__annotations__ = func.__annotations__ type_hints_mocked = typing.get_type_hints(mock_func) # 结果与原函数一致:{'a': int, 'c': pandas._libs.tslibs.timestamps.Timestamp, 'return': NoneType}
方法二:直接赋值解析后的类型提示
先通过get_type_hints()拿到原函数解析后的类型,再赋值给Mock的__annotations__,跳过字符串解析步骤:
from __future__ import annotations import typing from unittest.mock import create_autospec import pandas as pd def func(a: int, c: pd.Timestamp, d: typing.Literal['e', 'f']) -> None: pass mock_func = create_autospec(func) # 获取原函数解析后的类型提示 resolved_hints = typing.get_type_hints(func) # 把解析后的类型赋值给Mock的__annotations__ mock_func.__annotations__ = resolved_hints type_hints_mocked = typing.get_type_hints(mock_func) # 结果与原函数一致
方法三:结合functools.wraps包装Mock
用functools.wraps把原函数的元数据(包括注解)复制给Mock,同时保留create_autospec生成的mock行为:
from __future__ import annotations import functools import typing from unittest.mock import create_autospec import pandas as pd def func(a: int, c: pd.Timestamp, d: typing.Literal['e', 'f']) -> None: pass mock_func = create_autospec(func) # 使用wraps复制原函数的元数据 mock_func = functools.wraps(func)(mock_func) type_hints_mocked = typing.get_type_hints(mock_func) # 结果与原函数一致
内容的提问来源于stack exchange,提问作者ano
相关产品推荐
相关产品推荐

