diff --git a/qtpy/__init__.py b/qtpy/__init__.py index f4fd0b6d..d0ce4449 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -72,6 +72,10 @@ class PythonQtWarning(Warning): """Warning if some features are not implemented in a binding.""" +class PythonQtValueError(ValueError): + """Error raised if an invalid QT_API is specified.""" + + # Qt API environment variable name QT_API = 'QT_API' @@ -98,14 +102,14 @@ class PythonQtWarning(Warning): # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ -# Setting a default value for QT_API -os.environ.setdefault(QT_API, 'pyqt5') - API_NAMES = {'pyqt5': 'PyQt5', 'pyqt6': 'PyQt6', - 'pyside2':'PySide2', 'pyside6': 'PySide6'} -API = os.environ[QT_API].lower() + 'pyside2': 'PySide2', 'pyside6': 'PySide6'} +API = os.environ.get(QT_API, 'pyqt5').lower() initial_api = API -assert API in API_NAMES +if API not in API_NAMES: + raise PythonQtValueError( + f'Specified QT_API={repr(QT_API.lower())} is not in valid options: ' + f'{API_NAMES}') is_old_pyqt = is_pyqt46 = False QT5 = PYQT5 = True @@ -150,7 +154,9 @@ class PythonQtWarning(Warning): del macos_version except ImportError: - API = os.environ['QT_API'] = 'pyqt6' + API = 'pyqt6' + else: + os.environ[QT_API] = API if API in PYQT6_API: try: @@ -161,7 +167,9 @@ class PythonQtWarning(Warning): QT6 = PYQT6 = True except ImportError: - API = os.environ['QT_API'] = 'pyside2' + API = 'pyside2' + else: + os.environ[QT_API] = API if API in PYSIDE2_API: @@ -183,7 +191,9 @@ class PythonQtWarning(Warning): del macos_version except ImportError: - API = os.environ['QT_API'] = 'pyside6' + API = 'pyside6' + else: + os.environ[QT_API] = API if API in PYSIDE6_API: try: @@ -194,7 +204,9 @@ class PythonQtWarning(Warning): QT6 = PYSIDE6 = True except ImportError: - API = os.environ['QT_API'] = 'pyqt5' + API = 'pyqt5' + else: + os.environ[QT_API] = API # If a correct API name is passed to QT_API and it could not be found, diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index ea1fd190..a24f664f 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -21,8 +21,9 @@ def pytest_report_header(config): versions += 'PyQt6: ' try: - from PyQt6 import Qt - versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" + from PyQt6 import QtCore + versions += \ + f"PyQt: {QtCore.PYQT_VERSION_STR} - Qt: {QtCore.QT_VERSION_STR}" except ImportError: versions += 'not installed' except AttributeError: diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 86aeb5d2..000ddf10 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -1,6 +1,11 @@ import os +import sys +import subprocess + +import pytest + +from qtpy import QtCore, QtGui, QtWidgets, API_NAMES, PythonQtValueError -from qtpy import QtCore, QtGui, QtWidgets try: # removed in qt 6.0 from qtpy import QtWebEngineWidgets @@ -16,6 +21,7 @@ def assert_pyside2(): assert QtGui.QPainter is PySide2.QtGui.QPainter assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage + assert os.environ['QT_API'] == 'pyside2' def assert_pyside6(): """ @@ -27,6 +33,7 @@ def assert_pyside6(): assert QtWidgets.QWidget is PySide6.QtWidgets.QWidget # Only valid for qt>=6.2 # assert QtWebEngineWidgets.QWebEnginePage is PySide6.QtWebEngineCore.QWebEnginePage + assert os.environ['QT_API'] == 'pyside6' def assert_pyqt5(): """ @@ -40,6 +47,7 @@ def assert_pyqt5(): assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebEngineWidgets.QWebEnginePage else: assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebKitWidgets.QWebPage + assert os.environ['QT_API'] == 'pyqt5' def assert_pyqt6(): """ @@ -49,6 +57,7 @@ def assert_pyqt6(): assert QtCore.QEvent is PyQt6.QtCore.QEvent assert QtGui.QPainter is PyQt6.QtGui.QPainter assert QtWidgets.QWidget is PyQt6.QtWidgets.QWidget + assert os.environ['QT_API'] == 'pyqt6' def test_qt_api(): @@ -83,3 +92,39 @@ def test_qt_api(): assert_pyqt6() else: assert_pyqt5() + + +@pytest.mark.parametrize('api', API_NAMES.values()) +def test_qt_api_environ(api): + """ + If no QT_API is specified but some Qt is imported, ensure QT_API is set properly. + """ + mod = f'{api}.QtCore' + pytest.importorskip(mod, reason=f'Requires {api}') + # clean env + env = os.environ.copy() + for key in ('QT_API', 'USE_QT_API'): + if key in env: + del env[key] + cmd = f""" +import {mod} +from qtpy import API +import os +print(API) +print(os.environ['QT_API']) +""" + output = subprocess.check_output([sys.executable, '-c', cmd], env=env) + got_api, env_qt_api = output.strip().decode('utf-8').splitlines() + assert got_api == api.lower() + assert env_qt_api == api.lower() + # Also ensure we raise a nice error + env['QT_API'] = 'bad' + cmd = """ +try: + import qtpy +except ValueError as exc: + assert 'Specified QT_API' in str(exc), str(exc) +else: + raise AssertionError('QtPy imported despite bad QT_API') +""" + subprocess.check_call([sys.executable, '-Oc', cmd], env=env)