Skip to content

Commit

Permalink
Add a test for exception leakage described in pytest-dev#187
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nicoddemus committed Sep 25, 2017
1 parent 3b60118 commit 6a76973
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
19 changes: 11 additions & 8 deletions pytestqt/exceptions.py
@@ -1,6 +1,8 @@
from contextlib import contextmanager
import functools
import sys
import traceback
from contextlib import contextmanager

import pytest


Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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):
Expand Down
34 changes: 34 additions & 0 deletions tests/test_exceptions.py
Expand Up @@ -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*'])

0 comments on commit 6a76973

Please sign in to comment.