From 9726fafa98885fd3bec3972da3bda5f5ce91afdf Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 21 Mar 2015 09:26:35 +0100 Subject: [PATCH 1/3] allow postmortem debugging on failed test --- _pytest/runner.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/_pytest/runner.py b/_pytest/runner.py index 2932f14c3..8accd3b0c 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -86,7 +86,17 @@ def pytest_runtest_setup(item): item.session._setupstate.prepare(item) def pytest_runtest_call(item): - item.runtest() + try: + item.runtest() + except Exception: + # Store trace info to allow postmortem debugging + type, value, tb = sys.exc_info() + tb = tb.tb_next # Skip *this* frame + sys.last_type = type + sys.last_value = value + sys.last_traceback = tb + del tb # Get rid of it in this namespace + raise def pytest_runtest_teardown(item, nextitem): item.session._setupstate.teardown_exact(item, nextitem) From 0fc75c962253ed1b712d8ca144a7f6ecc6df1b49 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 21 Mar 2015 17:06:24 +0100 Subject: [PATCH 2/3] Storing sys.last_traceback: test, docs and changelog --- CHANGELOG | 4 ++++ doc/en/usage.txt | 3 +++ testing/test_runner.py | 15 +++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 20a86f0f1..5fed0265a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ 2.7.0.dev (compared to 2.6.4) ----------------------------- +- On failure, the ``sys.last_value``, ``sys.last_type`` and + ``sys.last_traceback`` are set, so that a user can inspect the error + via postmortem debugging. + - fix issue616: conftest.py files and their contained fixutres are now properly considered for visibility, independently from the exact current working directory and test arguments that are used. diff --git a/doc/en/usage.txt b/doc/en/usage.txt index b03b9b9d6..4931e7086 100644 --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -87,6 +87,9 @@ failure situation:: py.test -x --pdb # drop to PDB on first failure, then end test session py.test --pdb --maxfail=3 # drop to PDB for first three failures +Note that on any failure the exception information is stored on +``sys.last_traceback``. In interactive use, this allows one to drop +into postmortem debugging with any debug tool. Setting a breakpoint / aka ``set_trace()`` ---------------------------------------------------- diff --git a/testing/test_runner.py b/testing/test_runner.py index 9a13a6b47..fee1be09b 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -525,3 +525,18 @@ def test_makereport_getsource(testdir): result = testdir.runpytest() assert 'INTERNALERROR' not in result.stdout.str() result.stdout.fnmatch_lines(['*else: assert False*']) + + +def test_store_except_info_on_eror(testdir): + # Simulate item that raises a specific exception + class ItemThatRaises: + def runtest(self): + raise IndexError('TEST') + try: + runner.pytest_runtest_call(ItemThatRaises()) + except IndexError: + pass + # Check that exception info is stored on sys + assert sys.last_type is IndexError + assert sys.last_value.args[0] == 'TEST' + assert sys.last_traceback From a9b7de8bf0236de9f421c603f1e2e201777735d0 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 21 Mar 2015 17:26:23 +0100 Subject: [PATCH 3/3] address reviewer comments --- doc/en/usage.txt | 12 ++++++++++-- testing/test_runner.py | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/en/usage.txt b/doc/en/usage.txt index 4931e7086..e774ebef6 100644 --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -88,8 +88,16 @@ failure situation:: py.test --pdb --maxfail=3 # drop to PDB for first three failures Note that on any failure the exception information is stored on -``sys.last_traceback``. In interactive use, this allows one to drop -into postmortem debugging with any debug tool. +``sys.last_value``, ``sys.last_type`` and ``sys.last_traceback``. In +interactive use, this allows one to drop into postmortem debugging with +any debug tool. One can also manually access the exception information, +for example:: + + >> import sys + >> sys.last_traceback.tb_lineno + 42 + >> sys.last_value + AssertionError('assert result == "ok"',) Setting a breakpoint / aka ``set_trace()`` ---------------------------------------------------- diff --git a/testing/test_runner.py b/testing/test_runner.py index fee1be09b..e62aea9f7 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -527,7 +527,10 @@ def test_makereport_getsource(testdir): result.stdout.fnmatch_lines(['*else: assert False*']) -def test_store_except_info_on_eror(testdir): +def test_store_except_info_on_eror(): + """ Test that upon test failure, the exception info is stored on + sys.last_traceback and friends. + """ # Simulate item that raises a specific exception class ItemThatRaises: def runtest(self):