Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pytest-qt for 
openSUSE:Factory checked in at 2022-11-30 15:00:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-qt (Old)
 and      /work/SRC/openSUSE:Factory/.python-pytest-qt.new.1597 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pytest-qt"

Wed Nov 30 15:00:28 2022 rev:10 rq:1039106 version:4.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pytest-qt/python-pytest-qt.changes        
2022-07-05 12:10:24.388636554 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pytest-qt.new.1597/python-pytest-qt.changes  
    2022-11-30 15:01:21.669842828 +0100
@@ -1,0 +2,20 @@
+Wed Nov 30 12:26:35 UTC 2022 - Daniel Garcia <daniel.gar...@suse.com>
+
+- Remove python_module macro definition
+- Update to 4.2.0:
+  * Import the code sub-package from the correct location rather than the
+    deprecated py package, restoring compatibility with pytest 7.2.0, where py
+    was dropped. Thanks @The-Compiler for the PR.
+  * Use pytest.hookimpl to configure hooks, avoiding a deprecation warning in
+    pytest 7.2.0. Thanks @The-Compiler for the PR.
+  * 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).
+
+-------------------------------------------------------------------

Old:
----
  pytest-qt-4.1.0.tar.gz

New:
----
  pytest-qt-4.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pytest-qt.spec ++++++
--- /var/tmp/diff_new_pack.03lJer/_old  2022-11-30 15:01:22.117845457 +0100
+++ /var/tmp/diff_new_pack.03lJer/_new  2022-11-30 15:01:22.125845504 +0100
@@ -16,7 +16,6 @@
 #
 
 
-%{?!python_module:%define python_module() python3-%{**}}
 %define skip_python2 1
 %global flavor @BUILD_FLAVOR@%{nil}
 %if "%{flavor}" == ""
@@ -65,7 +64,7 @@
 %endif
 
 Name:           python-pytest-qt%{psuffix}
-Version:        4.1.0
+Version:        4.2.0
 Release:        0
 Summary:        Pytest support for PyQt and PySide applications
 License:        MIT

++++++ pytest-qt-4.1.0.tar.gz -> pytest-qt-4.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/.pre-commit-config.yaml 
new/pytest-qt-4.2.0/.pre-commit-config.yaml
--- old/pytest-qt-4.1.0/.pre-commit-config.yaml 2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/.pre-commit-config.yaml 2022-10-25 17:01:03.000000000 
+0200
@@ -1,6 +1,25 @@
 repos:
+-   repo: https://github.com/PyCQA/autoflake
+    rev: v1.7.6
+    hooks:
+    -   id: autoflake
+        name: autoflake
+        args: ["--in-place", "--remove-unused-variables", 
"--remove-all-unused-imports"]
+        language: python
+        files: \.py$
+-   repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.3.0
+    hooks:
+    -   id: trailing-whitespace
+    -   id: end-of-file-fixer
+    -   id: check-yaml
+    -   id: debug-statements
+-   repo: https://github.com/asottile/pyupgrade
+    rev: v3.1.0
+    hooks:
+    - id: pyupgrade
 -   repo: https://github.com/psf/black
-    rev: 22.3.0
+    rev: 22.10.0
     hooks:
     -   id: black
         args: [--safe, --quiet]
@@ -11,19 +30,8 @@
     -   id: blacken-docs
         additional_dependencies: [black==20.8b1]
         language_version: python3
--   repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.2.0
-    hooks:
-    -   id: trailing-whitespace
-    -   id: end-of-file-fixer
-    -   id: check-yaml
-    -   id: debug-statements
--   repo: https://github.com/asottile/pyupgrade
-    rev: v2.32.1
-    hooks:
-    - id: pyupgrade
 -   repo: https://github.com/PyCQA/flake8
-    rev: 4.0.1
+    rev: 5.0.4
     hooks:
     - id: flake8
 -   repo: https://github.com/pre-commit/pygrep-hooks
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/CHANGELOG.rst 
new/pytest-qt-4.2.0/CHANGELOG.rst
--- old/pytest-qt-4.1.0/CHANGELOG.rst   2022-06-23 16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/CHANGELOG.rst   2022-10-25 17:01:03.000000000 +0200
@@ -1,5 +1,25 @@
-UNRELEASED
-----------
+4.2.0 (2022-10-25)
+------------------
+
+- Import the ``code`` sub-package from the correct location rather than the
+  deprecated ``py`` package, restoring compatibility with pytest 7.2.0, where
+  ``py`` was dropped. Thanks `@The-Compiler`_ for the PR.
+
+- Use ``pytest.hookimpl`` to configure hooks, avoiding a deprecation warning in
+  pytest 7.2.0. Thanks `@The-Compiler`_ for the PR.
+
+- 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)
 ------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/PKG-INFO new/pytest-qt-4.2.0/PKG-INFO
