This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author mariocj89
Recipients cjw296, jaraco, mariocj89, michael.foord, xtreak
Date 2019-02-23.16:29:43
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1550939383.86.0.520693517019.issue35512@roundup.psfhosted.org>
In-reply-to
Content
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.
History
Date User Action Args
2019-02-23 16:29:43mariocj89setrecipients: + mariocj89, jaraco, cjw296, michael.foord, xtreak
2019-02-23 16:29:43mariocj89setmessageid: <1550939383.86.0.520693517019.issue35512@roundup.psfhosted.org>
2019-02-23 16:29:43mariocj89linkissue35512 messages
2019-02-23 16:29:43mariocj89create