classification
Title: unittest mock's reset_mock throws an error when an attribute has been deleted
Type: behavior Stage: patch review
Components: Library (Lib) Versions: Python 3.8, Python 3.7, Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: hmvp, michael.foord, xtreak
Priority: normal Keywords: patch

Created on 2017-08-10 14:22 by hmvp, last changed 2018-09-25 05:35 by xtreak.

Pull Requests
URL Status Linked Edit
PR 9302 open xtreak, 2018-09-14 10:46
Messages (3)
msg300090 - (view) Author: Hmvp (hmvp) Date: 2017-08-10 14:22
When using a mock and deleting a attribute reset_mock cannot be used anymore since it tries to call reset_mock on the _deleted sentinel value.

Reproduction path:
```
from unittest.mock import MagicMock
mock = MagicMock()
mock.a = 'test'
del mock.a
mock.reset_mock()
```

Gives:
```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.5/unittest/mock.py", line 544, in reset_mock
    child.reset_mock(visited)
AttributeError: '_SentinelObject' object has no attribute 'reset_mock'
```

Expected result:
mock is reset without throwing an exception and the 'a' attribute is no longer in a deleted state

Only checked 3.5 and current master if bug is present
msg325340 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2018-09-14 10:10
Can confirm this behavior on CPython master as well. It seems that when an attribute is deleted then a deleted flag is set for the attribute at https://github.com/python/cpython/blob/73820a60cc3c990abb351540ca27bf7689bce8ac/Lib/unittest/mock.py#L737 . But when reset_mock is called it doesn't check for the deleted flag at https://github.com/python/cpython/blob/73820a60cc3c990abb351540ca27bf7689bce8ac/Lib/unittest/mock.py#L543

➜  cpython git:(master) ./python.exe ../backups/bpo31177.py
Traceback (most recent call last):
  File "../backups/bpo31177.py", line 5, in <module>
    m.reset_mock()
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 546, in reset_mock
    child.reset_mock(visited)
AttributeError: '_SentinelObject' object has no attribute 'reset_mock'

A simple patch would be to skip the deleted as below but some of the code in mock module raise an AttributeError. I don't know the correct behavior here. But applying the below patch and running tests with `./python.exe Lib/unittest/test/` doesn't cause any test failure.

diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index db1e642c00..700e2fb8b9 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -541,7 +541,7 @@ class NonCallableMock(Base):
             self._mock_side_effect = None
 
         for child in self._mock_children.values():
-            if isinstance(child, _SpecState):
+            if isinstance(child, _SpecState) or child is _deleted:
                 continue
             child.reset_mock(visited)
 
I will try to make a PR if it's ok.

Thanks
msg326322 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2018-09-25 05:35
Adding Michael for thoughts on the fix and desired behavior. Removing 3.5 since only security fixes are accepted and adding 3.8 which is also affected.

Thanks
History
Date User Action Args
2018-09-25 05:35:14xtreaksetnosy: + michael.foord

messages: + msg326322
versions: + Python 3.8, - Python 3.5
2018-09-14 10:46:40xtreaksetkeywords: + patch
stage: patch review
pull_requests: + pull_request8731
2018-09-14 10:10:37xtreaksetnosy: + xtreak
messages: + msg325340
2017-08-10 14:22:19hmvpcreate