From 25c49ee17c78ed593a8946c721ec9ae28e24f69a Mon Sep 17 00:00:00 2001 From: Lakmal Caldera Date: Fri, 1 Jul 2022 08:43:39 +0200 Subject: [PATCH 1/2] Fix bug unsafe not set when patching Even though mocked objects are created by setting `unsafe=True` the internal attribute `_mock_unsafe` on the actual mocked instances were not properly set. The test case has been updated to capture this logic. --- Lib/test/test_unittest/testmock/testmock.py | 43 ++++++++++++++++----- Lib/unittest/mock.py | 4 ++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index 8a92490137f9a7..56f7273bf4938e 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -2215,7 +2215,7 @@ def test_misspelled_arguments(self): class Foo(): one = 'one' # patch, patch.object and create_autospec need to check for misspelled - # arguments explicitly and throw a RuntimError if found. + # arguments explicitly and throw a RuntimeError if found. with self.assertRaises(RuntimeError): with patch(f'{__name__}.Something.meth', autospect=True): pass with self.assertRaises(RuntimeError): @@ -2243,16 +2243,39 @@ class Foo(): with patch.multiple( f'{__name__}.Something', meth=DEFAULT, set_spec=True): pass - with patch(f'{__name__}.Something.meth', unsafe=True, autospect=True): - pass - with patch.object(Foo, 'one', unsafe=True, autospect=True): pass - with patch(f'{__name__}.Something.meth', unsafe=True, auto_spec=True): - pass - with patch.object(Foo, 'one', unsafe=True, auto_spec=True): pass - with patch(f'{__name__}.Something.meth', unsafe=True, set_spec=True): - pass - with patch.object(Foo, 'one', unsafe=True, set_spec=True): pass + with patch( + f'{__name__}.Something.meth', unsafe=True, autospect=True + ) as patched_object: + self.assertEqual(patched_object._mock_unsafe, True) + + with patch.object( + Foo, 'one', unsafe=True, autospect=True + ) as patched_object: + self.assertEqual(patched_object._mock_unsafe, True) + + with patch( + f'{__name__}.Something.meth', unsafe=True, auto_spec=True + ) as patched_object: + self.assertEqual(patched_object._mock_unsafe, True) + + with patch.object( + Foo, 'one', unsafe=True, auto_spec=True + ) as patched_object: + self.assertEqual(patched_object._mock_unsafe, True) + + with patch( + f'{__name__}.Something.meth', unsafe=True, set_spec=True + ) as patched_object: + self.assertEqual(patched_object._mock_unsafe, True) + + with patch.object( + Foo, 'one', unsafe=True, set_spec=True + ) as patched_object: + self.assertEqual(patched_object._mock_unsafe, True) + m = create_autospec(Foo, set_spec=True, unsafe=True) + self.assertEqual(m._mock_unsafe, True) + with patch.multiple( f'{__name__}.Typos', autospect=True, set_spec=True, auto_spec=True): pass diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index cd46fea5162aba..39fa460d370abf 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1282,6 +1282,8 @@ def __init__( f'Cannot spec attr {attribute!r} as the spec_set ' f'target has already been mocked out. [spec_set={spec_set!r}]') + if unsafe: + kwargs.update({'unsafe': unsafe}) self.getter = getter self.attribute = attribute self.new = new @@ -2652,6 +2654,8 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, _check_spec_arg_typos(kwargs) _kwargs.update(kwargs) + if unsafe: + _kwargs.update({'unsafe': unsafe}) Klass = MagicMock if inspect.isdatadescriptor(spec): From fdc7fc508aad2e917be284582d2f5d0eda5c5378 Mon Sep 17 00:00:00 2001 From: Lakmal Caldera Date: Sat, 16 Jul 2022 10:39:52 +0200 Subject: [PATCH 2/2] gh-94478: Fix bug unsafe not set when patching Updated testcase to actually test the behavior of the unsafe logic instead of just checking if the private attribute `_mock_unsafe` was set. --- Lib/test/test_unittest/testmock/testmock.py | 15 ++++++++------- Lib/unittest/mock.py | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_unittest/testmock/testmock.py b/Lib/test/test_unittest/testmock/testmock.py index 56f7273bf4938e..5434d7a62e4f90 100644 --- a/Lib/test/test_unittest/testmock/testmock.py +++ b/Lib/test/test_unittest/testmock/testmock.py @@ -2214,6 +2214,7 @@ def __init__(self): def test_misspelled_arguments(self): class Foo(): one = 'one' + assret_testing = False # patch, patch.object and create_autospec need to check for misspelled # arguments explicitly and throw a RuntimeError if found. with self.assertRaises(RuntimeError): @@ -2246,35 +2247,35 @@ class Foo(): with patch( f'{__name__}.Something.meth', unsafe=True, autospect=True ) as patched_object: - self.assertEqual(patched_object._mock_unsafe, True) + patched_object.assret_called_once() with patch.object( Foo, 'one', unsafe=True, autospect=True ) as patched_object: - self.assertEqual(patched_object._mock_unsafe, True) + patched_object.assret_called_once() with patch( f'{__name__}.Something.meth', unsafe=True, auto_spec=True ) as patched_object: - self.assertEqual(patched_object._mock_unsafe, True) + patched_object.assret_called_once() with patch.object( Foo, 'one', unsafe=True, auto_spec=True ) as patched_object: - self.assertEqual(patched_object._mock_unsafe, True) + patched_object.assret_called_once() with patch( f'{__name__}.Something.meth', unsafe=True, set_spec=True ) as patched_object: - self.assertEqual(patched_object._mock_unsafe, True) + patched_object.assret_called_once() with patch.object( Foo, 'one', unsafe=True, set_spec=True ) as patched_object: - self.assertEqual(patched_object._mock_unsafe, True) + patched_object.assret_called_once() m = create_autospec(Foo, set_spec=True, unsafe=True) - self.assertEqual(m._mock_unsafe, True) + m.assret_testing = True with patch.multiple( f'{__name__}.Typos', autospect=True, set_spec=True, auto_spec=True): diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 39fa460d370abf..f316c665cba4f8 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1283,7 +1283,7 @@ def __init__( f'target has already been mocked out. [spec_set={spec_set!r}]') if unsafe: - kwargs.update({'unsafe': unsafe}) + kwargs['unsafe'] = unsafe self.getter = getter self.attribute = attribute self.new = new @@ -2655,7 +2655,7 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, _kwargs.update(kwargs) if unsafe: - _kwargs.update({'unsafe': unsafe}) + _kwargs['unsafe'] = unsafe Klass = MagicMock if inspect.isdatadescriptor(spec):