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-07-05 12:09:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-qt (Old)
and /work/SRC/openSUSE:Factory/.python-pytest-qt.new.1548 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-qt"
Tue Jul 5 12:09:49 2022 rev:9 rq:986733 version:4.1.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pytest-qt/python-pytest-qt.changes
2021-07-13 22:37:58.933864215 +0200
+++
/work/SRC/openSUSE:Factory/.python-pytest-qt.new.1548/python-pytest-qt.changes
2022-07-05 12:10:24.388636554 +0200
@@ -1,0 +2,25 @@
+Fri Jul 1 19:06:12 UTC 2022 - Ben Greiner <[email protected]>
+
+- Update to version 4.1.0
+ * pytest-qt now requires Python 3.7+.
+ * Improved PEP-8 aliases definition so they have a smaller call
+ stack depth by one and better parameter suggestions in IDEs.
+ (#383). Thanks @luziferius for the PR.
+ * Updated model tester handling around hasChildren based on Qt's
+ updates.
+ * New qapp_cls fixture returning the QApplication class to use,
+ thus making it easier to use a custom subclass without having
+ to override the whole qapp fixture. Thanks @The-Compiler for
+ the PR.
+ * Updated model tester to track/verify in-flight changes based on
+ Qt's updates. Thanks @The-Compiler for the PR.
+ * New qtbot.screenshot() method which can be used to take a
+ screenshot of the given widget. Thanks @The-Compiler for the
+ PR.
+
+-------------------------------------------------------------------
+Thu Apr 21 13:13:18 UTC 2022 - Ben Greiner <[email protected]>
+
+- Enable pyside6 test flavor
+
+-------------------------------------------------------------------
Old:
----
pytest-qt-4.0.2.tar.gz
New:
----
pytest-qt-4.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pytest-qt.spec ++++++
--- /var/tmp/diff_new_pack.BKnTmg/_old 2022-07-05 12:10:24.788637129 +0200
+++ /var/tmp/diff_new_pack.BKnTmg/_new 2022-07-05 12:10:24.796637140 +0200
@@ -1,7 +1,7 @@
#
# spec file
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -65,13 +65,14 @@
%endif
Name: python-pytest-qt%{psuffix}
-Version: 4.0.2
+Version: 4.1.0
Release: 0
Summary: Pytest support for PyQt and PySide applications
License: MIT
Group: Development/Languages/Python
URL: https://github.com/pytest-dev/pytest-qt
Source:
https://files.pythonhosted.org/packages/source/p/pytest-qt/pytest-qt-%{version}.tar.gz
+BuildRequires: %{python_module base >= 3.7}
BuildRequires: %{python_module setuptools_scm}
BuildRequires: %{python_module setuptools}
BuildRequires: dos2unix
@@ -91,9 +92,8 @@
Requires: (python-qt5 or python-PyQt6)
%endif
%if %{with test}
-# https://github.com/pytest-dev/pytest-qt/issues/376
-BuildRequires: %{python_module pytest >= 4.5}
BuildRequires: %{python_module pytest-qt = %{version}}
+BuildRequires: %{python_module pytest}
%endif
%python_subpackages
++++++ _multibuild ++++++
--- /var/tmp/diff_new_pack.BKnTmg/_old 2022-07-05 12:10:24.836637198 +0200
+++ /var/tmp/diff_new_pack.BKnTmg/_new 2022-07-05 12:10:24.840637204 +0200
@@ -2,6 +2,6 @@
<flavor>test-pyqt5</flavor>
<flavor>test-pyqt6</flavor>
<flavor>test-pyside2</flavor>
- <!-- flavor>test-pyside6</flavor -->
+ <flavor>test-pyside6</flavor>
</multibuild>
++++++ pytest-qt-4.0.2.tar.gz -> pytest-qt-4.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/.github/workflows/main.yml
new/pytest-qt-4.1.0/.github/workflows/main.yml
--- old/pytest-qt-4.0.2/.github/workflows/main.yml 2021-06-14
00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/.github/workflows/main.yml 2022-06-23
16:39:11.000000000 +0200
@@ -10,23 +10,39 @@
fail-fast: false
matrix:
- python-version: [3.6, 3.7, 3.8, 3.9]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"]
qt-lib: [pyqt5, pyqt6, pyside2, pyside6]
os: [ubuntu-20.04, windows-latest, macos-latest]
include:
- - python-version: "3.6"
- tox-env: "py36"
- python-version: "3.7"
tox-env: "py37"
- python-version: "3.8"
tox-env: "py38"
- python-version: "3.9"
tox-env: "py39"
+ - python-version: "3.10"
+ tox-env: "py310"
+ - python-version: "3.11-dev"
+ tox-env: "py311"
+ # https://bugreports.qt.io/browse/PYSIDE-1797
+ exclude:
+ - qt-lib: pyside6
+ os: macos-latest
+ python-version: "3.7"
+ - qt-lib: pyside6
+ os: ubuntu-20.04
+ python-version: "3.7"
+ # Not installable so far
+ - qt-lib: pyside6
+ python-version: "3.11-dev"
+ - qt-lib: pyside2
+ os: windows-latest
+ python-version: "3.11-dev"
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
@@ -48,9 +64,9 @@
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v3
with:
python-version: "3.7"
- name: Install tox
@@ -73,9 +89,9 @@
needs: [build, checks]
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v1
+ uses: actions/setup-python@v3
with:
python-version: "3.7"
- name: Build package
@@ -85,7 +101,7 @@
python setup.py sdist bdist_wheel
- name: Publish package to PyPI
if: github.event_name == 'push' && startsWith(github.event.ref,
'refs/tags')
- uses: pypa/gh-action-pypi-publish@master
+ uses: pypa/[email protected]
with:
user: __token__
password: ${{ secrets.pypi_token }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/.pre-commit-config.yaml
new/pytest-qt-4.1.0/.pre-commit-config.yaml
--- old/pytest-qt-4.0.2/.pre-commit-config.yaml 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/.pre-commit-config.yaml 2022-06-23 16:39:11.000000000
+0200
@@ -1,33 +1,33 @@
repos:
- repo: https://github.com/psf/black
- rev: 21.5b2
+ rev: 22.3.0
hooks:
- id: black
args: [--safe, --quiet]
language_version: python3
- repo: https://github.com/asottile/blacken-docs
- rev: v1.10.0
+ rev: v1.12.1
hooks:
- id: blacken-docs
additional_dependencies: [black==20.8b1]
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.0.1
+ 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.19.1
+ rev: v2.32.1
hooks:
- id: pyupgrade
- repo: https://github.com/PyCQA/flake8
- rev: 3.9.2
+ rev: 4.0.1
hooks:
- id: flake8
- repo: https://github.com/pre-commit/pygrep-hooks
- rev: v1.8.0
+ rev: v1.9.0
hooks:
- id: rst-backticks
- repo: local
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/CHANGELOG.rst
new/pytest-qt-4.1.0/CHANGELOG.rst
--- old/pytest-qt-4.0.2/CHANGELOG.rst 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/CHANGELOG.rst 2022-06-23 16:39:11.000000000 +0200
@@ -1,3 +1,23 @@
+UNRELEASED
+----------
+
+4.1.0 (2022-06-23)
+------------------
+
+- ``pytest-qt`` now requires Python 3.7+.
+- Improved PEP-8 aliases definition so they have a smaller call stack depth by
one and better parameter suggestions in IDEs. (`#383`_). Thanks `@luziferius`_
for the PR.
+- Updated model tester handling around ``hasChildren`` based on Qt's updates.
+- New ``qapp_cls`` fixture returning the ``QApplication`` class to use, thus
+ making it easier to use a custom subclass without having to override the
+ whole ``qapp`` fixture. Thanks `@The-Compiler`_ for the PR.
+- Updated model tester to track/verify in-flight changes based on Qt's updates.
+ Thanks `@The-Compiler`_ for the PR.
+- New ``qtbot.screenshot()`` method which can be used to take a screenshot of
+ the given widget. Thanks `@The-Compiler`_ for the PR.
+
+.. _#383: https://github.com/pytest-dev/pytest-qt/pull/383
+.. _@luziferius: https://github.com/luziferius
+
4.0.2 (2021-06-14)
------------------
@@ -78,7 +98,7 @@
pass ``timeout=1000`` to those functions (`#306`_). Thanks `@The-Compiler`_
for the PR.
- ``waitUntil`` now raises a ``TimeoutError`` when a timeout occurs to make the
- cause of the timeout more explict (`#222`_). Thanks `@karlch`_ for the PR.
+ cause of the timeout more explicit (`#222`_). Thanks `@karlch`_ for the PR.
- The ``QtTest::keySequence`` method is now exposed (if available, with Qt >=
5.10) (`#289`_). Thanks `@The-Compiler`_ for the PR.
- ``addWidget`` now enforces that its argument is a ``QWidget`` in order to
@@ -534,7 +554,7 @@
when tests fail, similar to `pytest-catchlog`_. Also, tests
can be configured to automatically fail if an unexpected message is
generated.
-- New method ``waitSignals``: will block untill **all** signals given are
+- New method ``waitSignals``: will block until **all** signals given are
triggered (thanks `@The-Compiler`_ for idea and complete PR).
- New parameter ``raising`` to ``waitSignals`` and ``waitSignals``: when
``True``
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/PKG-INFO new/pytest-qt-4.1.0/PKG-INFO
--- old/pytest-qt-4.0.2/PKG-INFO 2021-06-14 00:54:52.000000000 +0200
+++ new/pytest-qt-4.1.0/PKG-INFO 2022-06-23 16:39:27.000000000 +0200
@@ -1,28 +1,27 @@
Metadata-Version: 2.1
Name: pytest-qt
-Version: 4.0.2
+Version: 4.1.0
Summary: pytest support for PyQt and PySide applications
Home-page: http://github.com/pytest-dev/pytest-qt
Author: Bruno Oliveira
Author-email: [email protected]
License: MIT
Keywords: pytest qt test unittest
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Desktop Environment :: Window Managers
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: User Interfaces
-Requires-Python: >=3.6
+Requires-Python: >=3.7
Provides-Extra: doc
Provides-Extra: dev
License-File: LICENSE
@@ -32,7 +31,7 @@
=========
pytest-qt is a `pytest`_ plugin that allows programmers to write tests
-for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PyQt6`_ applications.
+for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PySide6`_ applications.
The main usage is to use the ``qtbot`` fixture, responsible for handling
``qApp``
creation as needed and provides methods to simulate user interaction,
@@ -103,7 +102,7 @@
Requirements
============
-Since version 4.0.0, ``pytest-qt`` requires Python 3.6+.
+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
@@ -115,7 +114,7 @@
- ``PyQt5``
To force a particular API, set the configuration variable ``qt_api`` in your
``pytest.ini`` file to
-``pyqt6``, ``pyside2``, ``pyqt6`` or ```pyqt5``:
+``pyqt5``, ``pyside2``, or ``pyqt6``:
.. code-block:: ini
@@ -207,8 +206,9 @@
**Powered by**
-.. |pycharm| image:: https://www.jetbrains.com/pycharm/docs/logo_pycharm.png
+.. |pycharm| image::
https://resources.jetbrains.com/storage/products/company/brand/logos/PyCharm.png
:target: https://www.jetbrains.com/pycharm
+ :width: 400
.. |pydev| image:: http://www.pydev.org/images/pydev_banner3.png
:target: https://www.pydev.org
@@ -218,5 +218,3 @@
|pydev|
.. _tox: https://tox.readthedocs.io
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/README.rst
new/pytest-qt-4.1.0/README.rst
--- old/pytest-qt-4.0.2/README.rst 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/README.rst 2022-06-23 16:39:11.000000000 +0200
@@ -3,7 +3,7 @@
=========
pytest-qt is a `pytest`_ plugin that allows programmers to write tests
-for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PyQt6`_ applications.
+for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PySide6`_ applications.
The main usage is to use the ``qtbot`` fixture, responsible for handling
``qApp``
creation as needed and provides methods to simulate user interaction,
@@ -74,7 +74,7 @@
Requirements
============
-Since version 4.0.0, ``pytest-qt`` requires Python 3.6+.
+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
@@ -86,7 +86,7 @@
- ``PyQt5``
To force a particular API, set the configuration variable ``qt_api`` in your
``pytest.ini`` file to
-``pyqt6``, ``pyside2``, ``pyqt6`` or ```pyqt5``:
+``pyqt5``, ``pyside2``, or ``pyqt6``:
.. code-block:: ini
@@ -178,8 +178,9 @@
**Powered by**
-.. |pycharm| image:: https://www.jetbrains.com/pycharm/docs/logo_pycharm.png
+.. |pycharm| image::
https://resources.jetbrains.com/storage/products/company/brand/logos/PyCharm.png
:target: https://www.jetbrains.com/pycharm
+ :width: 400
.. |pydev| image:: http://www.pydev.org/images/pydev_banner3.png
:target: https://www.pydev.org
Binary files old/pytest-qt-4.0.2/docs/_static/button.png and
new/pytest-qt-4.1.0/docs/_static/button.png differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/conf.py
new/pytest-qt-4.1.0/docs/conf.py
--- old/pytest-qt-4.0.2/docs/conf.py 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/docs/conf.py 2022-06-23 16:39:11.000000000 +0200
@@ -41,8 +41,8 @@
master_doc = "index"
# General information about the project.
-project = u"pytest-qt"
-copyright = u"2013, Bruno Oliveira"
+project = "pytest-qt"
+copyright = "2013, Bruno Oliveira"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -180,7 +180,7 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass
[howto/manual]).
latex_documents = [
- ("index", "pytest-qt.tex", u"pytest-qt Documentation", u"Bruno Oliveira",
"manual")
+ ("index", "pytest-qt.tex", "pytest-qt Documentation", "Bruno Oliveira",
"manual")
]
# The name of an image file (relative to this directory) to place at the top of
@@ -208,7 +208,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [("index", "pytest-qt", u"pytest-qt Documentation", [u"Bruno
Oliveira"], 1)]
+man_pages = [("index", "pytest-qt", "pytest-qt Documentation", ["Bruno
Oliveira"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
@@ -223,8 +223,8 @@
(
"index",
"pytest-qt",
- u"pytest-qt Documentation",
- u"Bruno Oliveira",
+ "pytest-qt Documentation",
+ "Bruno Oliveira",
"pytest-qt",
"One line description of project.",
"Miscellaneous",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/debugging.rst
new/pytest-qt-4.1.0/docs/debugging.rst
--- old/pytest-qt-4.0.2/docs/debugging.rst 1970-01-01 01:00:00.000000000
+0100
+++ new/pytest-qt-4.1.0/docs/debugging.rst 2022-06-23 16:39:11.000000000
+0200
@@ -0,0 +1,54 @@
+Debugging failing tests
+=======================
+
+When a GUI-related test fails, it can sometimes be hard to find out where the
culprit lies. To aid
+with debugging such tests, the ``qtbot`` fixture allows to stop the current
+test and to take screenshots of widgets.
+
+Stopping the current test
+-------------------------
+
+By calling :meth:`pytestqt.qtbot.QtBot.stop`, the current test gets
+interrupted. After closing all visible windows, ``qtbot`` attempts to restore
+the previous state and the test continues to run.
+
+.. note::
+
+ If you use Xvfb or the ``offscreen`` platform plugin (e.g. via
+ ``QT_QPA_PLATFORM=offscreen``), remember to disable those to see the
windows. With the
+ `pytest-xvfb <https://github.com/The-Compiler/pytest-xvfb/>`_ plugin, this
+ is possible by passing ``--no-xvfb`` to pytest.
+
+Taking screenshots
+------------------
+
+.. versionadded:: 4.1
+
+Via :meth:`pytestqt.qtbot.QtBot.screenshot`, a screenshot of a given widget
+can be taken. That screenshot is then saved into a temporary directory provided
+by pytest. For example, this test:
+
+.. code:: python
+
+ from pytestqt.qt_compat import qt_api
+
+
+ def test_screenshot(qtbot):
+ button = qt_api.QtWidgets.QPushButton()
+ button.setText("Hello World!")
+ qtbot.add_widget(button)
+ path = qtbot.screenshot(button)
+ assert False, path # show the path and fail the test
+
+would result in the following file at a location like
+``/tmp/pytest-of-USER/pytest-N/test_screenshot0/screenshot_QPushButton.png``:
+
+.. image:: _static/button.png
+
+The filename is generated based on the following parts:
+
+* ``screenshot``
+* The class of the widget (e.g. ``QWidget`` or ``QPushButton``)
+* The widget's ``objectName()``, if set
+* The given ``suffix``, if passed
+* A counter to make the filename unique, if another screenshot already exists
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/index.rst
new/pytest-qt-4.1.0/docs/index.rst
--- old/pytest-qt-4.0.2/docs/index.rst 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/docs/index.rst 2022-06-23 16:39:11.000000000 +0200
@@ -20,6 +20,7 @@
modeltester
qapplication
note_dialogs
+ debugging
troubleshooting
reference
changelog
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/intro.rst
new/pytest-qt-4.1.0/docs/intro.rst
--- old/pytest-qt-4.0.2/docs/intro.rst 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/docs/intro.rst 2022-06-23 16:39:11.000000000 +0200
@@ -74,7 +74,7 @@
Requirements
============
-Since version 4.0.0, ``pytest-qt`` requires Python 3.6+.
+``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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/qapplication.rst
new/pytest-qt-4.1.0/docs/qapplication.rst
--- old/pytest-qt-4.0.2/docs/qapplication.rst 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/docs/qapplication.rst 2022-06-23 16:39:11.000000000
+0200
@@ -64,13 +64,16 @@
If your tests require access to app-level functions, like
``CustomQApplication.custom_function()``, you can override the built-in
-``qapp`` fixture in your ``conftest.py`` to use your own app:
+``qapp_cls`` fixture in your ``conftest.py`` to return your custom class:
.. code-block:: python
@pytest.fixture(scope="session")
- def qapp():
- yield CustomQApplication([])
+ def qapp_cls():
+ return CustomQApplication
+
+The ``qapp`` fixture will then use the returned class instead of the default
+``QApplication`` from ``QtWidgets``.
Setting a QApplication name
---------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/troubleshooting.rst
new/pytest-qt-4.1.0/docs/troubleshooting.rst
--- old/pytest-qt-4.0.2/docs/troubleshooting.rst 2021-06-14
00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/docs/troubleshooting.rst 2022-06-23
16:39:11.000000000 +0200
@@ -103,3 +103,19 @@
~~~~~~~~~~~
Instead of running Xvfb manually it is possible to use ``pytest-xvfb`` plugin.
+
+Using with other Qt-related packages
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Using Python's Qt modules (``PySide`` or ``PyQt5``) with other packages which
+use Qt (e.g. ``cv2``) can result in conflicts. This is because the latter
builds
+their own Qt and modify Qt-related environment variables. This may not raise
errors
+in your local app, but running the tests on CI servers can fail.
+
+In this case, try use the package without Qt dependency. For example, if your
+code does not rely on ``cv2``'s Qt feature you can use
+``opencv-python-headless`` instead of full ``opencv-python``.
+
+More details can be found in `issue #396`_.
+
+.. _issue #396: https://github.com/pytest-dev/pytest-qt/issues/396
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/docs/wait_until.rst
new/pytest-qt-4.1.0/docs/wait_until.rst
--- old/pytest-qt-4.0.2/docs/wait_until.rst 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/docs/wait_until.rst 2022-06-23 16:39:11.000000000
+0200
@@ -59,7 +59,7 @@
E + Please input a number
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _
> qtbot.waitUntil(check_label)
- E pytestqt.exceptions.TimeoutError: waitUntil timed out in 1000
miliseconds
+ E pytestqt.exceptions.TimeoutError: waitUntil timed out in 1000
milliseconds
A second way to use ``qtbot.waitUntil`` is to pass a callback which returns
``True`` when the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/setup.py new/pytest-qt-4.1.0/setup.py
--- old/pytest-qt-4.0.2/setup.py 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/setup.py 2022-06-23 16:39:11.000000000 +0200
@@ -23,7 +23,7 @@
url="http://github.com/pytest-dev/pytest-qt",
use_scm_version={"write_to": "src/pytestqt/_version.py"},
setup_requires=["setuptools_scm"],
- python_requires=">=3.6",
+ python_requires=">=3.7",
classifiers=[
"Development Status :: 5 - Production/Stable",
"Framework :: Pytest",
@@ -31,10 +31,10 @@
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
"Topic :: Desktop Environment :: Window Managers",
"Topic :: Software Development :: Quality Assurance",
"Topic :: Software Development :: Testing",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytest_qt.egg-info/PKG-INFO
new/pytest-qt-4.1.0/src/pytest_qt.egg-info/PKG-INFO
--- old/pytest-qt-4.0.2/src/pytest_qt.egg-info/PKG-INFO 2021-06-14
00:54:52.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytest_qt.egg-info/PKG-INFO 2022-06-23
16:39:26.000000000 +0200
@@ -1,28 +1,27 @@
Metadata-Version: 2.1
Name: pytest-qt
-Version: 4.0.2
+Version: 4.1.0
Summary: pytest support for PyQt and PySide applications
Home-page: http://github.com/pytest-dev/pytest-qt
Author: Bruno Oliveira
Author-email: [email protected]
License: MIT
Keywords: pytest qt test unittest
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Desktop Environment :: Window Managers
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: User Interfaces
-Requires-Python: >=3.6
+Requires-Python: >=3.7
Provides-Extra: doc
Provides-Extra: dev
License-File: LICENSE
@@ -32,7 +31,7 @@
=========
pytest-qt is a `pytest`_ plugin that allows programmers to write tests
-for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PyQt6`_ applications.
+for `PyQt5`_, `PyQt6`_, `PySide2`_ and `PySide6`_ applications.
The main usage is to use the ``qtbot`` fixture, responsible for handling
``qApp``
creation as needed and provides methods to simulate user interaction,
@@ -103,7 +102,7 @@
Requirements
============
-Since version 4.0.0, ``pytest-qt`` requires Python 3.6+.
+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
@@ -115,7 +114,7 @@
- ``PyQt5``
To force a particular API, set the configuration variable ``qt_api`` in your
``pytest.ini`` file to
-``pyqt6``, ``pyside2``, ``pyqt6`` or ```pyqt5``:
+``pyqt5``, ``pyside2``, or ``pyqt6``:
.. code-block:: ini
@@ -207,8 +206,9 @@
**Powered by**
-.. |pycharm| image:: https://www.jetbrains.com/pycharm/docs/logo_pycharm.png
+.. |pycharm| image::
https://resources.jetbrains.com/storage/products/company/brand/logos/PyCharm.png
:target: https://www.jetbrains.com/pycharm
+ :width: 400
.. |pydev| image:: http://www.pydev.org/images/pydev_banner3.png
:target: https://www.pydev.org
@@ -218,5 +218,3 @@
|pydev|
.. _tox: https://tox.readthedocs.io
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytest_qt.egg-info/SOURCES.txt
new/pytest-qt-4.1.0/src/pytest_qt.egg-info/SOURCES.txt
--- old/pytest-qt-4.0.2/src/pytest_qt.egg-info/SOURCES.txt 2021-06-14
00:54:52.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytest_qt.egg-info/SOURCES.txt 2022-06-23
16:39:27.000000000 +0200
@@ -17,6 +17,7 @@
docs/Makefile
docs/changelog.rst
docs/conf.py
+docs/debugging.rst
docs/index.rst
docs/intro.rst
docs/logging.rst
@@ -31,6 +32,7 @@
docs/virtual_methods.rst
docs/wait_callback.rst
docs/wait_until.rst
+docs/_static/button.png
docs/_static/find_files_dialog.png
src/pytest_qt.egg-info/PKG-INFO
src/pytest_qt.egg-info/SOURCES.txt
@@ -53,6 +55,8 @@
tests/test_exceptions.py
tests/test_logging.py
tests/test_modeltest.py
+tests/test_qtbot_pep8_aliases.py
tests/test_qtest_proxies.py
+tests/test_screenshot.py
tests/test_wait_signal.py
tests/test_wait_until.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-qt-4.0.2/src/pytest_qt.egg-info/entry_points.txt
new/pytest-qt-4.1.0/src/pytest_qt.egg-info/entry_points.txt
--- old/pytest-qt-4.0.2/src/pytest_qt.egg-info/entry_points.txt 2021-06-14
00:54:52.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytest_qt.egg-info/entry_points.txt 2022-06-23
16:39:26.000000000 +0200
@@ -1,3 +1,2 @@
[pytest11]
pytest-qt = pytestqt.plugin
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/_version.py
new/pytest-qt-4.1.0/src/pytestqt/_version.py
--- old/pytest-qt-4.0.2/src/pytestqt/_version.py 2021-06-14
00:54:52.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytestqt/_version.py 2022-06-23
16:39:25.000000000 +0200
@@ -1,5 +1,5 @@
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
-version = '4.0.2'
-version_tuple = (4, 0, 2)
+__version__ = version = '4.1.0'
+__version_tuple__ = version_tuple = (4, 1, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/exceptions.py
new/pytest-qt-4.1.0/src/pytestqt/exceptions.py
--- old/pytest-qt-4.0.2/src/pytestqt/exceptions.py 2021-06-14
00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytestqt/exceptions.py 2022-06-23
16:39:11.000000000 +0200
@@ -101,3 +101,14 @@
"""
pass
+
+
+class ScreenshotError(Exception):
+ """
+ .. versionadded:: 4.1
+
+ Exception thrown by :method:`pytestqt.qtbot.QtBot.screenshot` if taking the
+ screenshot failed.
+ """
+
+ pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/logging.py
new/pytest-qt-4.1.0/src/pytestqt/logging.py
--- old/pytest-qt-4.0.2/src/pytestqt/logging.py 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/src/pytestqt/logging.py 2022-06-23 16:39:11.000000000
+0200
@@ -10,7 +10,7 @@
class QtLoggingPlugin:
"""
- Pluging responsible for installing a QtMessageHandler before each
+ Plugin responsible for installing a QtMessageHandler before each
test and augment reporting if the test failed with the messages captured.
"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/modeltest.py
new/pytest-qt-4.1.0/src/pytestqt/modeltest.py
--- old/pytest-qt-4.0.2/src/pytestqt/modeltest.py 2021-06-14
00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytestqt/modeltest.py 2022-06-23
16:39:11.000000000 +0200
@@ -1,6 +1,6 @@
# This file is based on the original C++ qabstractitemmodeltester.cpp from:
#
http://code.qt.io/cgit/qt/qtbase.git/tree/src/testlib/qabstractitemmodeltester.cpp
-# Commit 4af292fe5158c2d19e8ab1351c71c3940c7f1032
+# Commit b6759ff81c1b6ecb7e18144db0b7c9c5884d7f24
#
# Licensed under the following terms:
#
@@ -41,6 +41,7 @@
#
# $QT_END_LICENSE$
+import enum
import collections
from pytestqt.qt_compat import qt_api
@@ -52,6 +53,18 @@
HAS_QT_TESTER = hasattr(qt_api.QtTest, "QAbstractItemModelTester")
+class _ChangeInFlight(enum.Enum):
+
+ COLUMNS_INSERTED = enum.auto()
+ COLUMNS_MOVED = enum.auto()
+ COLUMNS_REMOVED = enum.auto()
+ LAYOUT_CHANGED = enum.auto()
+ MODEL_RESET = enum.auto()
+ ROWS_INSERTED = enum.auto()
+ ROWS_MOVED = enum.auto()
+ ROWS_REMOVED = enum.auto()
+
+
class ModelTester:
"""A tester for Qt's QAbstractItemModels."""
@@ -63,6 +76,7 @@
self._remove = None
self._changing = []
self._qt_tester = None
+ self._change_in_flight = None
def _debug(self, text):
print("modeltest: " + text)
@@ -131,10 +145,32 @@
# Special checks for changes
self._model.layoutAboutToBeChanged.connect(self._on_layout_about_to_be_changed)
self._model.layoutChanged.connect(self._on_layout_changed)
+
+ # column operations
+ self._model.columnsAboutToBeInserted.connect(
+ self._on_columns_about_to_be_inserted
+ )
+
self._model.columnsAboutToBeMoved.connect(self._on_columns_about_to_be_moved)
+ self._model.columnsAboutToBeRemoved.connect(
+ self._on_columns_about_to_be_removed
+ )
+ self._model.columnsInserted.connect(self._on_columns_inserted)
+ self._model.columnsMoved.connect(self._on_columns_moved)
+ self._model.columnsRemoved.connect(self._on_columns_removed)
+
+ # row operations
self._model.rowsAboutToBeInserted.connect(self._on_rows_about_to_be_inserted)
+ self._model.rowsAboutToBeMoved.connect(self._on_rows_about_to_be_moved)
self._model.rowsAboutToBeRemoved.connect(self._on_rows_about_to_be_removed)
self._model.rowsInserted.connect(self._on_rows_inserted)
+ self._model.rowsMoved.connect(self._on_rows_moved)
self._model.rowsRemoved.connect(self._on_rows_removed)
+
+ # reset
+
self._model.modelAboutToBeReset.connect(self._on_model_about_to_be_reset)
+ self._model.modelReset.connect(self._on_model_reset)
+
+ # data
self._model.dataChanged.connect(self._on_data_changed)
self._model.headerDataChanged.connect(self._on_header_data_changed)
@@ -293,7 +329,7 @@
# QModelIndex when asked for the parent of an invalid index.
assert not self._parent(qt_api.QtCore.QModelIndex()).isValid()
- if not self._has_children():
+ if self._model.rowCount() == 0 or self._column_count() == 0:
return
# Column 0 | Column 1 |
@@ -304,11 +340,12 @@
# Common error test #1, make sure that a top level index has a parent
# that is a invalid QModelIndex.
top_index = self._model.index(0, 0, qt_api.QtCore.QModelIndex())
+ assert top_index.isValid()
assert not self._parent(top_index).isValid()
# Common error test #2, make sure that a second level index has a
# parent that is the first level index.
- if self._has_children(top_index):
+ if self._model.rowCount(top_index) > 0 and
self._column_count(top_index) > 0:
child_index = self._model.index(0, 0, top_index)
assert self._parent(child_index) == top_index
@@ -317,7 +354,10 @@
# Usually the second column shouldn't have children.
if self._model.hasIndex(0, 1):
top_index_1 = self._model.index(0, 1, qt_api.QtCore.QModelIndex())
- if self._has_children(top_index) and
self._has_children(top_index_1):
+ if (
+ self._model.rowCount(top_index) > 0
+ and self._model.rowCount(top_index_1) > 0
+ ):
child_index = self._model.index(0, 0, top_index)
assert child_index.isValid()
child_index_1 = self._model.index(0, 0, top_index_1)
@@ -447,7 +487,7 @@
def _test_data(self):
"""Test model's implementation of data()"""
- if not self._has_children():
+ if self._model.rowCount() == 0 or self._column_count() == 0:
return
# A valid index should have a valid QVariant data
@@ -511,11 +551,104 @@
qt_api.QtCore.Qt.CheckState.Checked,
]
+ def _on_columns_about_to_be_inserted(self, parent, start, end):
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.COLUMNS_INSERTED
+ last_index = self._model.index(start - 1, 0, parent)
+ self._debug(
+ "columns about to be inserted: start {}, end {}, parent {}, "
+ "current count of parent {}, last before insertion {} {}".format(
+ start,
+ end,
+ self._modelindex_debug(parent),
+ self._model.rowCount(parent),
+ self._modelindex_debug(last_index),
+ self._model.data(last_index),
+ )
+ )
+
+ def _on_columns_inserted(self, parent, start, end):
+ assert self._change_in_flight == _ChangeInFlight.COLUMNS_INSERTED
+ self._change_in_flight = None
+ self._debug(
+ "columns inserted: start {}, end {}, parent {}, "
+ "current count of parent {}, ".format(
+ start,
+ end,
+ self._modelindex_debug(parent),
+ self._model.rowCount(parent),
+ )
+ )
+
+ def _on_columns_about_to_be_moved(
+ self, source_parent, source_start, source_end, dest_parent, dest_column
+ ):
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.COLUMNS_MOVED
+ self._debug(
+ "columns about to be moved: source start {}, source end {}, "
+ "source parent {}, destination parent {}, "
+ "destination column {}".format(
+ source_start,
+ source_end,
+ self._modelindex_debug(source_parent),
+ self._modelindex_debug(dest_parent),
+ dest_column,
+ )
+ )
+
+ def _on_columns_moved(
+ self, source_parent, source_start, source_end, dest_parent, dest_column
+ ):
+ assert self._change_in_flight == _ChangeInFlight.COLUMNS_MOVED
+ self._change_in_flight = None
+ self._debug(
+ "columns moved: source start {}, source end {}, "
+ "source parent {}, destination parent {}, "
+ "destination column {}".format(
+ source_start,
+ source_end,
+ self._modelindex_debug(source_parent),
+ self._modelindex_debug(dest_parent),
+ dest_column,
+ )
+ )
+
+ def _on_columns_about_to_be_removed(self, parent, start, end):
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.COLUMNS_REMOVED
+ last_index = self._model.index(start - 1, 0, parent)
+ self._debug(
+ "columns about to be removed: start {}, end {}, "
+ "parent {}, parent rowcount {}, last before removal {}".format(
+ start,
+ end,
+ self._modelindex_debug(parent),
+ self._model.rowCount(parent),
+ self._modelindex_debug(last_index),
+ )
+ )
+
+ def _on_columns_removed(self, parent, start, end):
+ assert self._change_in_flight == _ChangeInFlight.COLUMNS_REMOVED
+ self._change_in_flight = None
+ self._debug(
+ "columns removed: start {}, end {}, parent {}, parent rowcount
{}".format(
+ start,
+ end,
+ self._modelindex_debug(parent),
+ self._model.rowCount(parent),
+ )
+ )
+
def _on_rows_about_to_be_inserted(self, parent, start, end):
"""Store what is about to be inserted.
This gets stored to make sure it actually happens in rowsInserted.
"""
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.ROWS_INSERTED
+
last_index = self._model.index(start - 1, 0, parent)
next_index = self._model.index(start, 0, parent)
parent_rowcount = self._model.rowCount(parent)
@@ -541,6 +674,9 @@
def _on_rows_inserted(self, parent, start, end):
"""Confirm that what was said was going to happen actually did."""
+ assert self._change_in_flight == _ChangeInFlight.ROWS_INSERTED
+ self._change_in_flight = None
+
c = self._insert.pop()
last_data = (
self._model.data(self._model.index(start - 1, 0, parent))
@@ -591,25 +727,82 @@
if next_data is not None:
assert c.next == next_data
+ def _on_rows_about_to_be_moved(
+ self, source_parent, source_start, source_end, dest_parent, dest_row
+ ):
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.ROWS_MOVED
+ self._debug(
+ "rows about to be moved: source start {}, source end {}, "
+ "source parent {}, destination parent {}, "
+ "destination row {}".format(
+ source_start,
+ source_end,
+ self._modelindex_debug(source_parent),
+ self._modelindex_debug(dest_parent),
+ dest_row,
+ )
+ )
+
+ def _on_rows_moved(
+ self, source_parent, source_start, source_end, dest_parent, dest_row
+ ):
+ assert self._change_in_flight == _ChangeInFlight.ROWS_MOVED
+ self._change_in_flight = None
+ self._debug(
+ "rows moved: source start {}, source end {}, "
+ "source parent {}, destination parent {}, "
+ "destination row {}".format(
+ source_start,
+ source_end,
+ self._modelindex_debug(source_parent),
+ self._modelindex_debug(dest_parent),
+ dest_row,
+ )
+ )
+
def _on_layout_about_to_be_changed(self):
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.LAYOUT_CHANGED
+
for i in range(max(self._model.rowCount(), 100)):
idx = qt_api.QtCore.QPersistentModelIndex(self._model.index(i, 0))
self._changing.append(idx)
def _on_layout_changed(self):
+ assert self._change_in_flight == _ChangeInFlight.LAYOUT_CHANGED
+ self._change_in_flight = None
+
for p in self._changing:
assert p == self._model.index(p.row(), p.column(), p.parent())
self._changing = []
+ def _on_model_about_to_be_reset(self):
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.MODEL_RESET
+
+ def _on_model_reset(self):
+ assert self._change_in_flight == _ChangeInFlight.MODEL_RESET
+ self._change_in_flight = None
+
def _on_rows_about_to_be_removed(self, parent, start, end):
"""Store what is about to be removed to make sure it actually happens.
This gets stored to make sure it actually happens in rowsRemoved.
"""
+ assert self._change_in_flight is None
+ self._change_in_flight = _ChangeInFlight.ROWS_REMOVED
+
parent_rowcount = self._model.rowCount(parent)
- last_index = self._model.index(start - 1, 0, parent) if start > 0 else
None
+ last_index = (
+ self._model.index(start - 1, 0, parent)
+ if start > 0 and self._column_count(parent) > 0
+ else None
+ )
next_index = (
- self._model.index(end + 1, 0, parent) if end < parent_rowcount - 1
else None
+ self._model.index(end + 1, 0, parent)
+ if end < parent_rowcount - 1 and self._column_count(parent) > 0
+ else None
)
self._debug(
@@ -638,6 +831,9 @@
def _on_rows_removed(self, parent, start, end):
"""Confirm that what was said was going to happen actually did."""
+ assert self._change_in_flight == _ChangeInFlight.ROWS_REMOVED
+ self._change_in_flight = None
+
c = self._remove.pop()
last_data = (
self._model.data(self._model.index(start - 1, 0, c.parent))
@@ -713,7 +909,7 @@
def _column_count(self, parent=qt_api.QtCore.QModelIndex()):
"""
Workaround for the fact that ``columnCount`` is a private method in
- QAbstractListModel/QAbstractTableModel subclasses.
+ QAbstractListModel subclasses.
"""
if isinstance(self._model, qt_api.QtCore.QAbstractListModel):
return 1 if parent == qt_api.QtCore.QModelIndex() else 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/plugin.py
new/pytest-qt-4.1.0/src/pytestqt/plugin.py
--- old/pytest-qt-4.0.2/src/pytestqt/plugin.py 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/src/pytestqt/plugin.py 2022-06-23 16:39:11.000000000
+0200
@@ -1,3 +1,5 @@
+import warnings
+
import pytest
from pytestqt.exceptions import (
@@ -27,7 +29,30 @@
@pytest.fixture(scope="session")
-def qapp(qapp_args, pytestconfig):
+def qapp_cls():
+ """
+ Fixture that provides the QApplication subclass to use.
+
+ You can override this fixture to use a custom QApplication subclass from
+ your application for tests:
+
+ .. code-block:: python
+
+ @pytest.fixture(scope="session")
+ def qapp_cls():
+ return myapp.Application
+
+ Or use a ``QCoreApplication`` if you want to test a non-gui Qt application:
+
+ @pytest.fixture(scope="session")
+ def qapp_cls():
+ return qt_api.QtCore.QCoreApplication
+ """
+ return qt_api.QtWidgets.QApplication
+
+
[email protected](scope="session")
+def qapp(qapp_args, qapp_cls, pytestconfig):
"""
Fixture that instantiates the QApplication instance that will be used by
the tests.
@@ -38,12 +63,17 @@
app = qt_api.QtWidgets.QApplication.instance()
if app is None:
global _qapp_instance
- _qapp_instance = qt_api.QtWidgets.QApplication(qapp_args)
+ _qapp_instance = qapp_cls(qapp_args)
name = pytestconfig.getini("qt_qapp_name")
_qapp_instance.setApplicationName(name)
return _qapp_instance
else:
- return app # pragma: no cover
+ if not isinstance(app, qapp_cls):
+ warnings.warn(
+ f"Existing QApplication {app} is not an instance of qapp_cls: "
+ f"{qapp_cls}"
+ )
+ return app
# holds a global QApplication instance created in the qapp fixture; keeping
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/qtbot.py
new/pytest-qt-4.1.0/src/pytestqt/qtbot.py
--- old/pytest-qt-4.0.2/src/pytestqt/qtbot.py 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/src/pytestqt/qtbot.py 2022-06-23 16:39:11.000000000
+0200
@@ -2,7 +2,7 @@
import weakref
import warnings
-from pytestqt.exceptions import TimeoutError
+from pytestqt.exceptions import TimeoutError, ScreenshotError
from pytestqt.qt_compat import qt_api
from pytestqt.wait_signal import (
SignalBlocker,
@@ -39,6 +39,7 @@
.. automethod:: waitExposed
.. automethod:: waitForWindowShown
.. automethod:: stop
+ .. automethod:: screenshot
.. automethod:: wait
**Signals and Events**
@@ -88,7 +89,7 @@
.. staticmethod:: keyToAscii (key)
- Auxiliary method that converts the given constant ot its equivalent
ascii.
+ Auxiliary method that converts the given constant to its equivalent
ascii.
:param Qt.Key_* key: one of the constants for keys in the Qt namespace.
@@ -138,6 +139,17 @@
def __init__(self, request):
self._request = request
+ # pep8 aliases. Set here to automatically use implementations defined
in sub-classes for alias creation
+ self.add_widget = self.addWidget
+ self.capture_exceptions = self.captureExceptions
+ self.wait_active = self.waitActive
+ self.wait_exposed = self.waitExposed
+ self.wait_for_window_shown = self.waitForWindowShown
+ self.wait_signal = self.waitSignal
+ self.wait_signals = self.waitSignals
+ self.assert_not_emitted = self.assertNotEmitted
+ self.wait_until = self.waitUntil
+ self.wait_callback = self.waitCallback
def _should_raise(self, raising_arg):
ini_val = self._request.config.getini("qt_default_raising")
@@ -593,6 +605,56 @@
capture_exceptions = captureExceptions
+ def screenshot(self, widget, suffix="", region=None):
+ """
+ .. versionadded:: 4.1
+
+ Take a screenshot of the given widget and save it.
+
+ The file is saved in a test-specific directory using pytest's
``tmp_path``
+ fixture. The filename is ensured to be unique using a counter, and
contains the
+ ``objectName()`` of the widget if set, as well as its class name. A
custom
+ ``suffix`` can be given to add to the generated name.
+
+ :param QWidget widget:
+ The widget to take a screenshot of.
+ :param str suffix:
+ An optional suffix to add to the filename.
+ :param QRect region:
+ The region of the widget to screeshot. By default, the entire
widget is
+ 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():
+ raise ScreenshotError("Got null pixmap from Qt")
+
+ tmp_path = self._request.getfixturevalue("tmp_path")
+
+ parts = ["screenshot", widget.__class__.__name__]
+ name = widget.objectName()
+ if name:
+ parts.append(name)
+ if suffix:
+ parts.append(suffix)
+
+ for i in range(1, 500):
+ counter = [] if i == 1 else [str(i)]
+
+ path = tmp_path / ("_".join(parts + counter) + ".png")
+ if path.exists():
+ continue
+
+ ok = pixmap.save(str(path))
+ if not ok:
+ raise ScreenshotError(f"Saving to {path} failed")
+
+ return path
+
+ raise ScreenshotError(f"Failed to find unique filename, last try:
{path}")
+
@staticmethod
def keyClick(*args, **kwargs):
qt_api.QtTest.QTest.keyClick(*args, **kwargs)
@@ -645,35 +707,6 @@
def mouseRelease(*args, **kwargs):
qt_api.QtTest.QTest.mouseRelease(*args, **kwargs)
- # pep-8 aliases
-
- def add_widget(self, *args, **kwargs):
- return self.addWidget(*args, **kwargs)
-
- def wait_active(self, *args, **kwargs):
- return self.waitActive(*args, **kwargs)
-
- def wait_exposed(self, *args, **kwargs):
- return self.waitExposed(*args, **kwargs)
-
- def wait_for_window_shown(self, *args, **kwargs):
- return self.waitForWindowShown(*args, **kwargs)
-
- def wait_signal(self, *args, **kwargs):
- return self.waitSignal(*args, **kwargs)
-
- def wait_signals(self, *args, **kwargs):
- return self.waitSignals(*args, **kwargs)
-
- def assert_not_emitted(self, *args, **kwargs):
- return self.assertNotEmitted(*args, **kwargs)
-
- def wait_until(self, *args, **kwargs):
- return self.waitUntil(*args, **kwargs)
-
- def wait_callback(self, *args, **kwargs):
- return self.waitCallback(*args, **kwargs)
-
# provide easy access to exceptions to qtbot fixtures
QtBot.SignalEmittedError = SignalEmittedError
@@ -721,7 +754,7 @@
def __init__(self, method_name, adjective_name, widget, timeout):
"""
- :param str method_name: name ot the ``QtTest`` method to call to check
if widget is active/exposed.
+ :param str method_name: name to the ``QtTest`` method to call to check
if widget is active/exposed.
:param str adjective_name: "activated" or "exposed".
:param widget:
:param timeout:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/src/pytestqt/wait_signal.py
new/pytest-qt-4.1.0/src/pytestqt/wait_signal.py
--- old/pytest-qt-4.0.2/src/pytestqt/wait_signal.py 2021-06-14
00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/src/pytestqt/wait_signal.py 2022-06-23
16:39:11.000000000 +0200
@@ -626,7 +626,7 @@
: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 occured.
+ If :class:`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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/tests/test_basics.py
new/pytest-qt-4.1.0/tests/test_basics.py
--- old/pytest-qt-4.0.2/tests/test_basics.py 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/tests/test_basics.py 2022-06-23 16:39:11.000000000
+0200
@@ -42,6 +42,90 @@
res.stdout.fnmatch_lines("*1 passed*")
+def test_qapp_cls(testdir):
+ testdir.makepyfile(
+ app="""
+ from pytestqt.qt_compat import qt_api
+
+ # Gets run before the plugin via conftest.py
+ qt_api.set_qt_api(None)
+
+ class CustomQApp(qt_api.QtWidgets.QApplication):
+ pass
+ """
+ )
+ testdir.makeconftest(
+ """
+ import pytest
+ from app import CustomQApp
+
+ @pytest.fixture(scope="session")
+ def qapp_cls():
+ return CustomQApp
+ """
+ )
+ testdir.makepyfile(
+ """
+ from app import CustomQApp
+
+ def test_cls(qapp):
+ assert isinstance(qapp, CustomQApp)
+ """
+ )
+ res = testdir.runpytest_subprocess()
+ res.stdout.fnmatch_lines("*1 passed*")
+
+
+def test_qapp_reuse_existing(testdir):
+ testdir.makepyfile(
+ """
+ from pytestqt.qt_compat import qt_api
+
+ app_instance = qt_api.QtWidgets.QApplication([])
+
+ def test_instances(qapp):
+ assert qapp is app_instance
+ assert qapp is qt_api.QtWidgets.QApplication.instance()
+ """
+ )
+ res = testdir.runpytest_subprocess()
+ res.stdout.fnmatch_lines("*1 passed*")
+
+
+def test_qapp_reuse_wrong_type(testdir):
+ testdir.makeconftest(
+ """
+ import pytest
+ from pytestqt.qt_compat import qt_api
+
+ # Gets run before the plugin
+ qt_api.set_qt_api(None)
+
+ class CustomQApp(qt_api.QtWidgets.QApplication):
+ pass
+
+ @pytest.fixture(scope="session")
+ def qapp_cls():
+ return CustomQApp
+ """
+ )
+ testdir.makepyfile(
+ """
+ from pytestqt.qt_compat import qt_api
+
+ app_instance = qt_api.QtWidgets.QApplication([])
+
+ def test_wrong_type(qapp):
+ pass
+ """
+ )
+ res = testdir.runpytest_subprocess()
+ res.stdout.fnmatch_lines(
+ "*Existing QApplication <*.QtWidgets.QApplication* at 0x*> is not an "
+ "instance of qapp_cls: <class 'conftest.CustomQApp'>"
+ )
+
+
def test_key_events(qtbot, event_recorder):
"""
Basic key events test.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/tests/test_modeltest.py
new/pytest-qt-4.1.0/tests/test_modeltest.py
--- old/pytest-qt-4.0.2/tests/test_modeltest.py 2021-06-14 00:54:48.000000000
+0200
+++ new/pytest-qt-4.1.0/tests/test_modeltest.py 2022-06-23 16:39:11.000000000
+0200
@@ -1,3 +1,5 @@
+import sys
+
import pytest
from pytestqt.qt_compat import qt_api
@@ -54,6 +56,23 @@
qtmodeltester.check(proxy, force_py=True)
+def test_standard_item_model_zero_columns(qtmodeltester):
+ model = qt_api.QtGui.QStandardItemModel()
+ qtmodeltester.check(model, force_py=True)
+
+ # QTBUG-92220
+ model.insertRows(0, 5)
+ model.removeRows(0, 5)
+
+ # QTBUG-92886
+ model.insertRows(0, 5)
+ model.removeRows(1, 2)
+
+ parent_index = model.index(0, 0)
+ model.insertRows(0, 5, parent_index)
+ model.insertRows(1, 2, parent_index)
+
+
@pytest.mark.parametrize(
"broken_role",
[
@@ -94,12 +113,22 @@
check_model(BrokenTypeModel(), should_pass=False)
+xfail_py311_pyside2 = pytest.mark.xfail(
+ sys.version_info[:2] == (3, 11) and qt_api.pytest_qt_api == "pyside2",
+ reason="Fails to OR mask flags",
+)
+
+
@pytest.mark.parametrize(
"role_value, should_pass",
[
- (qt_api.QtCore.Qt.AlignmentFlag.AlignLeft, True),
- (qt_api.QtCore.Qt.AlignmentFlag.AlignRight, True),
- (0xFFFFFF, False),
+ pytest.param(
+ qt_api.QtCore.Qt.AlignmentFlag.AlignLeft, True,
marks=xfail_py311_pyside2
+ ),
+ pytest.param(
+ qt_api.QtCore.Qt.AlignmentFlag.AlignRight, True,
marks=xfail_py311_pyside2
+ ),
+ pytest.param(0xFFFFFF, False, marks=xfail_py311_pyside2),
("foo", False),
(object(), False),
],
@@ -242,7 +271,7 @@
def test_overridden_methods(qtmodeltester):
- """Make sure overriden methods of a model are actually run.
+ """Make sure overridden methods of a model are actually run.
With a previous implementation of the modeltester using sip.cast, the
custom
implementations did never actually run.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/tests/test_qtbot_pep8_aliases.py
new/pytest-qt-4.1.0/tests/test_qtbot_pep8_aliases.py
--- old/pytest-qt-4.0.2/tests/test_qtbot_pep8_aliases.py 1970-01-01
01:00:00.000000000 +0100
+++ new/pytest-qt-4.1.0/tests/test_qtbot_pep8_aliases.py 2022-06-23
16:39:11.000000000 +0200
@@ -0,0 +1,92 @@
+import inspect
+from unittest.mock import MagicMock
+
+import pytest
+
+from pytestqt.qtbot import QtBot
+
+
+def _format_pep_8(camel_case_name: str) -> str:
+ """
+ Helper that creates a pep8_compliant_method_name
+ from a given camelCaseMethodName.
+ """
+ return camel_case_name[0].lower() + "".join(
+ f"_{letter.lower()}" if letter.isupper() else letter.lower()
+ for letter in camel_case_name[1:]
+ )
+
+
[email protected](
+ "expected, camel_case_input",
+ [
+ ("add_widget", "addWidget"),
+ ("wait_active", "waitActive"),
+ ("wait_exposed", "waitExposed"),
+ ("wait_for_window_shown", "waitForWindowShown"),
+ ("wait_signal", "waitSignal"),
+ ("wait_signals", "waitSignals"),
+ ("assert_not_emitted", "assertNotEmitted"),
+ ("wait_until", "waitUntil"),
+ ("wait_callback", "waitCallback"),
+ ],
+)
+def test_format_pep8(expected: str, camel_case_input: str):
+ assert _format_pep_8(camel_case_input) == expected
+
+
+def test_pep8_aliases(qtbot):
+ """
+ Test that defined PEP8 aliases actually refer to the correct
implementation.
+ Only check methods that have such an alias defined.
+ """
+ for name, func in inspect.getmembers(qtbot, inspect.ismethod):
+ if name != name.lower():
+ pep8_name = _format_pep_8(name)
+ if hasattr(qtbot, pep8_name):
+ # Found a PEP8 alias.
+ assert (
+ getattr(qtbot, name).__func__ is getattr(qtbot,
pep8_name).__func__
+ )
+
+
+def
generate_test_cases_for_test_subclass_of_qtbot_has_overwritten_pep8_aliases():
+ """
+ For each PEP8 alias found in QtBot, yields a test case consisting of
+ a QtBot subclass that has the alias pair???s camelCase implementation
+ overwritten with a MagicMock.
+
+ Yields tuples (subclass, camelCaseMethodName, pep8_method_name_alias)
+ """
+ for name, func in inspect.getmembers(QtBot, inspect.isfunction):
+ if name != name.lower():
+ subclass_logic_mock = MagicMock()
+ pep8_name = _format_pep_8(name)
+ if hasattr(QtBot, pep8_name):
+ # Found a PEP8 alias.
+ methods = QtBot.__dict__.copy()
+ # Only overwrite the camelCase method
+ methods[name] = subclass_logic_mock
+ sub_class = type("QtBotSubclass", (QtBot,), methods)
+ yield sub_class, name, pep8_name
+
+
[email protected](
+ "qtbot_subclass, method_name, pep8_name",
+
generate_test_cases_for_test_subclass_of_qtbot_has_overwritten_pep8_aliases(),
+)
+def test_subclass_of_qtbot_has_overwritten_pep8_aliases(
+ qtbot_subclass, method_name: str, pep8_name: str
+):
+ """
+ Test that subclassing QtBot does not create surprises,
+ by checking that the PEP8 aliases follow overwritten
+ camelCase methods.
+ """
+ instance: QtBot = qtbot_subclass(MagicMock())
+ assert isinstance(getattr(instance, method_name), MagicMock)
+ assert isinstance(getattr(instance, pep8_name), MagicMock)
+ # Now call the pep8_name_method and check that the subclass???s
+ # camelCaseMethod implementation was actually called.
+ getattr(instance, pep8_name)()
+ getattr(instance, method_name).assert_called()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/tests/test_screenshot.py
new/pytest-qt-4.1.0/tests/test_screenshot.py
--- old/pytest-qt-4.0.2/tests/test_screenshot.py 1970-01-01
01:00:00.000000000 +0100
+++ new/pytest-qt-4.1.0/tests/test_screenshot.py 2022-06-23
16:39:11.000000000 +0200
@@ -0,0 +1,92 @@
+import pathlib
+
+import pytest
+
+from pytestqt.qt_compat import qt_api
+from pytestqt.exceptions import ScreenshotError
+
+
[email protected]
+def widget(qtbot):
+ w = qt_api.QtWidgets.QWidget()
+ qtbot.addWidget(w)
+ w.setAttribute(qt_api.QtCore.Qt.WidgetAttribute.WA_StyledBackground)
+ w.setStyleSheet("background-color: magenta")
+ return w
+
+
+def test_basic(qtbot, widget):
+ path = qtbot.screenshot(widget)
+ assert path.exists()
+
+ pixmap = qt_api.QtGui.QPixmap()
+ assert pixmap.load(str(path))
+
+ image = pixmap.toImage()
+ color = image.pixelColor(image.rect().center())
+ assert (color.red(), color.green(), color.blue()) == (255, 0, 255)
+
+
+def test_region(qtbot, widget):
+ region = qt_api.QtCore.QRect(0, 0, 25, 25)
+ path = qtbot.screenshot(widget, region=region)
+
+ pixmap = qt_api.QtGui.QPixmap()
+ assert pixmap.load(str(path))
+ assert pixmap.rect() == region
+
+
+def test_filename_class(qtbot, widget):
+ path = qtbot.screenshot(widget)
+ assert path.name == "screenshot_QWidget.png"
+
+
+def test_filename_objectname(qtbot, widget):
+ widget.setObjectName("shotgun")
+ path = qtbot.screenshot(widget)
+ assert path.name == "screenshot_QWidget_shotgun.png"
+
+
+def test_filename_suffix(qtbot, widget):
+ path = qtbot.screenshot(widget, suffix="before")
+ assert path.name == "screenshot_QWidget_before.png"
+
+
+def test_filename_both(qtbot, widget):
+ widget.setObjectName("shotgun")
+ path = qtbot.screenshot(widget, suffix="before")
+ assert path.name == "screenshot_QWidget_shotgun_before.png"
+
+
+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"):
+ qtbot.screenshot(widget, suffix="before")
+
+
+def test_filename_invalid(qtbot, widget):
+ with pytest.raises(ScreenshotError, match="Saving to .* failed"):
+ qtbot.screenshot(widget, suffix=r"invalid/path\everywhere")
+
+
+def test_folder(qtbot, tmp_path, widget):
+ path = qtbot.screenshot(widget)
+ assert path.parent == tmp_path
+
+
[email protected](
+ "existing, expected",
+ [
+ (["QLineEdit"], "QWidget"),
+ (["QWidget"], "QWidget_2"),
+ (["QWidget_2"], "QWidget"),
+ (["QWidget", "QWidget_2"], "QWidget_3"),
+ ],
+)
+def test_filename_dedup(qtbot, widget, tmp_path, existing, expected):
+ for name in existing:
+ path = tmp_path / f"screenshot_{name}.png"
+ path.touch()
+
+ path = qtbot.screenshot(widget)
+ assert path.name == f"screenshot_{expected}.png"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-qt-4.0.2/tox.ini new/pytest-qt-4.1.0/tox.ini
--- old/pytest-qt-4.0.2/tox.ini 2021-06-14 00:54:48.000000000 +0200
+++ new/pytest-qt-4.1.0/tox.ini 2022-06-23 16:39:11.000000000 +0200
@@ -1,5 +1,5 @@
[tox]
-envlist = py{36,37,38}-pyqt5, py{36,37,38}-pyside2, py{37,38}-pyside6,
py{36,37,38}-pyqt6, linting
+envlist = py{37,38,39,310}-{pyqt5,pyside2,pyside6,pyqt6}, linting
[testenv]
deps=