Repository navigation
ThreadingMock.call_count is not thread safe #142651
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Dec 12, 2025 - addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Dec 12, 2025 https://docs.python.org/3/library/unittest.mock.html says:
call_args_list
This is a list of all the calls made to the mock object in sequence (so the length of the list is the number of times it has been called).Probably we can just use the length of this list as call_count.
I'll open a PR with this solution.
Does Mock document itself to be thread-safe? I'm not averse to bug fixes like this, but unless it's a clearly documented expectation that Mock is thread safe, I fear there may be many more cases like this lurking...
Does Mock document itself to be thread-safe? I'm not averse to bug fixes like this, but unless it's a clearly documented expectation that Mock is thread safe, I fear there may be many more cases like this lurking...
I created #142752 for followup on this
@godlygeek / @chaope - if either of you need this sooner than the next Python release, lemme know and I'll crank the backport machinery to get the backport release on pypi up to date.
I don't need it backported, but thank you. I've worked around this in the test suite where I hit it instead of using
call_count, but I wanted to file the bug report in the hopes of it getting fixed going forward so that people don't keep bumping into it.So, thanks for the fix!
iOS Test failure in unrelated PR:
FAIL: test_call_count_thread_safe (test.test_unittest.testmock.testthreadingmock.TestThreadingMock.test_call_count_thread_safe) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/runner/Library/Developer/CoreSimulator/Devices/AD3CB764-0EC8-4BE8-B517-274B0ADC1CE1/data/Containers/Bundle/Application/1DB972BD-087A-4A50-86C3-C413564FE5EC/iOSTestbed.app/python/lib/python3.15/test/test_unittest/testmock/testthreadingmock.py", line 219, in test_call_count_thread_safe self.assertEqual(m.call_count, LOOPS * THREADS) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: 998 != 1000 ----------------------------------------------------------------------2 remaining items
The problem with the fix in the PR is that the combination of these two lines are not atomic:
I am able to reproduce the issue, adding an artifical delay makes it easier to reproduce. Here is the modified code:
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 34fd49bf56f..f272d3b259b 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1187,7 +1187,10 @@ def _increment_mock_call(self, /, *args, **kwargs): _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) - self.call_count = len(self.call_args_list) + l = len(self.call_args_list) + import time + time.sleep(0.01) # simulate context switch + self.call_count = l # initial stuff for method_calls: do_method_calls = self._mock_parent is not None
cpython on main [$!] via C v17.0.0-clang via 🐍 v3.10.19 took 11s ❯ ./python.exe -m test test_unittest.testmock.testthreadingmock -F -j 40 Using random seed: 2077880501 0:00:00 load avg: 10.85 Run tests in parallel using 40 worker processes 0:00:03 load avg: 11.26 [ 1/1] test_unittest.testmock.testthreadingmock failed (1 failure) test test_unittest.testmock.testthreadingmock failed -- Traceback (most recent call last): File "/Users/kumaraditya/work/cpython/Lib/test/test_unittest/testmock/testthreadingmock.py", line 219, in test_call_count_thread_safe self.assertEqual(m.call_count, LOOPS * THREADS) ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AssertionError: 999 != 1000
I created #142922 to use a lock around the stores so that all three are set atomically.
With that change the test passes consistently.Hello, the recently release backports #142743 and #142744 have caused a slight regression in our tests.
Arguably we shouldn't be setting
call_countdirectly, but I thought it was worth pointing out as others may also be doing the wrong thing here. One could imagine a setter forcall_countto raise an appropriate error or warning as desired.This PR mostly outlines the issue and change we will need to make to adapt to Python
3.13.12and3.14.3Reacted by Elliott Sales de Andrade- added 2 commits that reference this issue
on Feb 9, 2026 - added a commit that references this issue
on Feb 13, 2026 - added a commit that references this issue
on Feb 13, 2026 - added a commit that references this issue
on Feb 15, 2026 - added a commit that references this issue
on Mar 10, 2026
Bug report
Bug description:
Running this test program with (plain old, non-free-threaded) Python 3.14 fails:
Results are something like:
This is essentially the same bug as #122957, but that was closed with just a fix to the test suite. This is a bug report that the version of MagicMock for multithreading tests is not thread-safe and so the obvious way of writing those multithreading tests may not work.
CPython versions tested on:
3.14
Operating systems tested on:
Linux
Linked PRs
Mock.call_countthread-safe (GH-142656) #142743Mock.call_countthread-safe (GH-142656) #142744NonCallableMock._lockfor thread safety of call_count #142922NonCallableMock._lockfor thread safety ofcall_count(GH-142922) #145739NonCallableMock._lockfor thread safety ofcall_count(GH-142922) #145740