Author xtreak
Recipients cjw296, michael.foord, xtreak
Date 2018-11-06.05:38:27
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1541482707.6.0.788709270274.issue17185@psf.upfronthosting.co.za>
In-reply-to
Content
Is this being worked on or can I try fixing this?

My analysis so far is as below : 

1. For functions : inspect.signature looks for attribute __signature__ for functions and while creating the mock for a function since we already have the signature we can set __signature__ attribute during _set_signature. I don't know why __signature__ was set. I downloaded mock from GitHub to look at the actual mocksignature implementation which uses lambda signature: _mock_signature to form a mock function which I hope is done here too with a function constructed and evald with sig.bind used for parameter checking. I am still new to the mocksignature internals so I couldn't understand how mocksignature worked.


2. For class and partial functions _check_signature is called and __call__ is overriden in the mock class to check for signature during initialization acting like a constructor. The actual signature will have self along with rest of the parameters but _get_signature_object checks for __init__ and skips the self thus the constructor signature skips self to return a partial function which makes comparing actual constructor call with self and the partial function signature without self to fail.


Attaching a sample patch with tests. Hope I am on the right track and guidance would help. I am changing the version to 3.8

diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index a9c82dcb5d..8cbef0e514 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -103,6 +103,7 @@ def _check_signature(func, mock, skipfirst, instance=False):
         sig.bind(*args, **kwargs)
     _copy_func_details(func, checksig)
     type(mock)._mock_check_sig = checksig
+    type(mock).__signature__ = sig


 def _copy_func_details(func, funcopy):
@@ -172,11 +173,11 @@ def _set_signature(mock, original, instance=False):
     return mock(*args, **kwargs)""" % name
     exec (src, context)
     funcopy = context[name]
-    _setup_func(funcopy, mock)
+    _setup_func(funcopy, mock, sig)
     return funcopy


-def _setup_func(funcopy, mock):
+def _setup_func(funcopy, mock, sig):
     funcopy.mock = mock

     # can't use isinstance with mocks
@@ -224,6 +225,7 @@ def _setup_func(funcopy, mock):
     funcopy.assert_called = assert_called
     funcopy.assert_not_called = assert_not_called
     funcopy.assert_called_once = assert_called_once
+    funcopy.__signature__ = sig

     mock._mock_delegate = funcopy


Initial set of tests where partial function and class test fails : 

def test_spec_inspect_signature(self):
    def foo(a: int, b: int=10, *, c:int) -> int:
        return a  b  c

    mock = create_autospec(foo)
    assert inspect.getfullargspec(mock) == inspect.getfullargspec(foo)
    self.assertRaises(TypeError, mock, 1)

def test_spec_inspect_signature_partial(self):
    def foo(a: int, b: int=10, *, c:int) -> int:
        return a  b  c

    import functools

    partial_object = functools.partial(foo, 1)
    mock = create_autospec(partial_object)
    assert inspect.getfullargspec(mock) == inspect.getfullargspec(partial_object) # Fails
    self.assertRaises(TypeError, partial_object)

def test_spec_inspect_signature_class(self):
    class Bar:
        def __init__(self, a: int):
            self.a = a

    mock = create_autospec(Bar)
    assert inspect.getfullargspec(mock) == inspect.getfullargspec(Bar) # Fails since mock signature has no self
    self.assertRaises(TypeError, mock)
    self._check_someclass_mock(mock)
History
Date User Action Args
2018-11-06 05:38:27xtreaksetrecipients: + xtreak, cjw296, michael.foord
2018-11-06 05:38:27xtreaksetmessageid: <1541482707.6.0.788709270274.issue17185@psf.upfronthosting.co.za>
2018-11-06 05:38:27xtreaklinkissue17185 messages
2018-11-06 05:38:27xtreakcreate