Message336385
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. |
|
Date |
User |
Action |
Args |
2019-02-23 16:29:43 | mariocj89 | set | recipients:
+ mariocj89, jaraco, cjw296, michael.foord, xtreak |
2019-02-23 16:29:43 | mariocj89 | set | messageid: <1550939383.86.0.520693517019.issue35512@roundup.psfhosted.org> |
2019-02-23 16:29:43 | mariocj89 | link | issue35512 messages |
2019-02-23 16:29:43 | mariocj89 | create | |
|