Mario Corchero <marioc...@gmail.com> added the comment:
Interesting, `patch` does resolve it when the patched function is called (see https://github.com/python/cpython/blob/175421b58cc97a2555e474f479f30a6c5d2250b0/Lib/unittest/mock.py#L1269) vs patch.dict that resolves it at the time the patcher is created - when decorating - (see https://github.com/python/cpython/blob/175421b58cc97a2555e474f479f30a6c5d2250b0/Lib/unittest/mock.py#L1624). An option might be to delay the resolution as done for patch, changing https://github.com/python/cpython/blob/175421b58cc97a2555e474f479f30a6c5d2250b0/Lib/unittest/mock.py#L1625 to `self.in_dict_name = in_dict` Example untested patch: ``` diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 8f46050462..5328fda417 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1620,9 +1620,7 @@ class _patch_dict(object): """ def __init__(self, in_dict, values=(), clear=False, **kwargs): - if isinstance(in_dict, str): - in_dict = _importer(in_dict) - self.in_dict = in_dict + self.in_dict_name = in_dict # support any argument supported by dict(...) constructor self.values = dict(values) self.values.update(kwargs) @@ -1649,7 +1647,7 @@ class _patch_dict(object): attr_value = getattr(klass, attr) if (attr.startswith(patch.TEST_PREFIX) and hasattr(attr_value, "__call__")): - decorator = _patch_dict(self.in_dict, self.values, self.clear) + decorator = _patch_dict(self.in_dict_name, self.values, self.clear) decorated = decorator(attr_value) setattr(klass, attr, decorated) return klass @@ -1662,7 +1660,11 @@ class _patch_dict(object): def _patch_dict(self): values = self.values - in_dict = self.in_dict + if isinstance(self.in_dict_name, str): + in_dict = _importer(self.in_dict_name) + else: + in_dict = self.in_dict_name + self.in_dict = in_dict ``` > This seems to be not a problem with patch.object where redefining a class > later like dict seems to work correctly and maybe it's due to creating a new > class itself that updates the local to reference new class? For patch, when you create a new class, the new one is patched as the name is resolved at the time the decorated function is executed, not when it is decorated. See: ``` $ cat t.py from unittest import mock import c target = dict(a=1) @mock.patch("c.A", "target", "updated") def test_with_decorator(): print(f"target inside decorator : {A.target}") def test_with_context_manager(): with mock.patch("c.A", "target", "updated"): print(f"target inside context : {A.target}") class A: target = "changed" c.A = A test_with_decorator() test_with_context_manager() xarmariocj89 at DESKTOP-9B6VH3A in ~/workspace/cpython on master* $ cat c.py class A: target = "original" mariocj89 at DESKTOP-9B6VH3A in ~/workspace/cpython on master* $ ./python ./t.py target inside decorator : changed target inside context : changed ``` If `patch` was implemented like `patch.dict`, you would see the first as "changed" as the reference to `c.A` would have been resolved when the decorator was run (before the re-definition of `A`). About `patch.object`, it cannot be compared, as it grabs the name at the time you execute the decorator because you are not passing a string, but the actual object to patch. ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue35512> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com