From 5563d4459579ee45c8a2869fa81f274b6269e27f Mon Sep 17 00:00:00 2001 From: Aaron Gallagher Date: Fri, 10 Feb 2017 14:57:03 -0800 Subject: [PATCH 1/3] Fix autospec's behavior on method-bound builtins. Cython will, in the right circumstances, offer a MethodType instance where im_func is a builtin function. Any instance of MethodType is automatically assumed to be a python-defined function (more specifically, a function that has an inspectable signature), but _set_signature was still conservative in its assumptions. As a result _set_signature would return early with None instead of a mock since the im_func had no inspectable signature. This causes problems deeper inside mock, as _set_signature is assumed to _always_ return a mock, and nothing checked its return value. In similar corner cases, autospec will simply not check the spec of the function, so _set_signature is amended to now return early with the original, not-wrapped mock object. --- Lib/unittest/mock.py | 2 +- Lib/unittest/test/testmock/testhelpers.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 6989dc792eb763..94e344234878d6 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -155,7 +155,7 @@ def _set_signature(mock, original, instance=False): skipfirst = isinstance(original, type) result = _get_signature_object(original, instance, skipfirst) if result is None: - return + return mock func, sig = result def checksig(*args, **kwargs): sig.bind(*args, **kwargs) diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index d2202a7b4132e9..7a6fabb87dd218 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -1,3 +1,5 @@ +import time +import types import unittest from unittest.mock import ( @@ -883,6 +885,19 @@ def test_args_list_contains_call_list(self): self.assertNotIn([call('fish')], mock.call_args_list) + def test_autospec_on_bound_builtin_function(self): + meth = types.MethodType(time.ctime, time.time()) + self.assertIsInstance(meth(), str) + mocked = create_autospec(meth) + + # no signature, so no spec to check against + mocked() + mocked.assert_called_once_with() + mocked.reset_mock() + mocked(4, 5, 6) + mocked.assert_called_once_with(4, 5, 6) + + def test_call_list_str(self): mock = Mock() mock(1, 2) From 989e99a0133b1ebccc61b11a5680ebef318fdfc5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 20 Jul 2017 02:31:27 +0300 Subject: [PATCH 2/3] Add blurb entry --- .../next/Library/2017-07-20-02-29-49.bpo-29403.3RinCV.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2017-07-20-02-29-49.bpo-29403.3RinCV.rst diff --git a/Misc/NEWS.d/next/Library/2017-07-20-02-29-49.bpo-29403.3RinCV.rst b/Misc/NEWS.d/next/Library/2017-07-20-02-29-49.bpo-29403.3RinCV.rst new file mode 100644 index 00000000000000..95bcd1007de05a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-07-20-02-29-49.bpo-29403.3RinCV.rst @@ -0,0 +1,2 @@ +Fix ``unittest.mock``'s autospec to not fail on method-bound builtin +functions. Patch by Aaron Gallagher. From 4f4ed0209b42706737c01f84aa435c702f7884af Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 20 Jul 2017 02:38:59 +0300 Subject: [PATCH 3/3] Move test to correct test case --- Lib/unittest/test/testmock/testhelpers.py | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 7a6fabb87dd218..7919482ae99c7f 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -858,6 +858,19 @@ def check_data_descriptor(mock_attr): check_data_descriptor(foo.desc) + def test_autospec_on_bound_builtin_function(self): + meth = types.MethodType(time.ctime, time.time()) + self.assertIsInstance(meth(), str) + mocked = create_autospec(meth) + + # no signature, so no spec to check against + mocked() + mocked.assert_called_once_with() + mocked.reset_mock() + mocked(4, 5, 6) + mocked.assert_called_once_with(4, 5, 6) + + class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): @@ -885,19 +898,6 @@ def test_args_list_contains_call_list(self): self.assertNotIn([call('fish')], mock.call_args_list) - def test_autospec_on_bound_builtin_function(self): - meth = types.MethodType(time.ctime, time.time()) - self.assertIsInstance(meth(), str) - mocked = create_autospec(meth) - - # no signature, so no spec to check against - mocked() - mocked.assert_called_once_with() - mocked.reset_mock() - mocked(4, 5, 6) - mocked.assert_called_once_with(4, 5, 6) - - def test_call_list_str(self): mock = Mock() mock(1, 2)