classification
Title: unittest.mock.patch: Double patching instance method: AttributeError: Mock object has no attribute '__name__'
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.6, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Anthony Sottile, asottile, cjw296, michael.foord, xtreak
Priority: normal Keywords: patch

Created on 2016-04-06 20:01 by asottile, last changed 2018-12-12 09:01 by cjw296. This issue is now closed.

Files
File name Uploaded Description Edit
patch asottile, 2016-04-06 21:17 review
patch2 asottile, 2016-04-06 23:32 review
2.patch Anthony Sottile, 2016-05-01 21:48 review
Pull Requests
URL Status Linked Edit
PR 11085 merged Anthony Sottile, 2018-12-10 21:22
PR 11126 merged miss-islington, 2018-12-12 09:00
Messages (9)
msg262960 - (view) Author: Anthony Sottile (asottile) * Date: 2016-04-06 20:01
Originally from https://github.com/testing-cabal/mock/issues/350

## Example
```python
from unittest import mock

class C(object):
    def f(self):
        pass

c = C()

with mock.patch.object(c, 'f', autospec=True):
    with mock.patch.object(c, 'f', autospec=True):
        pass
```

## Python3.3

```
$ test.py
$
```

## Python3.4 / 3.5 / 3.6 (From gitbhub.com/python/cpython@fa3fc6d7)

```
Traceback (most recent call last):
  File "test.py", line 10, in <module>
    with mock.patch.object(c, 'f', autospec=True):
  File "/home/asottile/workspace/cpython/Lib/unittest/mock.py", line 1320, in __enter__
    _name=self.attribute, **kwargs)
  File "/home/asottile/workspace/cpython/Lib/unittest/mock.py", line 2220, in create_autospec
    _check_signature(original, new, skipfirst=skipfirst)
  File "/home/asottile/workspace/cpython/Lib/unittest/mock.py", line 112, in _check_signature
    _copy_func_details(func, checksig)
  File "/home/asottile/workspace/cpython/Lib/unittest/mock.py", line 117, in _copy_func_details
    funcopy.__name__ = func.__name__
  File "/home/asottile/workspace/cpython/Lib/unittest/mock.py", line 578, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute '__name__'
```
msg262966 - (view) Author: Anthony Sottile (asottile) * Date: 2016-04-06 21:17
The root cause seems to be that autospecced functions return a function object (not a Mock instance) which a '.mock' attribute which is a MagicMock ( assigned here: https://github.com/python/cpython/blob/ae775ab1eb72f42de2d070158bade4bf261ac04f/Lib/unittest/mock.py#L198 )

I took a first stab at a patch (attached)
msg262972 - (view) Author: Anthony Sottile (asottile) * Date: 2016-04-06 23:32
Here's an improved patch which:

- passes the tests
- puts the test in the correct place

I'm not entirely happy with the approach -- open to suggestions :)
msg264611 - (view) Author: Anthony Sottile (Anthony Sottile) * Date: 2016-05-01 21:48
Seems I've named the patchfile incorrectly -- Hopefully this is correct this time?
msg331430 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2018-12-09 12:56
Thanks @asottile for the patch. I think the original AttributeError is resolved with issue28919 where they were silenced. It seems similar to issue32153 though the exception occurs from mock instead of partial object as in issue32153. The fix was applied to 3.7+ and hence 3.6 was not fixed which enters security fix only mode shortly. The attached tests also pass on master and I think it will it be a good unittest addition to Lib/unittest/test/testmock/testwith.py (similar to issue32153) that has a couple of nested with statements for the same attribute. Adding cjw296 to the list.
msg331536 - (view) Author: Anthony Sottile (Anthony Sottile) * Date: 2018-12-10 21:23
I've opened a PR with the test included: https://github.com/python/cpython/pull/11085
msg331579 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2018-12-11 06:23
Before we get too far: what's the use case for this double patching?
msg331588 - (view) Author: Anthony Sottile (Anthony Sottile) * Date: 2018-12-11 06:55
to be honest, I don't recall exactly given it's been 2 and a half years since the original report with no activity.

if I recall correctly, this was encountered while upgrading the `mock` backport in yelp's monolithic repository.

I want to say the reason this was hard to "fix" properly was due to some blanket patches being applied in a base test case and then other test cases re-patching those methods to add more specific behaviour.  This worked fine in python2.7 and all the way until python3.3 but then was broken by changes in python3.4

Fortunately, they've been fixed in python3.7.  I guess I've been encouraged to write a patch with a test so it does not regress in the future
msg331590 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2018-12-11 07:09
Ah, yeah, I can see the blanket patch and a more local patch in a monorepo being a thing, cool, let's have a look!
History
Date User Action Args
2018-12-12 09:01:53cjw296setstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2018-12-12 09:00:26miss-islingtonsetpull_requests: + pull_request10358
2018-12-11 07:09:29cjw296setmessages: + msg331590
2018-12-11 06:55:45Anthony Sottilesetmessages: + msg331588
2018-12-11 06:23:47cjw296setmessages: + msg331579
2018-12-10 21:23:00Anthony Sottilesetmessages: + msg331536
2018-12-10 21:22:31Anthony Sottilesetpull_requests: + pull_request10317
2018-12-09 12:56:47xtreaksetnosy: + cjw296, xtreak
messages: + msg331430
2016-05-01 21:48:00Anthony Sottilesetfiles: + 2.patch

nosy: + Anthony Sottile
messages: + msg264611

keywords: + patch
2016-04-07 14:50:00SilentGhostsetnosy: + michael.foord
stage: patch review
type: crash -> behavior

versions: - Python 3.4
2016-04-06 23:32:02asottilesetfiles: + patch2

messages: + msg262972
2016-04-06 21:17:51asottilesetfiles: + patch

messages: + msg262966
2016-04-06 20:01:26asottilecreate