--- old/pytest-qt-4.1.0/PKG-INFO        2022-06-23 16:39:27.000000000 +0200
+++ new/pytest-qt-4.2.0/PKG-INFO        2022-10-25 17:01:14.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pytest-qt
-Version: 4.1.0
+Version: 4.2.0
 Summary: pytest support for PyQt and PySide applications
 Home-page: http://github.com/pytest-dev/pytest-qt
 Author: Bruno Oliveira
@@ -104,9 +104,11 @@
 
 Since version 4.1.0, ``pytest-qt`` requires Python 3.7+.
 
-Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_, picking whichever
-is available on the system, giving preference to the first one installed in
-this order:
+Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_.
+
+If any of the above libraries is already imported by the time the tests 
execute, that library will be used.
+
+If not, pytest-qt will try to import and use the Qt APIs, in this order:
 
 - ``PySide6``
 - ``PySide2``
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/README.rst 
new/pytest-qt-4.2.0/README.rst
--- old/pytest-qt-4.1.0/README.rst      2022-06-23 16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/README.rst      2022-10-25 17:01:03.000000000 +0200
@@ -76,9 +76,11 @@
 
 Since version 4.1.0, ``pytest-qt`` requires Python 3.7+.
 
-Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_, picking whichever
-is available on the system, giving preference to the first one installed in
-this order:
+Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_.
+
+If any of the above libraries is already imported by the time the tests 
execute, that library will be used.
+
+If not, pytest-qt will try to import and use the Qt APIs, in this order:
 
 - ``PySide6``
 - ``PySide2``
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/docs/conf.py 
new/pytest-qt-4.2.0/docs/conf.py
--- old/pytest-qt-4.1.0/docs/conf.py    2022-06-23 16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/docs/conf.py    2022-10-25 17:01:03.000000000 +0200
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # pytest-qt documentation build configuration file, created by
 # sphinx-quickstart on Mon Mar 04 22:54:36 2013.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/docs/reference.rst 
new/pytest-qt-4.2.0/docs/reference.rst
--- old/pytest-qt-4.1.0/docs/reference.rst      2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/docs/reference.rst      2022-10-25 17:01:03.000000000 
+0200
@@ -12,6 +12,24 @@
 
 .. autoclass:: TimeoutError
 
+ScreenshotError
+------------------
+
+.. autoclass:: ScreenshotError
+
+
+SignalEmittedError
+------------------
+
+.. autoclass:: SignalEmittedError
+
+
+CallbackCalledTwiceError
+------------------------
+
+.. autoclass:: CallbackCalledTwiceError
+
+
 SignalBlocker
 -------------
 
@@ -23,10 +41,6 @@
 
 .. autoclass:: MultiSignalBlocker
 
-SignalEmittedError
-------------------
-
-.. autoclass:: SignalEmittedError
 
 Record
 ------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/docs/signals.rst 
new/pytest-qt-4.2.0/docs/signals.rst
--- old/pytest-qt-4.1.0/docs/signals.rst        2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/docs/signals.rst        2022-10-25 17:01:03.000000000 
+0200
@@ -21,7 +21,7 @@
             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)
 
@@ -34,7 +34,7 @@
 .. 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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/docs/troubleshooting.rst 
new/pytest-qt-4.2.0/docs/troubleshooting.rst
--- old/pytest-qt-4.1.0/docs/troubleshooting.rst        2022-06-23 
16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/docs/troubleshooting.rst        2022-10-25 
17:01:03.000000000 +0200
@@ -62,7 +62,7 @@
 .. _issue #206: https://github.com/pytest-dev/pytest-qt/issues/206
 
 GitHub Actions
-----------------
+--------------
 
 When using ``ubuntu-latest`` on Github Actions, the package 
``libxkbcommon-x11-0`` has to be installed, ``DISPLAY`` should be set and 
``xvfb`` run. More details can be found in `issue #293`_.
 
@@ -70,7 +70,7 @@
 
 Since Qt in version 5.15 ``xcb`` libraries are not distributed with Qt so this 
library in version at least 1.11 on runner. See more in 
https://codereview.qt-project.org/c/qt/qtbase/+/253905
 
