From 3ce31b6370fcaa02a63f09c86a2859800d15984a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 22 Jun 2019 19:22:43 -0300 Subject: [PATCH] Change pytest-faulthandler for simplification * The --no-faulthandler option is not necessary given that we can use `-p no:faulthandler`. * The `--faulthandler-timeout` command-line option has become an ini option, for the reasons described in https://github.com/pytest-dev/pytest-faulthandler/issues/34 and users can still set it from the command-line. Fix pytest-dev/pytest-faulthandler#34 --- doc/en/reference.rst | 17 ++++++++++ doc/en/usage.rst | 20 +++++++++-- src/_pytest/faulthandler.py | 64 ++++++++++++++---------------------- testing/test_faulthandler.py | 12 ++++--- 4 files changed, 66 insertions(+), 47 deletions(-) diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 0b168eb54..7ad69d4a8 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -1076,6 +1076,23 @@ passed multiple times. The expected format is ``name=value``. For example:: for more details. +.. confval:: faulthandler_timeout + + Dumps the tracebacks of all threads if a test takes longer than ``X`` seconds to run (including + fixture setup and teardown). Implemented using the `faulthandler.dump_traceback_later`_ function, + so all caveats there apply. + + .. code-block:: ini + + # content of pytest.ini + [pytest] + faulthandler_timeout=5 + + For more information please refer to :ref:`faulthandler`. + +.. _`faulthandler.dump_traceback_later`: https://docs.python.org/3/library/faulthandler.html#faulthandler.dump_traceback_later + + .. confval:: filterwarnings diff --git a/doc/en/usage.rst b/doc/en/usage.rst index c1332706f..a8acc3551 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -428,11 +428,25 @@ Fault Handler The `faulthandler `__ standard module can be used to dump Python tracebacks on a segfault or after a timeout. -The module is automatically enabled for pytest runs, unless the ``--no-faulthandler`` is given +The module is automatically enabled for pytest runs, unless the ``-p no:faulthandler`` is given on the command-line. -Also the ``--faulthandler-timeout=X`` can be used to dump the traceback of all threads if a test -takes longer than ``X`` seconds to finish (not available on Windows). +Also the :confval:`faulthandler_timeout=X` configuration option can be used +to dump the traceback of all threads if a test takes longer than ``X`` +seconds to finish (not available on Windows). + +.. note:: + + This functionality has been integrated from the external + `pytest-faulthandler `__ plugin, with two + small differences: + + * To disable it, use ``-p no:faulthandler`` instead of ``--no-faulthandler``: the former + can be used with any plugin, so it saves one option. + + * The ``--faulthandler-timeout`` command-line option has become the + :confval:`faulthandler_timeout` configuration option. It can still be configured from + the command-line using ``-o faulthandler_timeout=X``. Creating JUnitXML format files diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index 48fe0f218..068bec528 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -6,38 +6,24 @@ import pytest def pytest_addoption(parser): - group = parser.getgroup("terminal reporting") - group.addoption( - "--no-faulthandler", - action="store_false", - dest="fault_handler", - default=True, - help="Disable faulthandler module.", - ) - - group.addoption( - "--faulthandler-timeout", - type=float, - dest="fault_handler_timeout", - metavar="TIMEOUT", - default=0.0, - help="Dump the traceback of all threads if a test takes " + help = ( + "Dump the traceback of all threads if a test takes " "more than TIMEOUT seconds to finish.\n" - "Not available on Windows.", + "Not available on Windows." ) + parser.addini("faulthandler_timeout", help, default=0.0) def pytest_configure(config): - if config.getoption("fault_handler"): - import faulthandler + import faulthandler - # avoid trying to dup sys.stderr if faulthandler is already enabled - if faulthandler.is_enabled(): - return + # avoid trying to dup sys.stderr if faulthandler is already enabled + if faulthandler.is_enabled(): + return - stderr_fd_copy = os.dup(_get_stderr_fileno()) - config.fault_handler_stderr = os.fdopen(stderr_fd_copy, "w") - faulthandler.enable(file=config.fault_handler_stderr) + stderr_fd_copy = os.dup(_get_stderr_fileno()) + config.fault_handler_stderr = os.fdopen(stderr_fd_copy, "w") + faulthandler.enable(file=config.fault_handler_stderr) def _get_stderr_fileno(): @@ -51,26 +37,24 @@ def _get_stderr_fileno(): def pytest_unconfigure(config): - if config.getoption("fault_handler"): - import faulthandler + import faulthandler - faulthandler.disable() - # close our dup file installed during pytest_configure - f = getattr(config, "fault_handler_stderr", None) - if f is not None: - # 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 - config.fault_handler_stderr.close() - del config.fault_handler_stderr - faulthandler.enable(file=_get_stderr_fileno()) + faulthandler.disable() + # close our dup file installed during pytest_configure + f = getattr(config, "fault_handler_stderr", None) + if f is not None: + # 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 + config.fault_handler_stderr.close() + del config.fault_handler_stderr + faulthandler.enable(file=_get_stderr_fileno()) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_protocol(item): - enabled = item.config.getoption("fault_handler") - timeout = item.config.getoption("fault_handler_timeout") - if enabled and timeout > 0: + timeout = float(item.config.getini("faulthandler_timeout") or 0.0) + if timeout > 0: import faulthandler stderr = item.config.fault_handler_stderr diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index d1f2e8b9a..a0cf1d8c1 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -44,7 +44,7 @@ def test_disabled(testdir): assert not faulthandler.is_enabled() """ ) - result = testdir.runpytest_subprocess("--no-faulthandler") + result = testdir.runpytest_subprocess("-p", "no:faulthandler") result.stdout.fnmatch_lines(["*1 passed*"]) assert result.ret == 0 @@ -61,9 +61,13 @@ def test_timeout(testdir, enabled): time.sleep(2.0) """ ) - args = ["--faulthandler-timeout=1"] - if not enabled: - args.append("--no-faulthandler") + testdir.makeini( + """ + [pytest] + faulthandler_timeout = 1 + """ + ) + args = ["-p", "no:faulthandler"] if not enabled else [] result = testdir.runpytest_subprocess(*args) tb_output = "most recent call first"