`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 stub 和相關測試。檢查預設 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
-
question
難度 2/5 1-3 小時 新手友好度 62/100
conda-forge/vc-feedstock#132 ·
-
難度 2/5 1-3 小時 新手友好度 72/100
limitlessprojects/Go-Hybrid#3342 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 85/100
UKGovernmentBEIS/inspect_ai#6268 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 65/100
LSSTDESC/rail_sompz#42 ·
-
難度 2/5 1-3 小時 新手友好度 66/100
pytest-dev/pytest-forked#113 ·