8000 MNT: add warning if crossing Qt major versions · matplotlib/matplotlib@507bff4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 507bff4

Browse files
committed
MNT: add warning if crossing Qt major versions
1 parent 28bce7c commit 507bff4

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

lib/matplotlib/backends/backend_qt.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,28 @@ def _create_qApp():
114114
QtCore.Qt.AA_EnableHighDpiScaling)
115115
except AttributeError: # Only for Qt>=5.6, <6.
116116
pass
117+
118+
# Check to make sure a QApplication from a different major version
119+
# of Qt is not instantiated in the process
120+
if QT_API in {'PyQt6', 'PySide6'}:
121+
other_bindings = ('PyQt5', 'PySide2')
122+
elif QT_API in {'PyQt5', 'PySide2'}:
123+
other_bindings = ('PyQt6', 'PySide6')
124+
else:
125+
raise RuntimeError("Should never be here")
126+
127+
for binding in other_bindings:
128+
mod = sys.modules.get(f'{binding}.QtWidgets')
129+
if mod is not None and mod.QApplication.instance() is not None:
130+
other_core = sys.modules.get(f'{binding}.QtCore')
131+
_api.warn_external(
132+
f'Matplotlib is using {QT_API} which wraps '
133+
f'{QtCore.qVersion()} however an instantiated '
134+
f'QApplication from {binding} which wraps '
135+
f'{other_core.qVersion()} exists. Mixing Qt major '
136+
'versions may not work as expected.'
137+
)
138+
break
117139
try:
118140
QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy(
119141
QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)

lib/matplotlib/tests/test_backends_interactive.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from matplotlib import _c_internal_utils
1717
from matplotlib.testing import subprocess_run_helper as _run_helper
1818

19+
1920
# Minimal smoke-testing of the backends for which the dependencies are
2021
# PyPI-installable on CI. They are not available for all tested Python
2122
# versions so we don't fail on missing backends.
@@ -307,6 +308,50 @@ def test_qt5backends_uses_qt5():
307308
_run_helper(__name__, _implcore, timeout=_test_timeout)
308309

309310

311+
def _impl_test_cross_Qt_imports():
312+
import sys
313+
import importlib
314+
import pytest
315+
316+
_, host_binding, mpl_binding = sys.argv
317+
# import the mpl binding. This will force us to use that binding
318+
importlib.import_module(f'{mpl_binding}.QtCore')
319+
mpl_binding_qwidgets = importlib.import_module(f'{mpl_binding}.QtWidgets')
320+
import matplotlib.backends.backend_qt
321+
host_qwidgets = importlib.import_module(f'{host_binding}.QtWidgets')
322+
323+
host_app = host_qwidgets.QApplication(["mpl testing"])
324+
with pytest.warns(UserWarning, match="Mixing Qt major"):
325+
matplotlib.backends.backend_qt._create_qApp()
326+
327+
328+
def test_cross_Qt_imports():
329+
qt5_bindings = [
330+
dep for dep in ['PyQt5', 'PySide2']
331+
if importlib.util.find_spec(dep) is not None
332+
]
333+
qt6_bindings = [
334+
dep for dep in ['PyQt6', 'PySide6']
335+
if importlib.util.find_spec(dep) is not None
336+
]
337+
if len(qt5_bindings) == 0 or len(qt6_bindings) == 0:
338+
pytest.skip('need both QT6 and QT5 bindings')
339+
340+
for qt5 in qt5_bindings:
341+
for qt6 in qt6_bindings:
342+
for pair in ([qt5, qt6], [qt6, qt5]):
343+
try:
344+
_run_helper(__name__, _impl_test_cross_Qt_imports,
345+
*pair,
346+
timeout=_test_timeout)
347+
except subprocess.CalledProcessError as ex:
348+
# if segfauldt, carry on. We do try to warn the user they
349+
# are doing something that we do not expect to work
350+
if ex.returncode == -11:
351+
continue
352+
raise
353+
354+
310355
@pytest.mark.skipif('TF_BUILD' in os.environ,
311356
reason="this test fails an azure for unknown reasons")
312357
@pytest.mark.skipif(os.name == "nt", reason="Cannot send SIGINT on Windows.")

0 commit comments

Comments
 (0)
0