`unittest.mock.patch` and `unittest.mock.patch.object` are wrongly typed when new_callable is provided
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
检查 issue 中显示的 unittest.mock.patch 和 patch.object 重载,然后找到它们的 typeshed 存根和相关测试。检查默认 patching、new、new_callable 以及不可调用的 new_callable 情况下推断出的类型;完成的标准是两个 API 都只接受可调用的 factory,并正确推断所创建对象的类型。
由索引模型根据 Issue 内容生成。
描述
Steps to reproduce
from unittest.mock import patch
class NewPrinter:
pass
with patch("pprint.PrettyPrinter", new_callable=lambda: NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # "Union[unittest.mock.MagicMock, unittest.mock.AsyncMock]"
Same deal with unittest.mock.patch.object
from unittest.mock import patch
import pprint
class NewPrinter:
...
with patch.object(pprint, "PrettyPrinter", new_callable=lambda: NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # "Union[unittest.mock.MagicMock, unittest.mock.AsyncMock]"
Suggested fix
We discuss unittest.mock.patch in the following, but the same principles apply to unittest.mock.patch.object too
Current signatures:
@overload
def __call__(
self,
target: str,
new: _T,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Any | None = ...,
**kwargs: Any,
) -> _patch[_T]: ...
@overload
def __call__(
self,
target: str,
*,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Any | None = ...,
**kwargs: Any,
) -> _patch_default_new: ...
There are 2 problems here:
new_callableshouldn't beAny | Nonein the first place, it should only acceptCallable| None, as the it will raise if the provided value is not a callablewith patch("pprint.PrettyPrinter", new_callable=1) as patched_printer: # TypeError: 'int' object is not callable- Need to differentiate the case where new_callable is provided and left as default None
- When
new_callable: Callable[..., T], returns_patch[_T] - When
new_callable: None = ..., returns_patch_default_new
- When
Seems like this would do the job:
@overload
def __call__(
self,
target: str,
new: _T,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Callable[..., Any] | None = ...,
**kwargs: Any,
) -> _patch[_T]: ...
@overload
def __call__(
self,
target: str,
*,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Callable[..., _T],
**kwargs: Any,
) -> _patch[_T]
@overload
def __call__(
self,
target: str,
*,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: None = ...,
**kwargs: Any,
) -> _patch_default_new: ...
Testing results
from unittest.mock import patch
class NewPrinter:
pass
with patch("pprint.PrettyPrinter") as patched_printer:
print(patched_printer) # <MagicMock name='PrettyPrinter' id='125908774402896'>
reveal_type(patched_printer) # MagicMock | AsyncMock
with patch("pprint.PrettyPrinter", new=NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # type[NewPrinter]
with patch("pprint.PrettyPrinter", new_callable=lambda: NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # type[NewPrinter]
# No overload variant matches for non-callable type
with patch("pprint.PrettyPrinter", new_callable="non-callable") as patched_printer:
...
Haven't tried unittest.mock.patch.object yet, but probably same thing
Would be great to see what the maintainers think.
I'll dig deeper and try to open a PR, maybe this weekend, will be my first PR in typeshed :)
- 主要语言
- Python
- 星标
- 5.1k
- 派生
- 2.1k
- 平均合并
- 2 天 7 分钟
- 30 天内合并 PR
- 116
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
python/typeshed 的其他 Issue
-
`networkx.set_edge_attributes` should support scalar value + name可能已有人在做 @Fire162 于 9 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 67/100
维护者通常 1 天内回复
-
false alarm on decode call可能已有人在做 @jw9829 于 36 天前认领。 未关闭help wanted stubs: false positive
难度 2/5 1-3 小时 新手友好度 76/100
python/typeshed#16441 · 1 条评论 ·
维护者通常 1 天内回复
-
Don't run pyright basic checks on projects with strict pyright settings in CI可能已有人在做 @Mohith1612 于 73 天前认领。 未关闭project: infrastructure
难度 2/5 1-3 小时 新手友好度 68/100
python/typeshed#16005 · 1 条评论 ·
维护者通常 1 天内回复
-
stubs: improvement
难度 2/5 1-3 小时 新手友好度 72/100
python/typeshed#15548 · 5 条评论 ·
维护者通常 1 天内回复
-
stubs: improvement
难度 2/5 1-3 小时 新手友好度 64/100
python/typeshed#15529 · 5 条评论 ·
维护者通常 1 天内回复
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 72/100
pytest-dev/pyfakefs#1367 · 1 条评论 ·
维护者通常 2 天内回复
-
IFEvalG exclude_word_harder misses the keyword next to punctuation, at the start or end of the text, and in other letter case可能已有人在做 @damienen 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 76/100
allenai/open-instruct#2151 ·
维护者通常 1 天内回复
-
feat(plan): support convertorLabels and convertorAffinity in Plan resource可能已有人在做 @AmenB 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 82/100
RedHatQE/openshift-python-wrapper#2849 · 1 条评论 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 65/100
维护者通常 25 天内回复
-
难度 2/5 1-3 小时 新手友好度 70/100
libnativeapi/nativeapi#21 ·