Skip to content

Commit

Permalink
Reference TimeoutError and ScreenshotError via qtbot
Browse files Browse the repository at this point in the history
Fix #459
  • Loading branch information
nicoddemus committed Oct 25, 2022
1 parent 1eb6ccf commit 83fd352
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 37 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -11,7 +11,14 @@ UNRELEASED
- Now ``pytest-qt`` will check if any of the Qt libraries is already imported by the time the plugin loads,
and use it if that is the case (`#412`_). Thanks `@eyllanesc`_ for the PR.

- Most custom ``pytest-qt`` exceptions can be accessed via ``qtbot`` (for example ``qtbot.TimeoutError``),
but it was not always explicit in the documentation that this is the recommended way to access those exceptions, instead
of importing them from ``pytestqt.exceptions``.
This is now clarified in the documentation and examples, and an alias to ``ScreenshotError`` has been
added to ``qtbot`` so it can be accessed in the same way (`#460`_).

.. _#412: https://github.com/pytest-dev/pytest-qt/pull/412
.. _#460: https://github.com/pytest-dev/pytest-qt/pull/460
.. _@eyllanesc: https://github.com/eyllanesc

4.1.0 (2022-06-23)
Expand Down
22 changes: 18 additions & 4 deletions docs/reference.rst
Expand Up @@ -12,6 +12,24 @@ TimeoutError

.. autoclass:: TimeoutError

ScreenshotError
------------------

.. autoclass:: ScreenshotError


SignalEmittedError
------------------

.. autoclass:: SignalEmittedError


CallbackCalledTwiceError
------------------------

.. autoclass:: CallbackCalledTwiceError


SignalBlocker
-------------

Expand All @@ -23,10 +41,6 @@ MultiSignalBlocker

.. autoclass:: MultiSignalBlocker

SignalEmittedError
------------------

.. autoclass:: SignalEmittedError

Record
------
Expand Down
4 changes: 2 additions & 2 deletions docs/signals.rst
Expand Up @@ -21,7 +21,7 @@ ensuring the results are correct:
app.worker.start()
# Test will block at this point until either the "finished" or the
# "failed" signal is emitted. If 10 seconds passed without a signal,
# TimeoutError will be raised.
# qtbot.TimeoutError will be raised.
assert_application_results(app)
Expand All @@ -34,7 +34,7 @@ raising parameter
.. versionchanged:: 2.0

You can pass ``raising=False`` to avoid raising a
:class:`qtbot.TimeoutError <TimeoutError>` if the timeout is
:class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>` if the timeout is
reached before the signal is triggered:

.. code-block:: python
Expand Down
4 changes: 2 additions & 2 deletions docs/wait_callback.rst
Expand Up @@ -25,8 +25,8 @@ For example:
Anything following the ``with`` block will be run only after the callback has been called.

If the callback doesn't get called during the given timeout,
:class:`qtbot.TimeoutError <TimeoutError>` is raised. If it is called more than once,
:class:`qtbot.CallbackCalledTwiceError <CallbackCalledTwiceError>` is raised.
:class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>` is raised. If it is called more than once,
:class:`qtbot.CallbackCalledTwiceError <pytestqt.wait_signal.CallbackCalledTwiceError>` is raised.

