[Python-checkins] cpython (3.4): Closes issue 21239. unittest.mock.patch.stopall() did not work
michael.foord
python-checkins at python.org
Tue Apr 15 23:22:30 CEST 2014
http://hg.python.org/cpython/rev/727b7e9c40e3
changeset: 90338:727b7e9c40e3
branch: 3.4
parent: 90333:9fe4d9686162
user: Michael Foord <michael at voidspace.org.uk>
date: Tue Apr 15 17:21:08 2014 -0400
summary:
Closes issue 21239. unittest.mock.patch.stopall() did not work deterministically when the same name was patched multiple times.
files:
Lib/unittest/mock.py | 15 ++++++---
Lib/unittest/test/testmock/testpatch.py | 19 ++++++++++++-
Misc/NEWS | 3 ++
3 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -1050,7 +1050,7 @@
class _patch(object):
attribute_name = None
- _active_patches = set()
+ _active_patches = []
def __init__(
self, getter, attribute, new, spec, create,
@@ -1323,13 +1323,18 @@
def start(self):
"""Activate a patch, returning any created mock."""
result = self.__enter__()
- self._active_patches.add(self)
+ self._active_patches.append(self)
return result
def stop(self):
"""Stop an active patch."""
- self._active_patches.discard(self)
+ try:
+ self._active_patches.remove(self)
+ except ValueError:
+ # If the patch hasn't been started this will fail
+ pass
+
return self.__exit__()
@@ -1622,8 +1627,8 @@
def _patch_stopall():
- """Stop all active patches."""
- for patch in list(_patch._active_patches):
+ """Stop all active patches. LIFO to unroll nested patches."""
+ for patch in reversed(_patch._active_patches):
patch.stop()
diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py
--- a/Lib/unittest/test/testmock/testpatch.py
+++ b/Lib/unittest/test/testmock/testpatch.py
@@ -12,7 +12,7 @@
from unittest.mock import (
NonCallableMock, CallableMixin, patch, sentinel,
MagicMock, Mock, NonCallableMagicMock, patch, _patch,
- DEFAULT, call, _get_target
+ DEFAULT, call, _get_target, _patch
)
@@ -1779,6 +1779,23 @@
patched()
self.assertIs(os.path, path)
+ def test_stopall_lifo(self):
+ stopped = []
+ class thing(object):
+ one = two = three = None
+
+ def get_patch(attribute):
+ class mypatch(_patch):
+ def stop(self):
+ stopped.append(attribute)
+ return super(mypatch, self).stop()
+ return mypatch(lambda: thing, attribute, None, None,
+ False, None, None, None, {})
+ [get_patch(val).start() for val in ("one", "two", "three")]
+ patch.stopall()
+
+ self.assertEqual(stopped, ["three", "two", "one"])
+
if __name__ == '__main__':
unittest.main()
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -37,6 +37,9 @@
Library
-------
+- Issue #21239: patch.stopall() didn't work deterministically when the same
+ name was patched more than once.
+
- Issue #21222: Passing name keyword argument to mock.create_autospec now
works.
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list