[3.14] gh-79366: Fix a race condition when removing a logging handler (GH-154528) (GH-155077)
serhiy-storchaka <[email protected]> Sun, 02 Aug 2026 06:43:11 -0400 (EDT)
| Newsgroups | gmane.comp.python.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/python/cpython/commit/eae50e7a04f3a0cf219adc97c625f17b6e2e0d56 commit: eae50e7a04f3a0cf219adc97c625f17b6e2e0d56 branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-08-02T10:42:57Z summary: [3.14] gh-79366: Fix a race condition when removing a logging handler (GH-154528) (GH-155077) removeHandler() mutated the handler list in place, so if a handler was removed while callHandlers() was iterating the same list, the following handlers could be skipped. Replace the list instead of mutating it. (cherry picked from commit 083e0388b0a39d379ab0e7370e8dce7574a87171) Co-authored-by: Serhiy Storchaka <[email protected]> Co-authored-by: Ben Spiller <[email protected]> Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> files: A Misc/NEWS.d/next/Library/2026-07-23-06-53-01.gh-issue-79366.3gMT7I.rst M Lib/logging/__init__.py M Lib/test/test_logging.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 9005f1ef865c909..6e7606269fb7133 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1694,7 +1694,11 @@ def removeHandler(self, hdlr): """ with _lock: if hdlr in self.handlers: - self.handlers.remove(hdlr) + # Replace the list instead of mutating it in place, so that + # callHandlers() can iterate it without a lock (gh-79366). + handlers = self.handlers.copy() + handlers.remove(hdlr) + self.handlers = handlers def hasHandlers(self): """ diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 0c27c03ac850610..74b740510f3c791 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -799,6 +799,23 @@ def lock_holder_thread_fn(): support.wait_process(pid, exitcode=0) + def test_remove_handler_while_emitting(self): + # Removing a handler while callHandlers() iterates over the handlers + # should not cause the following handlers to be skipped (gh-79366). + logger = logging.Logger('test_remove_handler_while_emitting') + calls = [] + class RemovingHandler(logging.Handler): + def emit(self, record): + calls.append('removing') + logger.removeHandler(self) + class CountingHandler(logging.Handler): + def emit(self, record): + calls.append('counting') + logger.addHandler(RemovingHandler()) + logger.addHandler(CountingHandler()) + logger.error('spam') + self.assertEqual(calls, ['removing', 'counting']) + class BadStream(object): def write(self, data): diff --git a/Misc/NEWS.d/next/Library/2026-07-23-06-53-01.gh-issue-79366.3gMT7I.rst b/Misc/NEWS.d/next/Library/2026-07-23-06-53-01.gh-issue-79366.3gMT7I.rst new file mode 100644 index 000000000000000..ecb3c3ad92cb2be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-23-06-53-01.gh-issue-79366.3gMT7I.rst @@ -0,0 +1,3 @@ +Fixed a race condition in :mod:`logging`: +if a handler was removed while a record was being emitted, +the following handlers of the same logger could be skipped. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]