From 6a76973be36e9a0cdaef6f3e560f5494e9537371 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 25 Sep 2017 13:17:40 -0300 Subject: [PATCH] Add a test for exception leakage described in #187 Unfortunately at this moment this is still failing because of pytest-dev/pytest#2798, we will need to fix this in pytest first before being able to merge this. --- pytestqt/exceptions.py | 19 +++++++++++-------- tests/test_exceptions.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/pytestqt/exceptions.py b/pytestqt/exceptions.py index 539e24a6..6c822aee 100644 --- a/pytestqt/exceptions.py +++ b/pytestqt/exceptions.py @@ -1,6 +1,8 @@ -from contextlib import contextmanager +import functools import sys import traceback +from contextlib import contextmanager + import pytest @@ -19,6 +21,12 @@ def capture_exceptions(): manager.finish() +def _except_hook(type_, value, tback, exceptions=None): + """Hook functions installed by _QtExceptionCaptureManager""" + exceptions.append((type_, value, tback)) + sys.stderr.write(format_captured_exceptions([(type_, value, tback)])) + + class _QtExceptionCaptureManager(object): """ Manages exception capture context. @@ -32,12 +40,8 @@ def start(self): """Start exception capturing by installing a hook into sys.excepthook that records exceptions received into ``self.exceptions``. """ - def hook(type_, value, tback): - self.exceptions.append((type_, value, tback)) - sys.stderr.write(format_captured_exceptions([(type_, value, tback)])) - self.old_hook = sys.excepthook - sys.excepthook = hook + sys.excepthook = functools.partial(_except_hook, exceptions=self.exceptions) def finish(self): """Stop exception capturing, restoring the original hook. @@ -60,8 +64,7 @@ def fail_if_exceptions_occurred(self, when): prefix = '%s ERROR: ' % when msg = prefix + format_captured_exceptions(exceptions) del exceptions[:] # Don't keep exceptions alive longer. - pytest.fail(msg, - pytrace=False) + pytest.fail(msg, pytrace=False) def format_captured_exceptions(exceptions): diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 3bb5ca9a..ed551a1e 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -314,3 +314,37 @@ def event(self, ev): del exceptions[:] _out, err = capsys.readouterr() assert "raise RuntimeError('event processed')" in err + + +def test_exceptions_dont_leak(testdir): + """ + Ensure exceptions are cleared when an exception occurs and don't leak (#187). + """ + testdir.makepyfile(""" + from pytestqt.qt_compat import qt_api + import gc + import weakref + + class MyWidget(qt_api.QWidget): + + def event(self, ev): + called.append(1) + raise RuntimeError('event processed') + + weak_ref = None + called = [] + + def test_1(qapp): + global weak_ref + w = MyWidget() + weak_ref = weakref.ref(w) + qapp.postEvent(w, qt_api.QEvent(qt_api.QEvent.User)) + qapp.processEvents() + + def test_2(qapp): + assert called + gc.collect() + assert weak_ref() is None + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(['*1 failed, 1 passed*'])