diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index 9592de82d..c8eb03101 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -12,6 +12,7 @@ from _pytest.store import StoreKey fault_handler_stderr_key = StoreKey[TextIO]() +fault_handler_originally_enabled_key = StoreKey[bool]() def pytest_addoption(parser: Parser) -> None: @@ -27,6 +28,7 @@ def pytest_configure(config: Config) -> None: stderr_fd_copy = os.dup(get_stderr_fileno()) config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") + config._store[fault_handler_originally_enabled_key] = faulthandler.is_enabled() faulthandler.enable(file=config._store[fault_handler_stderr_key]) @@ -38,10 +40,9 @@ def pytest_unconfigure(config: Config) -> None: if fault_handler_stderr_key in config._store: config._store[fault_handler_stderr_key].close() del config._store[fault_handler_stderr_key] - # Re-enable the faulthandler, attaching it to the default sys.stderr - # so we can see crashes after pytest has finished, usually during - # garbage collection during interpreter shutdown. - faulthandler.enable(file=get_stderr_fileno()) + if config._store.get(fault_handler_originally_enabled_key, False): + # Re-enable the faulthandler if it was originally enabled. + faulthandler.enable(file=get_stderr_fileno()) def get_stderr_fileno() -> int: diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index 411e841a3..5b7911f21 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -19,22 +19,41 @@ def test_enabled(pytester: Pytester) -> None: assert result.ret != 0 -def test_crash_near_exit(pytester: Pytester) -> None: - """Test that fault handler displays crashes that happen even after - pytest is exiting (for example, when the interpreter is shutting down).""" +def setup_crashing_test(pytester: Pytester) -> None: pytester.makepyfile( """ - import faulthandler - import atexit - def test_ok(): - atexit.register(faulthandler._sigabrt) - """ + import faulthandler + import atexit + def test_ok(): + atexit.register(faulthandler._sigabrt) + """ ) - result = pytester.runpytest_subprocess() + + +def test_crash_during_shutdown_captured(pytester: Pytester) -> None: + """ + Re-enable faulthandler if pytest encountered it enabled during configure. + We should be able to then see crashes during interpreter shutdown. + """ + setup_crashing_test(pytester) + args = (sys.executable, "-Xfaulthandler", "-mpytest") + result = pytester.run(*args) result.stderr.fnmatch_lines(["*Fatal Python error*"]) assert result.ret != 0 +def test_crash_during_shutdown_not_captured(pytester: Pytester) -> None: + """ + Check that pytest leaves faulthandler disabled if it was not enabled during configure. + This prevents us from seeing crashes during interpreter shutdown (see #8260). + """ + setup_crashing_test(pytester) + args = (sys.executable, "-mpytest") + result = pytester.run(*args) + result.stderr.no_fnmatch_line("*Fatal Python error*") + assert result.ret != 0 + + def test_disabled(pytester: Pytester) -> None: """Test option to disable fault handler in the command line.""" pytester.makepyfile(