Skip to content

Commit 187eab3

Browse files
committed
Merge remote-tracking branch 'origin/master' into waitsignal-multiple
Conflicts: pytestqt/_tests/test_wait_signal.py pytestqt/plugin.py
2 parents ad507a4 + 071011e commit 187eab3

File tree

7 files changed

+106
-82
lines changed

7 files changed

+106
-82
lines changed

.project

Lines changed: 0 additions & 17 deletions
This file was deleted.

.pydevproject

Lines changed: 0 additions & 8 deletions
This file was deleted.

docs/conf.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,7 @@
9292

9393
# The theme to use for HTML and HTML Help pages. See the documentation for
9494
# a list of builtin themes.
95-
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
96-
if on_rtd:
97-
html_theme = 'default'
98-
else:
99-
#html_theme = 'agogo'
100-
html_theme = 'sphinxdoc'
101-
#html_theme = 'scrolls'
102-
#html_theme = 'nature'
103-
#html_theme = 'haiku'
104-
#html_theme = 'pyramid'
95+
html_theme = 'sphinx_rtd_theme'
10596

10697
# Theme options are theme-specific and customize the look and feel of a theme
10798
# further. For a list of options available for each theme, see the

docs/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ ensuring the results are correct::
163163
assert_application_results(app)
164164

165165

166+
.. versionadded:: 1.4
167+
168+
Additionally, you can pass ``raising=True`` to raise a
169+
:class:`SignalTimeoutError` if the timeout is reached.
170+
166171
Exceptions in virtual methods
167172
=============================
168173

@@ -238,6 +243,8 @@ SignalBlocker
238243

239244
.. autoclass:: SignalBlocker
240245

246+
.. autoclass:: SignalTimeoutError
247+
241248
Versioning
242249
==========
243250

pytestqt/_tests/test_wait_signal.py

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,40 +19,55 @@ def test_signal_blocker_exception(qtbot):
1919
qtbot.waitSignal(None, None).wait()
2020

2121

22-
def explicit_wait(qtbot, signal, timeout, multiple):
22+
def explicit_wait(qtbot, signal, timeout, multiple, raising, raises):
2323
"""
2424
Explicit wait for the signal using blocker API.
2525
"""
2626
func = qtbot.waitSignals if multiple else qtbot.waitSignal
27-
blocker = func(signal, timeout)
27+
blocker = func(signal, timeout, raising=raising)
2828
assert not blocker.signal_triggered
29-
blocker.wait()
29+
if raises:
30+
with pytest.raises(qtbot.SignalTimeoutError):
31+
blocker.wait()
32+
else:
33+
blocker.wait()
3034
return blocker
3135

3236

33-
def context_manager_wait(qtbot, signal, timeout, multiple):
37+
def context_manager_wait(qtbot, signal, timeout, multiple, raising, raises):
3438
"""
3539
Waiting for signal using context manager API.
3640
"""
37-
func = qtbot.waitSignals if multiple else qtbot.waitSignal
38-
with func(signal, timeout) as blocker:
39-
pass
41+
func = qtbot.waitSignals if multiple else qtbot.waitSignal
42+
if raises:
43+
with pytest.raises(qtbot.SignalTimeoutError):
44+
with func(signal, timeout, raising=raising) as blocker:
45+
pass
46+
else:
47+
with func(signal, timeout, raising=raising) as blocker:
48+
pass
4049
return blocker
4150

4251

