Repository: pytest-dev/pytest
File: src/_pytest/threadexception.py (Python)

Code before:
        thread_name = "<unknown>" if args.thread is None else args.thread.name
        summary = f"Exception in thread {thread_name}"
        traceback_message = "\n\n" + "".join(
            traceback.format_exception(
                args.exc_type,
                args.exc_value,
                args.exc_traceback,
            )
        )
        tracemalloc_tb = "\n" + tracemalloc_message(args.thread)
        msg = summary + traceback_message + tracemalloc_tb
        cause_msg = summary + tracemalloc_tb

        append(
            ThreadExceptionMeta(
                # Compute these strings here as they might change later
                msg=msg,
                cause_msg=cause_msg,
                exc_value=args.exc_value,
            )
        )
    except BaseException as e:
        append(e)
        # Raising this will cause the exception to be logged twice, once in our
        # collect_thread_exception and once by the unraisablehook plugin
        # (exceptions raised from this hook are 'unraisable')
        # which is fine - this should never happen anyway and if it does
        # it should probably be reported as a pytest bug.
        raise


def pytest_configure(config: Config) -> None:
    prev_hook = threading.excepthook
    deque: collections.deque[ThreadExceptionMeta | BaseException] = collections.deque()
    config.stash[thread_exceptions] = deque
    config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook))
    threading.excepthook = functools.partial(thread_exception_hook, append=deque.append)


@pytest.hookimpl(trylast=True)
def pytest_runtest_setup(item: Item) -> None:
    collect_thread_exception(item.config)


@pytest.hookimpl(trylast=True)
def pytest_runtest_call(item: Item) -> None:
    collect_thread_exception(item.config)


@pytest.hookimpl(trylast=True)
def pytest_runtest_teardown(item: Item) -> None:

Review comment (suggestion):
```suggestion
        # collect_thread_exception and once by sys.excepthook
```

Code after:
        thread_name = "<unknown>" if args.thread is None else args.thread.name
        summary = f"Exception in thread {thread_name}"
        traceback_message = "\n\n" + "".join(
            traceback.format_exception(
                args.exc_type,
                args.exc_value,
                args.exc_traceback,
            )
        )
        tracemalloc_tb = "\n" + tracemalloc_message(args.thread)
        msg = summary + traceback_message + tracemalloc_tb
        cause_msg = summary + tracemalloc_tb

        append(
            ThreadExceptionMeta(
                # Compute these strings here as they might change later
                msg=msg,
                cause_msg=cause_msg,
                exc_value=args.exc_value,
            )
        )
    except BaseException as e:
        append(e)
        # Raising this will cause the exception to be logged twice, once in our
        # collect_thread_exception and once by sys.excepthook
        # which is fine - this should never happen anyway and if it does
        # it should probably be reported as a pytest bug.
        raise


def pytest_configure(config: Config) -> None:
    prev_hook = threading.excepthook
    deque: collections.deque[ThreadExceptionMeta | BaseException] = collections.deque()
    config.stash[thread_exceptions] = deque
    config.add_cleanup(functools.partial(cleanup, config=config, prev_hook=prev_hook))
    threading.excepthook = functools.partial(thread_exception_hook, append=deque.append)


@pytest.hookimpl(trylast=True)
def pytest_runtest_setup(item: Item) -> None:
    collect_thread_exception(item.config)


@pytest.hookimpl(trylast=True)
def pytest_runtest_call(item: Item) -> None:
    collect_thread_exception(item.config)


@pytest.hookimpl(trylast=True)
def pytest_runtest_teardown(item: Item) -> None:
    collect_thread_exception(item.config)