BUG: Multiple `PyQt`/`PySide` Versions Not Supported
See original GitHub issueBug
I ran into this issue when trying to fix a bug with pyvistaqt. It appears, for some reason, when all of
PyQt5
PySide2
PyQt6
PySide6
are installed, pytest-qt
fails to create a QApplication
instance, causing tests to silently fail.
NOTE: QT_API
is not set to anything on my system when this occurs.
System Information
OS: Windows 10, x64-bit Python: 3.8.10 x64-bit (CPython) pytest-qt: 4.1.0
Steps to Reproduce
- Clone the
pyvistaqt
repo and cd into the folder
git clone https://github.com/pyvista/pyvistaqt.git
cd pyvistaqt
- Create and activate a virtual environment
py -m venv .venv && .venv\Scripts\Activate.ps1
- Install dependencies
py -m pip install --upgrade pip
py -m pip install -r requirements_test.txt
py -m pip install -r requirements_docs.txt
- First, install only
PySide6
py -m pip install pyside6
- Sanity Check: Run tests to verify everything is working correctly (All Pass)
pytest
- Now install
PyQt5
,PySide2
, andPyQt6
py -m pip install pyqt5 pyside2 pyqt6
- Rerun
pytest
and tests will fail silently
PS> pytest
=================================== test session starts =======================================
platform win32 -- Python 3.8.10, pytest-7.1.2, pluggy-1.0.0
PySide6 6.3.1 -- Qt runtime 6.3.1 -- Qt compiled 6.3.1
rootdir: %USERPROFILE%\Code\external\pyvistaqt-demo\pyvistaqt, configfile: pytest.ini
plugins: cov-3.0.0, memprof-0.2.0, mypy-plugins-1.9.3, mypy-testing-0.0.11, qt-4.1.0, sphinx-0.4.0
collected 2 items
tests\test_plotting.py
- Re-run
pytest
in verbose mode while printing output to stdout/stderr
PS> pytest -v -s
==================================== test session starts ======================================
platform win32 -- Python 3.8.10, pytest-7.1.2, pluggy-1.0.0 -- %USERPROFILE%\code\external\pyvistaqt-demo\pyvistaqt\.venv\scripts\python.exe
cachedir: .pytest_cache
PySide6 6.3.1 -- Qt runtime 6.3.1 -- Qt compiled 6.3.1
rootdir: %USERPROFILE%\Code\external\pyvistaqt-demo\pyvistaqt, configfile: pytest.ini
plugins: cov-3.0.0, memprof-0.2.0, mypy-plugins-1.9.3, mypy-testing-0.0.11, qt-4.1.0, sphinx-0.4.0
collected 2 items
tests/test_plotting.py::test_create_menu_bar QWidget: Must construct a QApplication before a QWidget
- Add an
assert
to the offending test for details
def test_create_menu_bar(qtbot):
assert QtWidgets.QApplication.instance() is not None
menu_bar = _create_menu_bar(parent=None)
qtbot.addWidget(menu_bar)
- Rerun test in verbose mode while outputing to stdout/stderr
PS> pytest -v -s
==================================== test session starts ======================================
platform win32 -- Python 3.8.10, pytest-7.1.2, pluggy-1.0.0 -- %USERPROFILE%\code\external\pyvistaqt-demo\pyvistaqt\.venv\scripts\python.exe
cachedir: .pytest_cache
PySide6 6.3.1 -- Qt runtime 6.3.1 -- Qt compiled 6.3.1
rootdir: %USERPROFILE%\Code\external\pyvistaqt-demo\pyvistaqt, configfile: pytest.ini
plugins: cov-3.0.0, memprof-0.2.0, mypy-plugins-1.9.3, mypy-testing-0.0.11, qt-4.1.0, sphinx-0.4.0
collected 2 items
tests/test_plotting.py::test_create_menu_bar FAILED
tests/test_qt.py::test_no_qt_binding PASSED
========================================= FAILURES ======================================
____________________________________________________________ test_create_menu_bar _________________________________________________________
qtbot = <pytestqt.qtbot.QtBot object at 0x00000237D940E160>
def test_create_menu_bar(qtbot):
> assert QtWidgets.QApplication.instance() is not None
E AssertionError: assert None is not None
E + where None = <built-in function instance>()
E + where <built-in function instance> = <class 'PyQt5.QtWidgets.QApplication'>.instance
E + where <class 'PyQt5.QtWidgets.QApplication'> = QtWidgets.QApplication
tests\test_plotting.py:79: AssertionError
================================== memory consumption estimates ==============================
tests::test_plotting.py::test_create_menu_bar - 40.0 KB
================================== short test summary info ====================================
FAILED tests/test_plotting.py::test_create_menu_bar - AssertionError: assert None is not None
================================== 1 failed, 1 passed in 0.79s ===================================
It appears there is a disconnect between the python Qt
library registered by pytest
:
PySide6 6.3.1 -- Qt runtime 6.3.1 -- Qt compiled 6.3.1
and the one picked up by pytest-qt
> assert QtWidgets.QApplication.instance() is not None
E AssertionError: assert None is not None
E + where None = <built-in function instance>()
E + where <built-in function instance> = <class 'PyQt5.QtWidgets.QApplication'>.instance
E + where <class 'PyQt5.QtWidgets.QApplication'> = QtWidgets.QApplication
Expected Behavior
pytest-qt
can be run with multiple versions of python Qt libraries installed simultaneously for testing purposes.
Actual Behavior
pytest-qt
has a disconnect between the python Qt binding registered with pytest
and the one it uses.
Issue Analytics
- State:
- Created a year ago
- Comments:9 (3 by maintainers)
Top Results From Across the Web
Does PySide support Python 3? - qt - Stack Overflow
Long story short, the answer is yes and it can be installed with hombrew. However, PySide has a somewhat unkown future. From what...
Read more >PySide availability for Python 3.5 · Issue #132 - GitHub
I discovered that while the official PyQt4 and PyQt5 downloads don't yet support Python 3.5, there are Windows binaries available through ...
Read more >PyQt vs PySide: What are the licensing ... - Python GUIs
PyQt was developed by Phil Thompson of Riverbank Computing Ltd. supporting versions of Qt going back to 2.x under a GPL license.
Read more >Differences Between PySide and PyQt - Qt Wiki
PySide only supports PyQt's API 2 (see PSEP 101) for details. Therefore Qt classes such as QStrings, QStringLists, and QVariants are not ...
Read more >Handling SQL Databases With PyQt: The Basics - Real Python
Use PyQt's SQL support to reliably connect to a database; Execute SQL ... 8 9# Open the connection 10if not con.open(): 11 print("Database...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
This happens because pyvistaqt uses QtPy which uses PyQt5 if
QT_API
was not set, butpytest-qt
defaults toPySide6
withoutPYTEST_QT_API
(orqt_api
in the pytest config). Thus, you end up with a PySide6QApplication
but a PyQt5 widget being created by the code.Doesn’t seem like a bug to me, just a misconfiguration on your (or the project’s) side. Maybe #412 would help against this kind of thing, because in
qt_compat.set_qt_api
, the only already imported wrapper isPyQt5
.Several other test packages rely on
Qt
, not justpytestqt
. The fact thatQT_API
exists forqtpy
is an indicator: what package does the settingqt_api
belong to? Justpytest-qt
? What if other packages come along that rely on the Qt binding being used?Is it really? They would just update their config variable with a prefix
pytest_
. Explicit is better than implicit.#412 won’t solve this problem. It’s still preferring PySide6 first when QtPy perfers PyQt5 first. There will still be a conflict when you have multiple Qt bindings installed as part of a test suite (as I did)
It’s really not that big of a change; there aren’t 5 ways to select the backend. You could even make it as simple as if
QT_API
is set, just use that, regardless of whether or notqtpy
is actually installed.I’m happy to add to #412 if you like.