From 57795817bd4713b12c8945559cee9dd8c397b780 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Thu, 11 Apr 2019 22:14:44 +0530 Subject: [PATCH 1/3] Fix isinstance check for Mock objects with spec executed under tracing --- Lib/unittest/mock.py | 2 +- Lib/unittest/test/testmock/testmock.py | 33 +++++++++++++++++++ .../2019-04-11-22-11-24.bpo-36598.hfzDUl.rst | 2 ++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2019-04-11-22-11-24.bpo-36598.hfzDUl.rst diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 8684f1dfa5729f..0e77f0e48943af 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -739,7 +739,7 @@ def __delattr__(self, name): obj = self._mock_children.get(name, _missing) if name in self.__dict__: - super().__delattr__(name) + _safe_super(NonCallableMock, self).__delattr__(name) elif obj is _deleted: raise AttributeError(name) if obj is not _missing: diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 66a5720d1432df..a29e6ae84d15cd 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1847,6 +1847,39 @@ def foo(a, b): self.assertRaises(TypeError, mock.child, 1) self.assertEqual(mock.mock_calls, [call.child(1, 2)]) + def test_isinstance_under_settrace(self): + # bpo-36593 : __class__ is not set for a class that has __class__ + # property defined when it's used with sys.settrace(trace) set. + # Delete the module to force reimport with tracing function set + # restore the old reference later since there are other tests that are + # dependent on unittest.mock.patch. In testpatch.PatchTest + # test_patch_dict_test_prefix and test_patch_test_prefix not restoring + # causes the objects patched to go out of sync + + old_patch = unittest.mock.patch + with patch.dict('sys.modules'): + del sys.modules['unittest.mock'] + + def trace(frame, event, arg): + return trace + + sys.settrace(trace) + self.addCleanup(sys.settrace, None) + + from unittest.mock import ( + Mock, MagicMock, NonCallableMock, NonCallableMagicMock + ) + + mocks = [ + Mock, MagicMock, NonCallableMock, NonCallableMagicMock + ] + + for mock in mocks: + obj = mock(spec=Something) + self.assertIsInstance(obj, Something) + + unittest.mock.patch = old_patch + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2019-04-11-22-11-24.bpo-36598.hfzDUl.rst b/Misc/NEWS.d/next/Library/2019-04-11-22-11-24.bpo-36598.hfzDUl.rst new file mode 100644 index 00000000000000..2a798020913695 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-11-22-11-24.bpo-36598.hfzDUl.rst @@ -0,0 +1,2 @@ +Fix ``isinstance`` check for Mock objects with spec when the code is +executed under tracing. Patch by Karthikeyan Singaravelan. From 959ea938169257277a6799079b3ee669be3a5c48 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Sat, 13 Apr 2019 22:55:05 +0530 Subject: [PATCH 2/3] Use addCleanUp to restore patch object --- Lib/unittest/test/testmock/testmock.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index a29e6ae84d15cd..f7c29c4e389464 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1857,6 +1857,15 @@ def test_isinstance_under_settrace(self): # causes the objects patched to go out of sync old_patch = unittest.mock.patch + + def _cleanup(old_patch): + unittest.mock.patch = old_patch + + # Directly using __setattr__ on unittest.mock causes current imported + # reference to be updated. Use a function so that during cleanup the + # re-imported new reference is updated. + self.addCleanup(_cleanup, old_patch) + with patch.dict('sys.modules'): del sys.modules['unittest.mock'] @@ -1878,8 +1887,6 @@ def trace(frame, event, arg): obj = mock(spec=Something) self.assertIsInstance(obj, Something) - unittest.mock.patch = old_patch - if __name__ == '__main__': unittest.main() From af0ca0cbd312382a1654b45b885e0291b34adec9 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Sat, 13 Apr 2019 23:26:30 +0530 Subject: [PATCH 3/3] Use lambda for setattr --- Lib/unittest/test/testmock/testmock.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index f7c29c4e389464..37f14c37f47d59 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1858,13 +1858,11 @@ def test_isinstance_under_settrace(self): old_patch = unittest.mock.patch - def _cleanup(old_patch): - unittest.mock.patch = old_patch - # Directly using __setattr__ on unittest.mock causes current imported - # reference to be updated. Use a function so that during cleanup the + # reference to be updated. Use a lambda so that during cleanup the # re-imported new reference is updated. - self.addCleanup(_cleanup, old_patch) + self.addCleanup(lambda patch: setattr(unittest.mock, 'patch', patch), + old_patch) with patch.dict('sys.modules'): del sys.modules['unittest.mock']