4352
@pytest.mark.parametrize(
44-
('wait_function', 'emit_delay', 'timeout', 'expected_signal_triggered'),
53+
('wait_function', 'emit_delay', 'timeout', 'expected_signal_triggered',
54+
'raising'),
4555
[
46-
(explicit_wait, 500, 2000, True),
47-
(explicit_wait, 500, None, True),
48-
(context_manager_wait, 500, 2000, True),
49-
(context_manager_wait, 500, None, True),
50-
(explicit_wait, 2000, 500, False),
51-
(context_manager_wait, 2000, 500, False),
52-
] * 2 # Running all tests twice to catch a QTimer segfault, see #42/#43.
56+
(explicit_wait, 500, 2000, True, False),
57+
(explicit_wait, 500, None, True, False),
58+
(context_manager_wait, 500, 2000, True, False),
59+
(context_manager_wait, 500, None, True, False),
60+
(explicit_wait, 2000, 500, False, False),
61+
(context_manager_wait, 2000, 500, False, False),
62+
63+
(explicit_wait, 2000, 500, False, True),
64+
(context_manager_wait, 2000, 500, False, True),
65+
(explicit_wait, 2000, 500, False, True),
66+
(context_manager_wait, 2000, 500, False, True),
67+
]
5368
)
5469
def test_signal_triggered(qtbot, wait_function, emit_delay, timeout,
55-
expected_signal_triggered):
70+
expected_signal_triggered, raising):
5671
"""
5772
Testing for a signal in different conditions, ensuring we are obtaining
5873
the expected results.
@@ -64,9 +79,11 @@ def test_signal_triggered(qtbot, wait_function, emit_delay, timeout,
6479
timer.timeout.connect(signaller.signal.emit)
6580
timer.start(emit_delay)
6681

67-
# block signal until either signal is emitted or timeout is reached
6882
start_time = time.time()
69-
blocker = wait_function(qtbot, signaller.signal, timeout, multiple=False)
83+
raises = raising and not expected_signal_triggered
84+
85+
blocker = wait_function(qtbot, signaller.signal, timeout, raising=raising,
86+
raises=raises, multiple=False)
7087

7188
# Check that event loop exited.
7289
assert not blocker._loop.isRunning()
@@ -84,23 +101,23 @@ def test_signal_triggered(qtbot, wait_function, emit_delay, timeout,
84101

85102
@pytest.mark.parametrize(
86103
('wait_function', 'emit_delay_1', 'emit_delay_2', 'timeout',
87-
'expected_signal_triggered'),
104+
'expected_signal_triggered', 'raising'),
88105
[
89-
(explicit_wait, 500, 600, 2000, True),
90-
(explicit_wait, 500, 600, None, True),
91-
(context_manager_wait, 500, 600, 2000, True),
92-
(context_manager_wait, 500, 600, None, True),
93-
(explicit_wait, 2000, 2000, 500, False),
94-
(explicit_wait, 500, 2000, 1000, False),
95-
(explicit_wait, 2000, 500, 1000, False),
96-
(context_manager_wait, 2000, 2000, 500, False),
97-
(context_manager_wait, 500, 2000, 1000, False),
98-
(context_manager_wait, 2000, 500, 1000, False),
106+
(explicit_wait, 500, 600, 2000, True, False),
107+
(explicit_wait, 500, 600, None, True, False),
108+
(context_manager_wait, 500, 600, 2000, True, False),
109+
(context_manager_wait, 500, 600, None, True, False),
110+
(explicit_wait, 2000, 2000, 500, False, False),
111+
(explicit_wait, 500, 2000, 1000, False, False),
112+
(explicit_wait, 2000, 500, 1000, False, False),
113+
(context_manager_wait, 2000, 2000, 500, False, False),
114+
(context_manager_wait, 500, 2000, 1000, False, False),
115+
(context_manager_wait, 2000, 500, 1000, False, False),
99116
]
100117
)
101118
def test_signal_triggered_multiple(qtbot, wait_function, emit_delay_1,
102119
emit_delay_2, timeout,
103-
expected_signal_triggered):
120+
expected_signal_triggered, raising):
104121
"""
105122
Testing for a signal in different conditions, ensuring we are obtaining
106123
the expected results.
@@ -117,10 +134,10 @@ def test_signal_triggered_multiple(qtbot, wait_function, emit_delay_1,
117134
timer2.timeout.connect(signaller.signal_2.emit)
118135
timer2.start(emit_delay_2)
119136

120-
# block signal until either signal is emitted or timeout is reached
137+
raises = raising and not expected_signal_triggered
121138
start_time = time.time()
122139
blocker = wait_function(qtbot, [signaller.signal, signaller.signal_2],
123-
timeout, multiple=True)
140+
timeout, multiple=True, raising=raising, raises=raises)
124141

125142
# Check that event loop exited.
126143
assert not blocker._loop.isRunning()

pytestqt/plugin.py

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def stopForInteraction(self):
234234

235235
stop = stopForInteraction
236236

237-
def waitSignal(self, signal=None, timeout=1000):
237+
def waitSignal(self, signal=None, timeout=1000, raising=False):
238238
"""
239239
.. versionadded:: 1.2
240240
@@ -256,25 +256,31 @@ def waitSignal(self, signal=None, timeout=1000):
256256
long_function_that_calls_signal()
257257
blocker.wait()
258258
259+
.. versionadded:: 1.4
260+
The *raising* parameter.
261+
259262
:param Signal signal:
260263
A signal to wait for. Set to ``None`` to just use timeout.
261264
:param int timeout:
262265
How many milliseconds to wait before resuming control flow.
266+
:param bool raising:
267+
If :class:`QtBot.SignalTimeoutError <pytestqt.plugin.SignalTimeoutError>`
268+
should be raised if a timeout occurred.
263269
:returns:
264270
``SignalBlocker`` object. Call ``SignalBlocker.wait()`` to wait.
265271
266272
.. note::
267273
Cannot have both ``signals`` and ``timeout`` equal ``None``, or
268274
else you will block indefinitely. We throw an error if this occurs.
269275
"""
270-
blocker = SignalBlocker(timeout=timeout)
276+
blocker = SignalBlocker(timeout=timeout, raising=raising)
271277
if signal is not None:
272278
blocker.connect(signal)
273279
return blocker
274280

275281
wait_signal = waitSignal # pep-8 alias
276282

