Skip to content

@patch.dict(...) loses wrapped function's signature #16307

Description

@mxr

unittest.mock._patch_dict.__call__ is typed as def __call__(self, f: Any) -> Any: ..., so using @patch.dict(...) as a decorator erases the wrapped function's signature (return type Any). Note _patch.__call__ (plain @patch(...)) has overloads that keep the wrapped callable's signature (via _TT / Callable[_P, _R]).

This makes type checkers that flag signature-erasing decorators (ex: ty's dynamic-function-decorator-return) warn on @patch.dict(...)-decorated test functions.

from unittest.mock import patch


@patch.dict("os.environ", {"FOO": "bar"})
def test_something() -> None: ...
{
    "environment": {
        "python-version": "3.14"
    },
    "rules": {
        "all": "error"
    }
}

See output at https://play.ty.dev/0e61be9c-810a-4266-985b-8daf6377f986

Suggested fix:

diff --git a/stdlib/unittest/mock.pyi b/stdlib/unittest/mock.pyi
index 1b6ab756d..4fc5ea120 100644
--- a/stdlib/unittest/mock.pyi
+++ b/stdlib/unittest/mock.pyi
@@ -301,7 +301,12 @@ class _patch_dict:
     values: Any
     clear: Any
     def __init__(self, in_dict: Any, values: Any = (), clear: Any = False, **kwargs: Any) -> None: ...
-    def __call__(self, f: Any) -> Any: ...
+    @overload
+    def __call__(self, f: _TT) -> _TT: ...
+    @overload
+    def __call__(self, f: _AF) -> _AF: ...
+    @overload
+    def __call__(self, f: _F) -> _F: ...
     def __enter__(self) -> Any: ...
     def __exit__(self, *args: object) -> Any: ...
     def decorate_callable(self, f: _F) -> _F: ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions