From 9ba1821e9121c3ee49a68b4863cd7ee50de4a5a5 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sun, 17 Jan 2021 19:23:57 +0100 Subject: [PATCH] Fix faulthandler for Twisted Logger when used with "--capture=no" The Twisted Logger will return an invalid file descriptor since it is not backed by an FD. So, let's also forward this to the same code path as with `pytest-xdist`. --- AUTHORS | 1 + changelog/8249.bugfix.rst | 1 + src/_pytest/faulthandler.py | 7 ++++++- testing/test_faulthandler.py | 25 +++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog/8249.bugfix.rst diff --git a/AUTHORS b/AUTHORS index e75baa8cd..0c721a496 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Anders Hovmöller Andras Mitzki Andras Tim Andrea Cimatoribus +Andreas Motl Andreas Zeidler Andrey Paramonov Andrzej Klajnert diff --git a/changelog/8249.bugfix.rst b/changelog/8249.bugfix.rst new file mode 100644 index 000000000..aa084c757 --- /dev/null +++ b/changelog/8249.bugfix.rst @@ -0,0 +1 @@ +Fix the ``faulthandler`` plugin for occasions when running with ``twisted.logger`` and using ``pytest --capture=no``. diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index d0cc0430c..ff673b5b1 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -69,7 +69,12 @@ class FaultHandlerHooks: @staticmethod def _get_stderr_fileno(): try: - return sys.stderr.fileno() + fileno = sys.stderr.fileno() + # The Twisted Logger will return an invalid file descriptor since it is not backed + # by an FD. So, let's also forward this to the same code path as with pytest-xdist. + if fileno == -1: + raise AttributeError() + return fileno except (AttributeError, io.UnsupportedOperation): # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index caf39813c..370084c12 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -1,3 +1,4 @@ +import io import sys import pytest @@ -135,3 +136,27 @@ def test_already_initialized(faulthandler_timeout: int, pytester: Pytester) -> N result.stdout.no_fnmatch_line(warning_line) result.stdout.fnmatch_lines("*1 passed*") assert result.ret == 0 + + +def test_get_stderr_fileno_invalid_fd() -> None: + """Test for faulthandler being able to handle invalid file descriptors for stderr (#8249).""" + from _pytest.faulthandler import FaultHandlerHooks + + class StdErrWrapper(io.StringIO): + """ + Mimic ``twisted.logger.LoggingFile`` to simulate returning an invalid file descriptor. + + https://github.com/twisted/twisted/blob/twisted-20.3.0/src/twisted/logger/_io.py#L132-L139 + """ + + def fileno(self): + return -1 + + wrapper = StdErrWrapper() + + with pytest.MonkeyPatch.context() as mp: + mp.setattr("sys.stderr", wrapper) + + # Even when the stderr wrapper signals an invalid file descriptor, + # ``_get_stderr_fileno()`` should return the real one. + assert FaultHandlerHooks._get_stderr_fileno() == 2