-For Github Actions, Azure pipelines and Travis-CI you will need to install 
``libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 
libxcb-xinerama0 libxcb-xfixes0``
+For Github Actions, Azure pipelines and Travis-CI you will need to install 
``libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 
libxcb-xinerama0 libxcb-xfixes0 x11-utils``
 
 As an example, here is a working config :
 
@@ -84,21 +84,25 @@
         strategy:
           matrix:
             os : [ubuntu-latest]
-            python: [3.7]
+            python: ["3.10"]
         env:
           DISPLAY: ':99.0'
         steps:
         - name: get repo
-          uses: actions/checkout@v1
+          uses: actions/checkout@v3
         - name: Set up Python
-          uses: actions/setup-python@v1
+          uses: actions/setup-python@v4
           with:
             python-version: ${{ matrix.python }}
         - name: setup ${{ matrix.os }}
           run: |
-            sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 
libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 
libxcb-xfixes0
+            sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 
libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 
libxcb-xfixes0 x11-utils
             /sbin/start-stop-daemon --start --quiet --pidfile 
/tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 
-screen 0 1920x1200x24 -ac +extension GLX
 
+``tlambert03/setup-qt-libs``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Instead manually curate list of used packages you may use 
``tlambert03/setup-qt-libs`` github action: 
https://github.com/tlambert03/setup-qt-libs
+
 pytest-xvfb
 ~~~~~~~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/docs/wait_callback.rst 
new/pytest-qt-4.2.0/docs/wait_callback.rst
--- old/pytest-qt-4.1.0/docs/wait_callback.rst  2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/docs/wait_callback.rst  2022-10-25 17:01:03.000000000 
+0200
@@ -25,8 +25,8 @@
 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
 -----------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/docs/wait_until.rst 
new/pytest-qt-4.2.0/docs/wait_until.rst
--- old/pytest-qt-4.1.0/docs/wait_until.rst     2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/docs/wait_until.rst     2022-10-25 17:01:03.000000000 
+0200
@@ -46,7 +46,7 @@
 
 ``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:
 
 ::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytest_qt.egg-info/PKG-INFO 
new/pytest-qt-4.2.0/src/pytest_qt.egg-info/PKG-INFO
--- old/pytest-qt-4.1.0/src/pytest_qt.egg-info/PKG-INFO 2022-06-23 
16:39:26.000000000 +0200
+++ new/pytest-qt-4.2.0/src/pytest_qt.egg-info/PKG-INFO 2022-10-25 
17:01:14.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pytest-qt
-Version: 4.1.0
+Version: 4.2.0
 Summary: pytest support for PyQt and PySide applications
 Home-page: http://github.com/pytest-dev/pytest-qt
 Author: Bruno Oliveira
@@ -104,9 +104,11 @@
 
 Since version 4.1.0, ``pytest-qt`` requires Python 3.7+.
 
-Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_, picking whichever
-is available on the system, giving preference to the first one installed in
-this order:
+Works with either PySide6_, PySide2_, PyQt6_ or PyQt5_.
+
+If any of the above libraries is already imported by the time the tests 
execute, that library will be used.
+
+If not, pytest-qt will try to import and use the Qt APIs, in this order:
 
 - ``PySide6``
 - ``PySide2``
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/_version.py 
new/pytest-qt-4.2.0/src/pytestqt/_version.py
--- old/pytest-qt-4.1.0/src/pytestqt/_version.py        2022-06-23 
16:39:25.000000000 +0200
+++ new/pytest-qt-4.2.0/src/pytestqt/_version.py        2022-10-25 
17:01:14.000000000 +0200
@@ -1,5 +1,5 @@
 # coding: utf-8
 # file generated by setuptools_scm
 # don't change, don't track in version control
