msg219331 - (view) |
Author: fumihiko kakuma (kakuma) * |
Date: 2014-05-29 03:35 |
It seems that stopall doesn't work when do start patch.dict to sys.modules.
I show sample scripts the following.
Using stopall test case seems to always refer the first mock foo object.
But using stop it refers a new mock foo object.
$ cat test_sampmod.py
import foo
def myfunc():
print "myfunc foo=%s" % foo
return foo
$ cat test_samp.py
import mock
import sys
import unittest
class SampTestCase(unittest.TestCase):
def setUp(self):
self.foo_mod = mock.Mock()
self.m = mock.patch.dict('sys.modules', {'foo': self.foo_mod})
self.p = self.m.start()
print "foo_mod=%s" % self.foo_mod
__import__('test_sampmod')
self.test_sampmod = sys.modules['test_sampmod']
def tearDown(self):
if len(sys.argv) > 1:
self.m.stop()
print ">>> stop patch"
else:
mock.patch.stopall()
print ">>> stopall patch"
def test_samp1(self):
self.assertEqual(self.foo_mod, self.test_sampmod.myfunc())
def test_samp2(self):
self.assertEqual(self.foo_mod, self.test_sampmod.myfunc())
def test_samp3(self):
self.assertEqual(self.foo_mod, self.test_sampmod.myfunc())
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(SampTestCase))
unittest.TextTestRunner(verbosity=2).run(suite)
$ python test_samp.py stop
test_samp1 (__main__.SampTestCase) ... foo_mod=<Mock id='41504336'>
myfunc foo=<Mock id='41504336'>
>>> stop patch
ok
test_samp2 (__main__.SampTestCase) ... foo_mod=<Mock id='41504464'>
myfunc foo=<Mock id='41504464'>
>>> stop patch
ok
test_samp3 (__main__.SampTestCase) ... foo_mod=<Mock id='41504720'>
myfunc foo=<Mock id='41504720'>
>>> stop patch
ok
----------------------------------------------------------------------
Ran 3 tests in 0.004s
OK
$ python test_samp.py
test_samp1 (__main__.SampTestCase) ... foo_mod=<Mock id='19152464'>
myfunc foo=<Mock id='19152464'>
>>> stopall patch
ok
test_samp2 (__main__.SampTestCase) ... foo_mod=<Mock id='19152592'>
myfunc foo=<Mock id='19152464'>
FAIL
>>> stopall patch
test_samp3 (__main__.SampTestCase) ... foo_mod=<Mock id='19182096'>
myfunc foo=<Mock id='19152464'>
FAIL
>>> stopall patch
======================================================================
FAIL: test_samp2 (__main__.SampTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_samp.py", line 27, in test_samp2
self.assertEqual(self.foo_mod, self.test_sampmod.myfunc())
AssertionError: <Mock id='19152592'> != <Mock id='19152464'>
======================================================================
FAIL: test_samp3 (__main__.SampTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_samp.py", line 30, in test_samp3
self.assertEqual(self.foo_mod, self.test_sampmod.myfunc())
AssertionError: <Mock id='19182096'> != <Mock id='19152464'>
----------------------------------------------------------------------
Ran 3 tests in 0.003s
FAILED (failures=2)
$
|
msg219567 - (view) |
Author: fumihiko kakuma (kakuma) * |
Date: 2014-06-02 10:49 |
Checking mock.py(version 1.0.1) it seems that patch.stopall does not support patch.dict. Does it have any problem to support ptch.dict by stopall.
I made the attached patch file for this. It seems to work well. How about this?
But I don't know why sys.modules refers the first mock object.
$ python test_samp.py
test_samp1 (__main__.SampTestCase) ... foo_mod=<Mock id='140164117280080'>
myfunc foo=<Mock id='140164117280080'>
>>> stopall patch
ok
test_samp2 (__main__.SampTestCase) ... foo_mod=<Mock id='140164117280208'>
myfunc foo=<Mock id='140164117280208'>
>>> stopall patch
ok
test_samp3 (__main__.SampTestCase) ... foo_mod=<Mock id='140164117280464'>
myfunc foo=<Mock id='140164117280464'>
>>> stopall patch
ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
$
|
msg219697 - (view) |
Author: Michael Foord (michael.foord) * |
Date: 2014-06-03 15:15 |
Yep, patch.dict wasn't designed with stopall in mind so it needs adding. Thanks for pointing this out and your fix.
Your patch isn't quite right, those operations shouldn't be inside the try excepts. (And there are no tests.)
|
msg219885 - (view) |
Author: fumihiko kakuma (kakuma) * |
Date: 2014-06-06 14:49 |
Thank you for your reply.
Yes, you are right. The patch was too slapdash. I re-created it and added unit tests.
|
msg219887 - (view) |
Author: Michael Foord (michael.foord) * |
Date: 2014-06-06 14:53 |
That's better - thanks. Another minor tweak needed though. stopall should only stop patches that were started with "start", not those used as context managers or decorators (or they will be stopped twice!).
See how the main patch object only adds to the set of active patches in the start method, not in __enter__ (and removes in stop rather than __exit__).
|
msg219951 - (view) |
Author: fumihiko kakuma (kakuma) * |
Date: 2014-06-07 16:34 |
Hi michael,
Certainly, thank you for your many advices. I attached the new patch file.
|
msg220030 - (view) |
Author: Michael Foord (michael.foord) * |
Date: 2014-06-08 12:05 |
That looks great - thanks! I'll get it committed shortly.
|
msg220082 - (view) |
Author: fumihiko kakuma (kakuma) * |
Date: 2014-06-09 04:47 |
Thank you in advance.
|
msg229291 - (view) |
Author: Colton Leekley-Winslow (lwcolton) * |
Date: 2014-10-14 14:37 |
What's the status on this?
|
msg229292 - (view) |
Author: Michael Foord (michael.foord) * |
Date: 2014-10-14 14:38 |
It just needs committing, I believe Kushal Das has volunteered to do it.
|
msg229294 - (view) |
Author: Kushal Das (kushal.das) * |
Date: 2014-10-14 14:40 |
I will do that. New job is taking time.
|
msg232411 - (view) |
Author: Eric Snow (eric.snow) * |
Date: 2014-12-10 03:35 |
FYI, you probably don't want to be patching out sys.modules. It isn't guaranteed that doing so will have the expected effect on import semantics.
|
msg232435 - (view) |
Author: Michael Foord (michael.foord) * |
Date: 2014-12-10 16:57 |
why not?
|
msg232440 - (view) |
Author: Colton Leekley-Winslow (lwcolton) * |
Date: 2014-12-10 19:11 |
Patching sys.modules is an idea I got from the official documentation
https://docs.python.org/3/library/unittest.mock.html#patch-dict
http://mock.readthedocs.org/en/latest/patch.html#patch-dict
|
msg232458 - (view) |
Author: Eric Snow (eric.snow) * |
Date: 2014-12-11 03:52 |
At least for CPython sys.modules is initially set to the modules dict on the interpreter struct. As of 3.4 the import system itself only cares about sys.modules (I'll have to double check on builtin___import__). However, if I recall correctly at least part of the import C-API interacts directly with the original interpreter copy of the modules dict.
The catch is that setting sys.modules to something else does not also change the dict on the interpreter struct. So they will be out of sync. This may cause problems. In practice I don't think it's a big deal, but there is no guarantee that patching out sys.modules will give you the behavior that you expect.
See also issue #12633.
|
msg232467 - (view) |
Author: Michael Foord (michael.foord) * |
Date: 2014-12-11 10:18 |
Using patch.dict manipulates the contents of sys.modules, it doesn't replace sys.modules.
|
msg232480 - (view) |
Author: Eric Snow (eric.snow) * |
Date: 2014-12-11 14:57 |
Ah. Never mind then. :)
|
msg298203 - (view) |
Author: Antoni Szych (Antoni Szych) |
Date: 2017-07-12 11:44 |
Hi,
It's been 3 years now since this issue was first raised.
We bumped upon this issue while using code like following:
def tearDown():
patch.stopall()
def test123():
p=patch.dict(...)
p.start()
assert False
p.stop()
While `patch.stopall()` is run, it doesn't stop anything. This is because `p.start()` in fact executes `mock._patch_dict._patch_dict()`, which does not execute `self._active_patches.append(self)` (like ordinary `p=patch(...).start()` would).
I could understand that this is just a design choice (which may seem unintuitive for me, but possibly perfectly good for others), however the official documentation [1] states:
>**All** the patchers have start() and stop() methods.
And few lines below [2], we have:
>patch.stopall() Stop all active patches. Only stops patches started with start.
The above is not true for `patch.dict()`, so the documentation is unfortunatelly misleading.
Is there a possibility to fix this in some 3.5 maintenance release (preferably code, not docs :) )?
If anyone else will have the same issue: for now a workaround would be to use `patch.dict()` either as a decorator, or as a context manager (`with patch.dict()`).
[1] https://docs.python.org/3/library/unittest.mock.html#patch-methods-start-and-stop
[2] https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.stopall
|
msg322016 - (view) |
Author: Antoni Szych (Antoni Szych) |
Date: 2018-07-20 15:26 |
Hi,
Any chance of getting this resolved 1 year later? :)
BR
|
msg358323 - (view) |
Author: Karthikeyan Singaravelan (xtreak) * |
Date: 2019-12-13 09:05 |
This sounds like a good idea given the docs and the general agreement in the thread. I would like to target this for 3.9 . Differentiation between the normal patches and the ones from patch.dict is nice but given that docstring indicates LIFO for all active patches storing patch.dict items also in _patch.active_patches ensures we can do LIFO if there is a mixture of normal and dictionary patches started with start method. Adding other maintainers for thoughts.
diff --git Lib/unittest/mock.py Lib/unittest/mock.py
index cd5a2aeb60..96115e06ba 100644
--- Lib/unittest/mock.py
+++ Lib/unittest/mock.py
@@ -1853,8 +1853,21 @@ 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__()
|
msg358324 - (view) |
Author: Mario Corchero (mariocj89) * |
Date: 2019-12-13 09:57 |
Makes total sense, I think we should get this for 3.9.
Not sure I'll backport this, even if a bugfix it might cause unexpected changes (Though I'd be OK with it, given that calling stop twice causes no issue).
I'm happy to push a PR with the proposed change and some tests if you want @xtreak. This is quite a simple fix we can get through in a short time.
|
msg358374 - (view) |
Author: Karthikeyan Singaravelan (xtreak) * |
Date: 2019-12-14 04:40 |
Thanks Mario. I think the last patch attached to the issue is complete with tests and needs to be updated master. Feel free to raise a PR.
|
|
Date |
User |
Action |
Args |
2022-04-11 14:58:04 | admin | set | github: 65799 |
2020-01-24 08:41:44 | cjw296 | set | status: open -> closed resolution: fixed stage: patch review -> resolved |
2019-12-14 14:42:34 | mariocj89 | set | stage: test needed -> patch review pull_requests:
+ pull_request17075 |
2019-12-14 04:40:21 | xtreak | set | messages:
+ msg358374 |
2019-12-13 09:57:20 | mariocj89 | set | messages:
+ msg358324 |
2019-12-13 09:05:59 | xtreak | set | versions:
+ Python 3.9, - Python 3.5 nosy:
+ lisroach, xtreak, cjw296, mariocj89
messages:
+ msg358323
components:
+ Library (Lib), - Tests |
2018-07-21 02:36:14 | ppperry | set | title: mock.patch.stopall doesn't work with patch.dict to sys.modules -> mock.patch.stopall doesn't work with patch.dict |
2018-07-20 15:26:23 | Antoni Szych | set | messages:
+ msg322016 |
2017-07-12 11:44:02 | Antoni Szych | set | nosy:
+ Antoni Szych messages:
+ msg298203
|
2014-12-11 14:57:10 | eric.snow | set | messages:
+ msg232480 |
2014-12-11 10:18:07 | michael.foord | set | messages:
+ msg232467 |
2014-12-11 03:52:13 | eric.snow | set | messages:
+ msg232458 |
2014-12-10 19:11:38 | lwcolton | set | messages:
+ msg232440 |
2014-12-10 16:57:58 | michael.foord | set | messages:
+ msg232435 |
2014-12-10 03:35:58 | eric.snow | set | nosy:
+ eric.snow messages:
+ msg232411
|
2014-10-14 14:40:40 | kushal.das | set | messages:
+ msg229294 |
2014-10-14 14:38:00 | michael.foord | set | messages:
+ msg229292 |
2014-10-14 14:37:15 | lwcolton | set | nosy:
+ lwcolton messages:
+ msg229291
|
2014-06-09 04:47:42 | kakuma | set | messages:
+ msg220082 |
2014-06-08 12:05:59 | michael.foord | set | messages:
+ msg220030 |
2014-06-07 16:34:09 | kakuma | set | files:
+ support_patch_dict_by_stopall.diff
messages:
+ msg219951 |
2014-06-06 14:53:13 | michael.foord | set | messages:
+ msg219887 |
2014-06-06 14:49:01 | kakuma | set | files:
+ support_patch_dict_by_stopall.diff
messages:
+ msg219885 |
2014-06-03 15:15:14 | michael.foord | set | assignee: michael.foord stage: test needed messages:
+ msg219697 versions:
+ Python 3.5, - Python 2.7 |
2014-06-03 14:47:37 | r.david.murray | set | nosy:
+ kushal.das
|
2014-06-02 10:49:55 | kakuma | set | files:
+ add_stopall_patch_dict.patch keywords:
+ patch messages:
+ msg219567
|
2014-05-29 18:16:32 | ned.deily | set | nosy:
+ michael.foord
|
2014-05-29 03:35:28 | kakuma | create | |