diff --git a/pytestqt/wait_signal.py b/pytestqt/wait_signal.py index 8d70f6e7..cae31eea 100644 --- a/pytestqt/wait_signal.py +++ b/pytestqt/wait_signal.py @@ -48,16 +48,14 @@ def wait(self): self.timeout) def _quit_loop_by_timeout(self): - self._loop.quit() - self._cleanup() + try: + self._cleanup() + finally: + self._loop.quit() def _cleanup(self): if self._timer is not None: - try: - self._timer.timeout.disconnect(self._quit_loop_by_timeout) - except (TypeError, RuntimeError): - # already disconnected by Qt? - pass + _silent_disconnect(self._timer.timeout, self._quit_loop_by_timeout) self._timer.stop() self._timer = None @@ -119,19 +117,18 @@ def _quit_loop_by_signal(self, *args): """ quits the event loop and marks that we finished because of a signal. """ - self.signal_triggered = True - self.args = list(args) - self._loop.quit() - self._cleanup() + try: + self.signal_triggered = True + self.args = list(args) + self._cleanup() + finally: + self._loop.quit() def _cleanup(self): super(SignalBlocker, self)._cleanup() for signal in self._signals: - try: - signal.disconnect(self._quit_loop_by_signal) - except (TypeError, RuntimeError): # pragma: no cover - # already disconnected by Qt? - pass + _silent_disconnect(signal, self._quit_loop_by_signal) + self._signals = [] class MultiSignalBlocker(_AbstractSignalBlocker): @@ -151,6 +148,7 @@ class MultiSignalBlocker(_AbstractSignalBlocker): def __init__(self, timeout=1000, raising=False): super(MultiSignalBlocker, self).__init__(timeout, raising=raising) self._signals = {} + self._slots = {} def _add_signal(self, signal): """ @@ -160,7 +158,9 @@ def _add_signal(self, signal): :param signal: QtCore.Signal """ self._signals[signal] = False - signal.connect(functools.partial(self._signal_emitted, signal)) + slot = functools.partial(self._signal_emitted, signal) + self._slots[signal] = slot + signal.connect(slot) def _signal_emitted(self, signal): """ @@ -171,8 +171,18 @@ def _signal_emitted(self, signal): """ self._signals[signal] = True if all(self._signals.values()): - self.signal_triggered = True - self._loop.quit() + try: + self.signal_triggered = True + self._cleanup() + finally: + self._loop.quit() + + def _cleanup(self): + super(MultiSignalBlocker, self)._cleanup() + for signal, slot in self._slots.items(): + _silent_disconnect(signal, slot) + self._signals.clear() + self._slots.clear() class SignalTimeoutError(Exception): @@ -184,3 +194,12 @@ class SignalTimeoutError(Exception): """ pass + +def _silent_disconnect(signal, slot): + """Disconnects a signal from a slot, ignoring errors. Sometimes + Qt might disconnect a signal automatically for unknown reasons. + """ + try: + signal.disconnect(slot) + except (TypeError, RuntimeError): # pragma: no cover + pass