277-
def waitSignals(self, signals=None, timeout=1000):
283+
def waitSignals(self, signals=None, timeout=1000, raising=False):
278284
"""
279285
.. versionadded:: 1.4
280286
@@ -302,6 +308,9 @@ def waitSignals(self, signals=None, timeout=1000):
302308
timeout.
303309
:param int timeout:
304310
How many milliseconds to wait before resuming control flow.
311+
:param bool raising:
312+
If :class:`QtBot.SignalTimeoutError <pytestqt.plugin.SignalTimeoutError>`
313+
should be raised if a timeout occurred.
305314
:returns:
306315
``MultiSignalBlocker`` object. Call ``MultiSignalBlocker.wait()``
307316
to wait.
@@ -310,7 +319,7 @@ def waitSignals(self, signals=None, timeout=1000):
310319
Cannot have both ``signals`` and ``timeout`` equal ``None``, or
311320
else you will block indefinitely. We throw an error if this occurs.
312321
"""
313-
blocker = MultiSignalBlocker(timeout=timeout)
322+
blocker = MultiSignalBlocker(timeout=timeout, raising=raising)
314323
if signals is not None:
315324
for signal in signals:
316325
blocker.add_signal(signal)
@@ -335,15 +344,19 @@ class AbstractSignalBlocker(object):
335344
be changed before :meth:`wait` is called.
336345
337346
:ivar bool signal_triggered: set to ``True`` if a signal (or all signals in
338-
case of :class:MultipleSignalBlocker:) was triggered, or ``False`` if
339-
timeout was reached instead. Until :meth:`wait` is called, this is set
340-
to ``None``.
347+
case of :class:MultipleSignalBlocker:) was triggered, or
348+
``False`` if timeout was reached instead. Until :meth:`wait` is called,
349+
this is set to ``None``.
350+
351+
:ivar bool raising:
352+
If :class:`SignalTimeoutError` should be raised if a timeout occurred.
341353
"""
342354

343-
def __init__(self, timeout=1000):
355+
def __init__(self, timeout=1000, raising=False):
344356
self._loop = QtCore.QEventLoop()
345357
self.timeout = timeout
346358
self.signal_triggered = False
359+
self.raising = raising
347360

348361
def wait(self):
349362
"""
@@ -359,6 +372,9 @@ def wait(self):
359372
if self.timeout is not None:
360373
QtCore.QTimer.singleShot(self.timeout, self._loop.quit)
361374
self._loop.exec_()
375+
if not self.signal_triggered and self.raising:
376+
raise SignalTimeoutError("Didn't get signal after %sms." %
377+
self.timeout)
362378

363379
def __enter__(self):
364380
return self
@@ -377,14 +393,17 @@ class SignalBlocker(AbstractSignalBlocker):
377393
378394
"""
379395

380-
def __init__(self, timeout=1000):
381-
super(SignalBlocker, self).__init__(timeout)
396+
def __init__(self, timeout=1000, raising=False):
397+
super(SignalBlocker, self).__init__(timeout, raising=raising)
382398
self._signals = []
383399

384400
def connect(self, signal):
385401
"""
386-
Connects to the given signal, making :meth:`wait()` return once this signal
387-
is emitted.
402+
Connects to the given signal, making :meth:`wait()` return once
403+
this signal is emitted.
404+
405+
More than one signal can be connected, in which case **any** one of
406+
them will make ``wait()`` return.
388407
389408
:param signal: QtCore.Signal
390409
"""
@@ -408,8 +427,8 @@ class MultiSignalBlocker(AbstractSignalBlocker):
408427
.. automethod:: add_signal
409428
"""
410429

411-
def __init__(self, timeout=1000):
412-
super(MultiSignalBlocker, self).__init__(timeout)
430+
def __init__(self, timeout=1000, raising=False):
431+
super(MultiSignalBlocker, self).__init__(timeout, raising=raising)
413432
self._signals = {}
414433

415434
def add_signal(self, signal):
@@ -435,6 +454,19 @@ def _signal_emitted(self, signal):
435454
self._loop.quit()
436455

437456

457+
class SignalTimeoutError(Exception):
458+
"""
459+
.. versionadded:: 1.4
460+
461+
The exception thrown by :meth:`QtBot.waitSignal` if the *raising*
462+
parameter has been given and there was a timeout.
463+
"""
464+
pass
465+
466+
# provide easy access to SignalTimeoutError to qtbot fixtures
467+
QtBot.SignalTimeoutError = SignalTimeoutError
468+
469+
438470
@contextmanager
439471
def capture_exceptions():
440472
"""

pytestqt/qt_compat.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,10 @@ def __call__(self, *args, **kwargs):
9898
def __getattr__(cls, name):
9999
if name in ('__file__', '__path__'):
100100
return '/dev/null'
101-
elif name == '__name__':
101+
elif name in ('__name__', '__qualname__'):
102102
return name
103+
elif name == '__annotations__':
104+
return {}
103105
else:
104106
return Mock()
105107

0 commit comments

Comments
 (0)