From 155c38c6c4e29caeff7b820881ed7df559e46ffc Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 29 May 2018 21:16:56 -0300 Subject: [PATCH 1/2] Do not fail tests if PYTEST_QT_API is not set --- tests/test_basics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_basics.py b/tests/test_basics.py index 9c2229b1..11acfef0 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -332,7 +332,7 @@ def test_qt_api_ini_config(testdir, monkeypatch, option_api): """ from pytestqt.qt_compat import qt_api - monkeypatch.delenv("PYTEST_QT_API") + monkeypatch.delenv("PYTEST_QT_API", raising=False) testdir.makeini(""" [pytest] From 06eda3b5ae076add17462c2d43765a763aadf638 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 29 May 2018 21:24:55 -0300 Subject: [PATCH 2/2] Properly handle Python 3 chained exceptions in format_captured_exceptions Fix #215 --- CHANGELOG.rst | 10 ++++++++++ pytestqt/exceptions.py | 17 ++++++++++++----- tests/test_exceptions.py | 39 +++++++++++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 118fc8b1..eab0b36e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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 ----- diff --git a/pytestqt/exceptions.py b/pytestqt/exceptions.py index 3b0689f4..75dec33c 100644 --- a/pytestqt/exceptions.py +++ b/pytestqt/exceptions.py @@ -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): diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 3bb5ca9a..c0ace2c8 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -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): @@ -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) @@ -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*') @@ -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): """