raising parameter
-----------------
Expand Down
2 changes: 1 addition & 1 deletion docs/wait_until.rst
Expand Up @@ -46,7 +46,7 @@ assertion:
``qtbot.waitUntil`` will periodically call ``check_label`` until it no longer raises
``AssertionError`` or a timeout is reached. If a timeout is reached, a
:class:`qtbot.TimeoutError <TimeoutError>`
:class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`
is raised from the last assertion error and the test will fail:

::
Expand Down
12 changes: 7 additions & 5 deletions src/pytestqt/exceptions.py
Expand Up @@ -98,17 +98,19 @@ class TimeoutError(Exception):
.. versionadded:: 2.1
Exception thrown by :class:`pytestqt.qtbot.QtBot` methods.
"""
pass
Access via ``qtbot.TimeoutError``.
"""


class ScreenshotError(Exception):
"""
.. versionadded:: 4.1
Exception thrown by :method:`pytestqt.qtbot.QtBot.screenshot` if taking the
Exception thrown by :meth:`pytestqt.qtbot.QtBot.screenshot` if taking the
screenshot failed.
"""
pass
.. versionchanged:: 4.2
Access via ``qtbot.ScreenshotError``.
"""
26 changes: 16 additions & 10 deletions src/pytestqt/qtbot.py
Expand Up @@ -182,7 +182,8 @@ def addWidget(self, widget, *, before_close_func=None):
def waitActive(self, widget, *, timeout=5000):
"""
Context manager that waits for ``timeout`` milliseconds or until the window is active.
If window is not exposed within ``timeout`` milliseconds, raise ``TimeoutError``.
If window is not exposed within ``timeout`` milliseconds, raise
:class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`
This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen
some time after being asked to show itself on the screen.
Expand All @@ -208,7 +209,8 @@ def waitActive(self, widget, *, timeout=5000):
def waitExposed(self, widget, *, timeout=5000):
"""
Context manager that waits for ``timeout`` milliseconds or until the window is exposed.
If the window is not exposed within ``timeout`` milliseconds, raise ``TimeoutError``.
If the window is not exposed within ``timeout`` milliseconds, raise
:class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`
This is mainly useful for asynchronous systems like X11, where a window will be mapped to screen
some time after being asked to show itself on the screen.
Expand Down Expand Up @@ -238,7 +240,8 @@ def waitForWindowShown(self, widget):
show itself on the screen.
.. warning::
This method does **not** raise ``TimeoutError`` if the window wasn't shown.
This method does **not** raise :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>` if
the window wasn't shown.
.. deprecated:: 4.0
Use the ``qtbot.waitExposed`` context manager instead.
Expand All @@ -255,7 +258,7 @@ def waitForWindowShown(self, widget):
warnings.warn(
"waitForWindowShown is deprecated, as the underlying Qt method was "
"obsoleted in Qt 5.0 and removed in Qt 6.0. Its name is imprecise and "
"the pytest-qt wrapper does not raise TimeoutError if the window "
"the pytest-qt wrapper does not raise qtbot.TimeoutError if the window "
"wasn't shown. Please use the qtbot.waitExposed context manager "
"instead.",
DeprecationWarning,
Expand Down Expand Up @@ -315,11 +318,11 @@ def waitSignal(self, signal, *, timeout=5000, raising=None, check_params_cb=None
:param Signal signal:
A signal to wait for, or a tuple ``(signal, signal_name_as_str)`` to improve the error message that is part
of ``TimeoutError``.
of :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`.
:param int timeout:
How many milliseconds to wait before resuming control flow.
:param bool raising:
If :class:`QtBot.TimeoutError <pytestqt.plugin.TimeoutError>`
If :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`
should be raised if a timeout occurred.
This defaults to ``True`` unless ``qt_default_raising = false``
is set in the config.
Expand Down Expand Up @@ -376,11 +379,11 @@ def waitSignals(
:param list signals:
A list of :class:`Signal` objects to wait for. Alternatively: a list of (``Signal, str``) tuples of the form
``(signal, signal_name_as_str)`` to improve the error message that is part of ``TimeoutError``.
``(signal, signal_name_as_str)`` to improve the error message that is part of ``qtbot.TimeoutError``.
:param int timeout:
How many milliseconds to wait before resuming control flow.
:param bool raising:
If :class:`QtBot.TimeoutError <pytestqt.plugin.TimeoutError>`
If :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`
should be raised if a timeout occurred.
This defaults to ``True`` unless ``qt_default_raising = false``
is set in the config.
Expand Down Expand Up @@ -566,7 +569,7 @@ def waitCallback(self, *, timeout=5000, raising=None):
:param int timeout:
How many milliseconds to wait before resuming control flow.
:param bool raising:
If :class:`QtBot.TimeoutError <pytestqt.plugin.TimeoutError>`
If :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>`
should be raised if a timeout occurred.
This defaults to ``True`` unless ``qt_default_raising = false``
is set in the config.
Expand Down Expand Up @@ -616,6 +619,9 @@ def screenshot(self, widget, suffix="", region=None):
``objectName()`` of the widget if set, as well as its class name. A custom
``suffix`` can be given to add to the generated name.
Raises :class:`qtbot.ScreenshotError <pytestqt.exceptions.ScreenshotError>`
if taking the screenshot or saving the file failed.
:param QWidget widget:
The widget to take a screenshot of.
:param str suffix:
Expand All @@ -625,7 +631,6 @@ def screenshot(self, widget, suffix="", region=None):
contained.
:returns:
A ``pathlib.Path`` object with the taken screenshot.
:raises ScreenshotError: if taking the screenshot or saving the file failed.
"""
pixmap = widget.grab() if region is None else widget.grab(region)
if pixmap.isNull():
Expand Down Expand Up @@ -711,6 +716,7 @@ def mouseRelease(*args, **kwargs):
# provide easy access to exceptions to qtbot fixtures
QtBot.SignalEmittedError = SignalEmittedError
QtBot.TimeoutError = TimeoutError
QtBot.ScreenshotError = ScreenshotError
QtBot.CallbackCalledTwiceError = CallbackCalledTwiceError


