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 xtreak
Recipients David Hoyes, Eli_B, carljm, cjw296, jordan-pittier, mariocj89, michael.foord, pitrou, xtreak
Date 2018-12-14.18:11:33
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1544811093.45.0.788709270274.issue27715@psf.upfronthosting.co.za>
In-reply-to
Content
I think the original issue with patch.object reported by Carl is different from the one reported by David for autospec. Analyzed the report by David and When we call autospec on a class with instance=True then the spec is modeled on the signature of __call__ instead of __init__ where __call__ has the signature of (self, x) and self is  discarded with _eat_self passed as True. But mock also stores _spec_signature that is not aware of skipping self and has the signature as (self, x) and is used for checking signature in mock.assert_called_with. When instance=True then kwargs['_spec_as_instance']=True so does it makes sense to set kwargs['_eat_self'] = True at [0] ? I applied the change and there are no test failures so this deserves a test.

This makes __call__ to have a different signature when it's called from mock and when the call is checked with assert_called_with_once. But it's not the case for methods of the class where self is skipped both for mock and _spec_signature. Since __signature__ is set for mock with my PR this makes it little easy to debug. Can this be dealt as a separate issue?

I would also love to see if the assertion message can be improved since expected_call and actual_call are printed with repr version of the call object in spite of the signature failure. This has caused confusion here and in other places like issue26752 and issue25312.

Sample program to demonstrate difference in signatures :  

import inspect
from unittest import mock

class Foo:

    def __call__(self, x):
        return x

    def bar(self, x):
        pass

m = mock.create_autospec(Foo, instance=True)
m(7)
m.bar(7)
print(inspect.signature(m))
print(m._spec_signature)
print(inspect.signature(m.bar))
print(m.bar._spec_signature)
m.bar.assert_called_once_with(7) # 7 passed as self with no value for x
m.assert_called_once_with(7) # Fails due to self

(x)
(self, x) # self is not skipped in _spec_signature
(x)
(x)
TypeError: missing a required argument: 'x'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "../backups/bpo27715_1.py", line 20, in <module>
    m.assert_called_once_with(7)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 840, in assert_called_once_with
    return self.assert_called_with(*args, **kwargs)
  File "/Users/karthikeyansingaravelan/stuff/python/cpython/Lib/unittest/mock.py", line 827, in assert_called_with
    raise AssertionError(_error_message()) from cause
AssertionError: Expected call: mock(7)
Actual call: mock(7)


[0] https://github.com/python/cpython/blob/f8e9bd568adf85c1e4aea1dda542a96b027797e2/Lib/unittest/mock.py#L2199
History
Date User Action Args
2018-12-14 18:11:33xtreaksetrecipients: + xtreak, pitrou, cjw296, carljm, michael.foord, Eli_B, jordan-pittier, mariocj89, David Hoyes
2018-12-14 18:11:33xtreaksetmessageid: <1544811093.45.0.788709270274.issue27715@psf.upfronthosting.co.za>
2018-12-14 18:11:33xtreaklinkissue27715 messages
2018-12-14 18:11:33xtreakcreate