From e131c9720d087c0c4988bd2a5c62020feb9d1d77 Mon Sep 17 00:00:00 2001 From: Mario Corchero Date: Fri, 24 Jan 2020 08:38:33 +0000 Subject: [PATCH] Fix `mock.patch.dict` to be stopped with `mock.patch.stopall` (#17606) As the function was not registering in the active patches, the mocks started by `mock.patch.dict` were not being stopped when `mock.patch.stopall` was being called. --- Lib/unittest/mock.py | 19 ++++++- Lib/unittest/test/testmock/testpatch.py | 50 +++++++++++++++++++ .../2019-12-14-14-38-40.bpo-21600.kC4Cgh.rst | 2 + 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-12-14-14-38-40.bpo-21600.kC4Cgh.rst diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 5622917dc37..3fafe594c59 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1851,8 +1851,23 @@ class _patch_dict(object): self._unpatch_dict() return False - start = __enter__ - stop = __exit__ + + def start(self): + """Activate a patch, returning any created mock.""" + result = self.__enter__() + _patch._active_patches.append(self) + return result + + + def stop(self): + """Stop an active patch.""" + try: + _patch._active_patches.remove(self) + except ValueError: + # If the patch hasn't been started this will fail + pass + + return self.__exit__() def _clear_dict(in_dict): diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index dc4ccdbae24..438dfd8cfbc 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -1808,6 +1808,56 @@ class PatchTest(unittest.TestCase): self.assertEqual(stopped, ["three", "two", "one"]) + def test_patch_dict_stopall(self): + dic1 = {} + dic2 = {1: 'a'} + dic3 = {1: 'A', 2: 'B'} + origdic1 = dic1.copy() + origdic2 = dic2.copy() + origdic3 = dic3.copy() + patch.dict(dic1, {1: 'I', 2: 'II'}).start() + patch.dict(dic2, {2: 'b'}).start() + + @patch.dict(dic3) + def patched(): + del dic3[1] + + patched() + self.assertNotEqual(dic1, origdic1) + self.assertNotEqual(dic2, origdic2) + self.assertEqual(dic3, origdic3) + + patch.stopall() + + self.assertEqual(dic1, origdic1) + self.assertEqual(dic2, origdic2) + self.assertEqual(dic3, origdic3) + + + def test_patch_and_patch_dict_stopall(self): + original_unlink = os.unlink + original_chdir = os.chdir + dic1 = {} + dic2 = {1: 'A', 2: 'B'} + origdic1 = dic1.copy() + origdic2 = dic2.copy() + + patch('os.unlink', something).start() + patch('os.chdir', something_else).start() + patch.dict(dic1, {1: 'I', 2: 'II'}).start() + patch.dict(dic2).start() + del dic2[1] + + self.assertIsNot(os.unlink, original_unlink) + self.assertIsNot(os.chdir, original_chdir) + self.assertNotEqual(dic1, origdic1) + self.assertNotEqual(dic2, origdic2) + patch.stopall() + self.assertIs(os.unlink, original_unlink) + self.assertIs(os.chdir, original_chdir) + self.assertEqual(dic1, origdic1) + self.assertEqual(dic2, origdic2) + def test_special_attrs(self): def foo(x=0): diff --git a/Misc/NEWS.d/next/Library/2019-12-14-14-38-40.bpo-21600.kC4Cgh.rst b/Misc/NEWS.d/next/Library/2019-12-14-14-38-40.bpo-21600.kC4Cgh.rst new file mode 100644 index 00000000000..0f726393106 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-12-14-14-38-40.bpo-21600.kC4Cgh.rst @@ -0,0 +1,2 @@ +Fix :func:`mock.patch.stopall` to stop active patches that were created with +:func:`mock.patch.dict`.