Expand Down
10 changes: 4 additions & 6 deletions src/pytestqt/wait_signal.py
Expand Up @@ -164,7 +164,8 @@ class SignalBlocker(_AbstractSignalBlocker):
this is set to ``None``.
:ivar bool raising:
If :class:`TimeoutError` should be raised if a timeout occurred.
If :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>` should be raised
if a timeout occurred.
.. note:: contrary to the parameter of same name in
:meth:`pytestqt.qtbot.QtBot.waitSignal`, this parameter does not
Expand Down Expand Up @@ -626,7 +627,8 @@ class CallbackBlocker:
:ivar int timeout: maximum time to wait for the callback to be called.
:ivar bool raising:
If :class:`TimeoutError` should be raised if a timeout occurred.
If :class:`qtbot.TimeoutError <pytestqt.exceptions.TimeoutError>` should be raised if
a timeout occurred.
.. note:: contrary to the parameter of same name in
:meth:`pytestqt.qtbot.QtBot.waitCallback`, this parameter does not
Expand Down Expand Up @@ -722,8 +724,6 @@ class SignalEmittedError(Exception):
signal was emitted unexpectedly.
"""

pass


class CallbackCalledTwiceError(Exception):
"""
Expand All @@ -733,8 +733,6 @@ class CallbackCalledTwiceError(Exception):
callback was called twice.
"""

pass


def _silent_disconnect(signal, slot):
"""Disconnects a signal from a slot, ignoring errors. Sometimes
Expand Down
1 change: 0 additions & 1 deletion tests/test_modeltest.py
Expand Up @@ -267,7 +267,6 @@ def test_changing_model_sort(qtmodeltester):

def test_nop(qtmodeltester):
"""We should not get a crash on cleanup with no model."""
pass


def test_overridden_methods(qtmodeltester):
Expand Down
5 changes: 2 additions & 3 deletions tests/test_screenshot.py
Expand Up @@ -3,7 +3,6 @@
import pytest

from pytestqt.qt_compat import qt_api
from pytestqt.exceptions import ScreenshotError


@pytest.fixture
Expand Down Expand Up @@ -60,12 +59,12 @@ def test_filename_both(qtbot, widget):

def test_filename_endless(qtbot, widget, monkeypatch):
monkeypatch.setattr(pathlib.Path, "exists", lambda _self: True)
with pytest.raises(ScreenshotError, match="Failed to find unique filename"):
with pytest.raises(qtbot.ScreenshotError, match="Failed to find unique filename"):
qtbot.screenshot(widget, suffix="before")


def test_filename_invalid(qtbot, widget):
with pytest.raises(ScreenshotError, match="Saving to .* failed"):
with pytest.raises(qtbot.ScreenshotError, match="Saving to .* failed"):
qtbot.screenshot(widget, suffix=r"invalid/path\everywhere")


Expand Down
4 changes: 1 addition & 3 deletions tests/test_wait_until.py
@@ -1,7 +1,5 @@
import pytest

from pytestqt.exceptions import TimeoutError


def test_wait_until(qtbot, wait_4_ticks_callback, tick_counter):
tick_counter.start(100)
Expand All @@ -11,7 +9,7 @@ def test_wait_until(qtbot, wait_4_ticks_callback, tick_counter):

def test_wait_until_timeout(qtbot, wait_4_ticks_callback, tick_counter):
tick_counter.start(200)
with pytest.raises(TimeoutError):
with pytest.raises(qtbot.TimeoutError):
qtbot.waitUntil(wait_4_ticks_callback, timeout=100)
assert tick_counter.ticks < 4

Expand Down

0 comments on commit 83fd352

Please sign in to comment.