gh-142651: make Mock.call_count thread-safe (#142656)

This commit is contained in:
chaope 2025-12-15 06:43:15 -05:00 committed by GitHub
parent 38ad651b67
commit 850f95f6f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 2 deletions

View file

@ -1,8 +1,10 @@
import sys
import time
import unittest
import threading
import concurrent.futures
from test.support import threading_helper
from test.support import setswitchinterval, threading_helper
from unittest.mock import patch, ThreadingMock
@ -196,6 +198,26 @@ def test_reset_mock_resets_wait(self):
m.wait_until_any_call_with()
m.assert_called_once()
def test_call_count_thread_safe(self):
# See https://github.com/python/cpython/issues/142651.
m = ThreadingMock()
LOOPS = 100
THREADS = 10
def test_function():
for _ in range(LOOPS):
m()
oldswitchinterval = sys.getswitchinterval()
setswitchinterval(1e-6)
try:
threads = [threading.Thread(target=test_function) for _ in range(THREADS)]
with threading_helper.start_threads(threads):
pass
finally:
sys.setswitchinterval(oldswitchinterval)
self.assertEqual(m.call_count, LOOPS * THREADS)
if __name__ == "__main__":
unittest.main()

View file

@ -1180,7 +1180,6 @@ def _mock_call(self, /, *args, **kwargs):
def _increment_mock_call(self, /, *args, **kwargs):
self.called = True
self.call_count += 1
# handle call_args
# needs to be set here so assertions on call arguments pass before
@ -1188,6 +1187,7 @@ 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)
# initial stuff for method_calls:
do_method_calls = self._mock_parent is not None

View file

@ -0,0 +1,3 @@
:mod:`unittest.mock`: fix a thread safety issue where :attr:`Mock.call_count
<unittest.mock.Mock.call_count>` may return inaccurate values when the mock
is called concurrently from multiple threads.