-__version__ = version = '4.1.0'
-__version_tuple__ = version_tuple = (4, 1, 0)
+__version__ = version = '4.2.0'
+__version_tuple__ = version_tuple = (4, 2, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/exceptions.py 
new/pytest-qt-4.2.0/src/pytestqt/exceptions.py
--- old/pytest-qt-4.1.0/src/pytestqt/exceptions.py      2022-06-23 
16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/src/pytestqt/exceptions.py      2022-10-25 
17:01:03.000000000 +0200
@@ -98,17 +98,19 @@
     .. 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``.
+    """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/logging.py 
new/pytest-qt-4.2.0/src/pytestqt/logging.py
--- old/pytest-qt-4.1.0/src/pytestqt/logging.py 2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/src/pytestqt/logging.py 2022-10-25 17:01:03.000000000 
+0200
@@ -2,7 +2,7 @@
 from contextlib import contextmanager
 import datetime
 import re
-from py._code.code import TerminalRepr, ReprFileLocation
+from _pytest._code.code import TerminalRepr, ReprFileLocation
 import pytest
 from pytestqt.qt_compat import qt_api
 from pytestqt.utils import get_marker
@@ -39,7 +39,7 @@
         item.qt_log_capture = _QtMessageCapture(ignore_regexes)
         item.qt_log_capture._start()
 
-    @pytest.mark.hookwrapper
+    @pytest.hookimpl(hookwrapper=True)
     def pytest_runtest_makereport(self, item, call):
         """Add captured Qt messages to test item report if the call failed."""
         outcome = yield
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/plugin.py 
new/pytest-qt-4.2.0/src/pytestqt/plugin.py
--- old/pytest-qt-4.1.0/src/pytestqt/plugin.py  2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/src/pytestqt/plugin.py  2022-10-25 17:01:03.000000000 
+0200
@@ -157,8 +157,7 @@
     )
 
 
-@pytest.mark.hookwrapper
-@pytest.mark.tryfirst
+@pytest.hookimpl(hookwrapper=True, tryfirst=True)
 def pytest_runtest_setup(item):
     """
     Hook called after before test setup starts, to start capturing exceptions
@@ -174,8 +173,7 @@
         item.qt_exception_capture_manager.fail_if_exceptions_occurred("SETUP")
 
 
-@pytest.mark.hookwrapper
-@pytest.mark.tryfirst
+@pytest.hookimpl(hookwrapper=True, tryfirst=True)
 def pytest_runtest_call(item):
     yield
     _process_events()
@@ -184,8 +182,7 @@
         item.qt_exception_capture_manager.fail_if_exceptions_occurred("CALL")
 
 
-@pytest.mark.hookwrapper
-@pytest.mark.trylast
+@pytest.hookimpl(hookwrapper=True, trylast=True)
 def pytest_runtest_teardown(item):
     """
     Hook called after each test tear down, to process any pending events and
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/qt_compat.py 
new/pytest-qt-4.2.0/src/pytestqt/qt_compat.py
--- old/pytest-qt-4.1.0/src/pytestqt/qt_compat.py       2022-06-23 
16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/src/pytestqt/qt_compat.py       2022-10-25 
17:01:03.000000000 +0200
@@ -1,6 +1,6 @@
 """
 Provide a common way to import Qt classes used by pytest-qt in a unique manner,
-abstracting API differences between PyQt5 and PySide2/6.
+abstracting API differences between PyQt5/6 and PySide2/6.
 
 .. note:: This module is not part of pytest-qt public API, hence its interface
 may change between releases and users should not rely on it.
@@ -9,20 +9,31 @@
 """
 
 
-from collections import namedtuple
+from collections import namedtuple, OrderedDict
 import os
+import sys
 
 import pytest
 
 
 VersionTuple = namedtuple("VersionTuple", "qt_api, qt_api_version, runtime, 
compiled")
 
+QT_APIS = OrderedDict()
+QT_APIS["pyside6"] = "PySide6"
+QT_APIS["pyside2"] = "PySide2"
+QT_APIS["pyqt6"] = "PyQt6"
+QT_APIS["pyqt5"] = "PyQt5"
+
 
 def _import(name):
     """Think call so we can mock it during testing"""
     return __import__(name)
 
 
+def _is_library_loaded(name):
+    return name in sys.modules
+
+
 class _QtApi:
     """
     Interface to the underlying Qt API currently configured for pytest-qt.
@@ -36,12 +47,7 @@
 
     def _get_qt_api_from_env(self):
         api = os.environ.get("PYTEST_QT_API")
-        supported_apis = [
-            "pyside6",
-            "pyside2",
-            "pyqt6",
-            "pyqt5",
-        ]
+        supported_apis = QT_APIS.keys()
 
         if api is not None:
             api = api.lower()
@@ -50,6 +56,12 @@
                 raise pytest.UsageError(msg)
         return api
 
+    def _get_already_loaded_backend(self):
+        for api, backend in QT_APIS.items():
+            if _is_library_loaded(backend):
+                return api
+        return None
+
     def _guess_qt_api(self):  # pragma: no cover
         def _can_import(name):
             try:
@@ -61,18 +73,18 @@
 
         # Note, not importing only the root namespace because when 
uninstalling from conda,
         # the namespace can still be there.
-        if _can_import("PySide6.QtCore"):
-            return "pyside6"
-        elif _can_import("PySide2.QtCore"):
-            return "pyside2"
-        elif _can_import("PyQt6.QtCore"):
-            return "pyqt6"
-        elif _can_import("PyQt5.QtCore"):
-            return "pyqt5"
+        for api, backend in QT_APIS.items():
+            if _can_import(f"{backend}.QtCore"):
+                return api
         return None
 
     def set_qt_api(self, api):
-        self.pytest_qt_api = self._get_qt_api_from_env() or api or 
self._guess_qt_api()
+        self.pytest_qt_api = (
+            self._get_qt_api_from_env()
+            or api
+            or self._get_already_loaded_backend()
+            or self._guess_qt_api()
+        )
 
         self.is_pyside = self.pytest_qt_api in ["pyside2", "pyside6"]
         self.is_pyqt = self.pytest_qt_api in ["pyqt5", "pyqt6"]
@@ -88,13 +100,7 @@
             )
             raise pytest.UsageError(msg)
 
