Karthikeyan Singaravelan <tir.kar...@gmail.com> added the comment:

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

----------
nosy: +cjw296, mariocj89
versions:  -Python 3.6

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue27715>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to