Skip to content

Commit

Permalink
Properly handle Python 3 chained exceptions in format_captured_except…
Browse files Browse the repository at this point in the history
…ions

Fix pytest-dev#215
  • Loading branch information
nicoddemus committed May 30, 2018
1 parent 155c38c commit 06eda3b
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 11 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
2.4.1
-----

- Properly handle chained exceptions when capturing them inside
virtual methods (`#215`_). Thanks `@fabioz`_ for the report and sample
code with the fix.

.. _#215: https://github.com/pytest-dev/pytest-qt/pull/215


2.4.0
-----

Expand Down
17 changes: 12 additions & 5 deletions pytestqt/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,19 @@ def format_captured_exceptions(exceptions):
Formats exceptions given as (type, value, traceback) into a string
suitable to display as a test failure.
"""
message = 'Qt exceptions in virtual methods:\n'
message += '_' * 80 + '\n'
if sys.version_info.major == 2:
from StringIO import StringIO
else:
from io import StringIO

stream = StringIO()
stream.write('Qt exceptions in virtual methods:\n')
sep = '_' * 80 + '\n'
stream.write(sep)
for (exc_type, value, tback) in exceptions:
message += ''.join(traceback.format_exception(exc_type, value, tback)) + '\n'
message += '_' * 80 + '\n'
return message
traceback.print_exception(exc_type, value, tback, file=stream)
stream.write(sep)
return stream.getvalue()


def _is_exception_capture_enabled(item):
Expand Down
39 changes: 33 additions & 6 deletions tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pytestqt.exceptions import capture_exceptions, format_captured_exceptions
import pytest
import sys

import pytest

from pytestqt.exceptions import capture_exceptions, format_captured_exceptions


@pytest.mark.parametrize('raise_error', [False, True])
def test_catch_exceptions_in_virtual_methods(testdir, raise_error):
Expand All @@ -18,7 +20,11 @@ class Receiver(qt_api.QtCore.QObject):
def event(self, ev):
if {raise_error}:
raise ValueError('mistakes were made')
try:
raise RuntimeError('original error')
except RuntimeError:
raise ValueError('mistakes were made')
return qt_api.QtCore.QObject.event(self, ev)
Expand All @@ -32,11 +38,14 @@ def test_exceptions(qtbot):
'''.format(raise_error=raise_error))
result = testdir.runpytest()
if raise_error:
result.stdout.fnmatch_lines([
'*Qt exceptions in virtual methods:*',
expected_lines = ['*Qt exceptions in virtual methods:*']
if sys.version_info.major == 3:
expected_lines.append('RuntimeError: original error')
expected_lines.extend([
'*ValueError: mistakes were made*',
'*1 failed*',
'*1 failed*'
])
result.stdout.fnmatch_lines(expected_lines)
assert 'pytest.fail' not in '\n'.join(result.outlines)
else:
result.stdout.fnmatch_lines('*1 passed*')
Expand All @@ -55,6 +64,24 @@ def test_format_captured_exceptions():
assert 'ValueError: errors were made' in lines


@pytest.mark.skipif(sys.version_info.major == 2, reason='Python 3 only')
def test_format_captured_exceptions_chained():
try:
try:
raise ValueError('errors were made')
except ValueError:
raise RuntimeError('error handling value error')
except RuntimeError:
exceptions = [sys.exc_info()]

obtained_text = format_captured_exceptions(exceptions)
lines = obtained_text.splitlines()

assert 'Qt exceptions in virtual methods:' in lines
assert 'ValueError: errors were made' in lines
assert 'RuntimeError: error handling value error' in lines


@pytest.mark.parametrize('no_capture_by_marker', [True, False])
def test_no_capture(testdir, no_capture_by_marker):
"""
Expand Down

0 comments on commit 06eda3b

Please sign in to comment.