-        _root_modules = {
-            "pyside6": "PySide6",
-            "pyside2": "PySide2",
-            "pyqt6": "PyQt6",
-            "pyqt5": "PyQt5",
-        }
-        _root_module = _root_modules[self.pytest_qt_api]
+        _root_module = QT_APIS[self.pytest_qt_api]
 
         def _import_module(module_name):
             m = __import__(_root_module, globals(), locals(), [module_name], 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/qtbot.py 
new/pytest-qt-4.2.0/src/pytestqt/qtbot.py
--- old/pytest-qt-4.1.0/src/pytestqt/qtbot.py   2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/src/pytestqt/qtbot.py   2022-10-25 17:01:03.000000000 
+0200
@@ -182,7 +182,8 @@
     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.
@@ -208,7 +209,8 @@
     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.
@@ -238,10 +240,11 @@
         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.waitForWindowExposed context manager instead.
+            Use the ``qtbot.waitExposed`` context manager instead.
 
         :param QWidget widget:
             Widget to wait on.
@@ -255,7 +258,7 @@
         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,
@@ -315,11 +318,11 @@
 
         :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.
@@ -376,11 +379,11 @@
 
         :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.
@@ -566,7 +569,7 @@
         :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.
@@ -616,6 +619,9 @@
         ``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:
@@ -625,7 +631,6 @@
             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():
@@ -711,6 +716,7 @@
 # provide easy access to exceptions to qtbot fixtures
 QtBot.SignalEmittedError = SignalEmittedError
 QtBot.TimeoutError = TimeoutError
+QtBot.ScreenshotError = ScreenshotError
 QtBot.CallbackCalledTwiceError = CallbackCalledTwiceError
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/src/pytestqt/wait_signal.py 
new/pytest-qt-4.2.0/src/pytestqt/wait_signal.py
--- old/pytest-qt-4.1.0/src/pytestqt/wait_signal.py     2022-06-23 
16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/src/pytestqt/wait_signal.py     2022-10-25 
17:01:03.000000000 +0200
@@ -164,7 +164,8 @@
         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
@@ -626,7 +627,8 @@
     :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
@@ -722,8 +724,6 @@
     signal was emitted unexpectedly.
     """
 
-    pass
-
 
 class CallbackCalledTwiceError(Exception):
     """
@@ -733,8 +733,6 @@
     callback was called twice.
     """
 
-    pass
-
 
 def _silent_disconnect(signal, slot):
     """Disconnects a signal from a slot, ignoring errors. Sometimes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/tests/test_basics.py 
new/pytest-qt-4.2.0/tests/test_basics.py
--- old/pytest-qt-4.1.0/tests/test_basics.py    2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/tests/test_basics.py    2022-10-25 17:01:03.000000000 
+0200
@@ -563,8 +563,12 @@
     def _fake_import(name, *args):
         raise ModuleNotFoundError(f"Failed to import {name}")
 
+    def _fake_is_library_loaded(name, *args):
+        return False
+
     monkeypatch.delenv("PYTEST_QT_API", raising=False)
     monkeypatch.setattr(qt_compat, "_import", _fake_import)
+    monkeypatch.setattr(qt_compat, "_is_library_loaded", 
_fake_is_library_loaded)
 
     expected = (
         "pytest-qt requires either PySide2, PySide6, PyQt5 or PyQt6 
installed.\n"
@@ -578,6 +582,73 @@
         qt_api.set_qt_api(api=None)
 
 
+@pytest.mark.parametrize(
+    "option_api, backend",
+    [
+        ("pyqt5", "PyQt5"),
+        ("pyqt6", "PyQt6"),
+        ("pyside2", "PySide2"),
+        ("pyside6", "PySide6"),
+    ],
+)
+def test_already_loaded_backend(monkeypatch, option_api, backend):
+
+    import builtins
+
+    class Mock:
+        pass
+
+    qtcore = Mock()
+    for method_name in (
+        "qInstallMessageHandler",
+        "qDebug",
+        "qWarning",
+        "qCritical",
+        "qFatal",
+    ):
+        setattr(qtcore, method_name, lambda *_: None)
+
+    if backend in ("PyQt5", "PyQt6"):
+        pyqt_version = 0x050B00 if backend == "PyQt5" else 0x060000
+        qtcore.PYQT_VERSION = pyqt_version + 1
+        qtcore.pyqtSignal = object()
+        qtcore.pyqtSlot = object()
+        qtcore.pyqtProperty = object()
+    else:
+        qtcore.Signal = object()
+        qtcore.Slot = object()
+        qtcore.Property = object()
+
+    qtwidgets = Mock()
+    qapplication = Mock()
+    qapplication.instance = lambda *_: None
+    qtwidgets.QApplication = qapplication
+
+    qbackend = Mock()
+    qbackend.QtCore = qtcore
+    qbackend.QtGui = object()
+    qbackend.QtTest = object()
+    qbackend.QtWidgets = qtwidgets
+
+    import_orig = builtins.__import__
+
+    def _fake_import(name, *args, **kwargs):
+        if name == backend:
+            return qbackend
+        return import_orig(name, *args, **kwargs)
+
+    def _fake_is_library_loaded(name, *args):
+        return name == backend
+
+    monkeypatch.delenv("PYTEST_QT_API", raising=False)
+    monkeypatch.setattr(qt_compat, "_is_library_loaded", 
_fake_is_library_loaded)
+    monkeypatch.setattr(builtins, "__import__", _fake_import)
+
+    qt_api.set_qt_api(api=None)
+
+    assert qt_api.pytest_qt_api == option_api
+
+
 def test_before_close_func(testdir):
     """
     Test the `before_close_func` argument of qtbot.addWidget.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/tests/test_logging.py 
new/pytest-qt-4.2.0/tests/test_logging.py
--- old/pytest-qt-4.1.0/tests/test_logging.py   2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/tests/test_logging.py   2022-10-25 17:01:03.000000000 
+0200
@@ -541,7 +541,7 @@
         conftest="""
         import pytest
 
-        @pytest.mark.hookwrapper(tryfirst=True)
+        @pytest.hookimpl(hookwrapper=True, tryfirst=True)
         def pytest_runtest_makereport(call):
             if call.when == 'call':
                 raise Exception("This should not be hidden")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/tests/test_modeltest.py 
new/pytest-qt-4.2.0/tests/test_modeltest.py
--- old/pytest-qt-4.1.0/tests/test_modeltest.py 2022-06-23 16:39:11.000000000 
+0200
+++ new/pytest-qt-4.2.0/tests/test_modeltest.py 2022-10-25 17:01:03.000000000 
+0200
@@ -267,7 +267,6 @@
 
 def test_nop(qtmodeltester):
     """We should not get a crash on cleanup with no model."""
-    pass
 
 
 def test_overridden_methods(qtmodeltester):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/tests/test_screenshot.py 
new/pytest-qt-4.2.0/tests/test_screenshot.py
--- old/pytest-qt-4.1.0/tests/test_screenshot.py        2022-06-23 
16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/tests/test_screenshot.py        2022-10-25 
17:01:03.000000000 +0200
@@ -3,7 +3,6 @@
 import pytest
 
 from pytestqt.qt_compat import qt_api
-from pytestqt.exceptions import ScreenshotError
 
 
 @pytest.fixture
@@ -60,12 +59,12 @@
 
 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")
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-qt-4.1.0/tests/test_wait_until.py 
new/pytest-qt-4.2.0/tests/test_wait_until.py
--- old/pytest-qt-4.1.0/tests/test_wait_until.py        2022-06-23 
16:39:11.000000000 +0200
+++ new/pytest-qt-4.2.0/tests/test_wait_until.py        2022-10-25 
17:01:03.000000000 +0200
@@ -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)
@@ -11,7 +9,7 @@
 
 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
 

Reply via email to