From 80da4274398617274ff9e576339184ce77893d9b Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 5 Dec 2024 12:52:23 +0000 Subject: [PATCH] Remove thread join from threadexception plugin (#13027) As per review comment: https://github.com/pytest-dev/pytest/pull/13016#discussion_r1870340465. Closes #13028. --------- Co-authored-by: Bruno Oliveira --- changelog/13016.improvement.rst | 1 - src/_pytest/threadexception.py | 21 +++------------------ testing/test_threadexception.py | 3 +-- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/changelog/13016.improvement.rst b/changelog/13016.improvement.rst index 22642fc6f4..634672ab69 100644 --- a/changelog/13016.improvement.rst +++ b/changelog/13016.improvement.rst @@ -1,7 +1,6 @@ A number of :ref:`threadexception ` enhancements: * Set the excepthook as early as possible and unset it as late as possible, to collect the most possible number of unhandled exceptions from threads. -* join threads for 1 second just before unsetting the excepthook, to collect any straggling exceptions * Collect multiple thread exceptions per test phase. * Report the :mod:`tracemalloc` allocation traceback (if available). * Avoid using a generator based hook to allow handling :class:`StopIteration` in test failures. diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index 38665d2731..eb57783be2 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -5,7 +5,6 @@ import functools import sys import threading -import time import traceback from typing import NamedTuple from typing import TYPE_CHECKING @@ -25,22 +24,6 @@ from exceptiongroup import ExceptionGroup -def join_threads() -> None: - start = time.monotonic() - current_thread = threading.current_thread() - # This function is executed right at the end of the pytest run, just - # before we return an exit code, which is where the interpreter joins - # any remaining non-daemonic threads anyway, so it's ok to join all the - # threads. However there might be threads that depend on some shutdown - # signal that happens after pytest finishes, so we want to limit the - # join time somewhat. A one second timeout seems reasonable. - timeout = 1 - for thread in threading.enumerate(): - if thread is not current_thread and not thread.daemon: - # TODO: raise an error/warning if there's dangling threads. - thread.join(timeout - (time.monotonic() - start)) - - class ThreadExceptionMeta(NamedTuple): msg: str cause_msg: str @@ -96,7 +79,9 @@ def cleanup( ) -> None: try: try: - join_threads() + # We don't join threads here, so exceptions raised from any + # threads still running by the time _threading_atexits joins them + # do not get captured (see #13027). collect_thread_exception(config) finally: threading.excepthook = prev_hook diff --git a/testing/test_threadexception.py b/testing/test_threadexception.py index 6ee93ab9c2..5dad07b8b8 100644 --- a/testing/test_threadexception.py +++ b/testing/test_threadexception.py @@ -195,17 +195,16 @@ def test_2(): pass def test_unhandled_thread_exception_after_teardown(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" - import time import threading import pytest def thread(): def oops(): - time.sleep(0.5) raise ValueError("Oops") t = threading.Thread(target=oops, name="MyThread") t.start() + t.join() def test_it(request): request.config.add_cleanup(thread)