Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add QT_VERSION environment variable #478

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ default unless you set the `QT_API` environment variable.
* `pyqt6` (to use PyQt6).
* `pyside6` (to use PySide6).

Alternatively the `QT_VERSION` environment variable can be used with the following values:

* `qt5` (to use PyQt5 or PySide2).
* `qt6` (to use PyQt6 or PySide6).

### Module aliases and constants

Expand Down
60 changes: 58 additions & 2 deletions qtpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,13 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
# Qt API environment variable name
QT_API = "QT_API"

# Qt VERSION environment variable name
QT_VERSION_ENV = "QT_VERSION"

# Names of the expected PyQt5 api
PYQT5_API = ["pyqt5"]

# Names of the expected PyQt6 api
PYQT6_API = ["pyqt6"]

# Names of the expected PySide2 api
Expand All @@ -151,6 +155,12 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
# Names of the expected PySide6 api
PYSIDE6_API = ["pyside6"]

# Names of the expected Qt5 version name
QT5_VERSIONS = ["qt5"]

# Names of the expected Qt6 version name
QT6_VERSIONS = ["qt6"]

# Minimum supported versions of Qt and the bindings
QT5_VERSION_MIN = PYQT5_VERSION_MIN = "5.9.0"
PYSIDE2_VERSION_MIN = "5.12.0"
Expand All @@ -160,8 +170,9 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
PYQT_VERSION_MIN = PYQT5_VERSION_MIN
PYSIDE_VERSION_MIN = PYSIDE2_VERSION_MIN

# Detecting if a binding was specified by the user
# Detecting if a binding or version was specified by the user
binding_specified = QT_API in os.environ
version_specified = QT_VERSION_ENV in os.environ

API_NAMES = {
"pyqt5": "PyQt5",
Expand All @@ -173,10 +184,42 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
initial_api = API
if API not in API_NAMES:
raise PythonQtValueError(
f"Specified QT_API={QT_API.lower()!r} is not in valid options: "
f"Specified QT_API={API.lower()!r} is not in valid options: "
f"{API_NAMES}",
)

VERSION_NAMES = {
"qt5": ("PyQt5", "PySide2"),
"qt6": ("PyQt6", "PySide6"),
}
requested_version = os.environ.get(QT_VERSION_ENV, "qt5").lower()
if requested_version not in VERSION_NAMES:
raise PythonQtValueError(
f"Specified QT_VERSION={requested_version.lower()!r} is not in valid options: "
f"{VERSION_NAMES}",
)

# If only QT_VERSION is given select API based on VERSION
if version_specified and not binding_specified:
if requested_version in QT5_VERSIONS:
API = PYQT5_API[0]
if requested_version in QT6_VERSIONS:
API = PYQT6_API[0]


# Warn if the QT_API and the QT_VERSION environment variables are incompatible
if (
API_NAMES[initial_api] not in VERSION_NAMES[requested_version]
and version_specified
and binding_specified
):
warnings.warn(
f"Specified QT_API={initial_api!r} and specified QT_VERSION={requested_version!r} are incompatible, QT_API will take precedence.",
PythonQtWarning,
stacklevel=2,
)


is_old_pyqt = is_pyqt46 = False
QT5 = PYQT5 = True
QT4 = QT6 = PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False
Expand Down Expand Up @@ -299,6 +342,19 @@ def __init__(self, *, missing_package=None, **superclass_kwargs):
stacklevel=2,
)

# Warn if the requested QT_VERSION could not be satisfied
if (
API_NAMES[API] not in VERSION_NAMES[requested_version]
and version_specified
and not binding_specified
):
warnings.warn(
f"No binding was found for the selected version {requested_version!r}; "
f"falling back to {API!r}",
PythonQtWarning,
stacklevel=2,
)


# Set display name of the Qt API
API_NAME = API_NAMES[API]
Expand Down
41 changes: 41 additions & 0 deletions qtpy/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,34 @@ def assert_pyqt6():
assert os.environ["QT_API"] == "pyqt6"


def assert_qt5():
try:
import PyQt5
except ImportError:
try:
import PySide2
except ImportError:
pytest.fail("No Qt5 API available.")
else:
assert_pyside2()
else:
assert_pyqt5()


def assert_qt6():
try:
import PyQt6
except ImportError:
try:
import PySide6
except ImportError:
pytest.fail("No Qt6 API available.")
else:
assert_pyside6()
else:
assert_pyqt6()


def test_qt_api():
"""
If QT_API is specified, we check that the correct Qt wrapper was used
Expand Down Expand Up @@ -140,3 +168,16 @@ def test_qt_api_environ(api):
raise AssertionError('QtPy imported despite bad QT_API')
"""
subprocess.check_call([sys.executable, "-Oc", cmd], env=env)


def test_qt_version():
"""
If QT_VERSION is specified, check that one of the correct Qt wrappers was used
"""

QT_VERSION = os.environ.get("QT_VERSION", "").lower()

if QT_VERSION == "qt5":
assert_qt5()
elif QT_VERSION == "qt6":
assert_qt6()
Comment on lines +173 to +183
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not totally sure but, I think we are not setting the QT_VERSION env var over the CI so for the moment this test is passing without doing any assert?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't think QT_VERSION is set in CI, ideally both possibilities should be checked. Do you happen to know how this works for the similar test test_qt_api?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think currently the test_qt_api is going through the else branch 😅 Checking the script that does the test setup (test.sh) I'm not seeing us setting up QT_API 🤔 Maybe we should try to check a way to setup QT_API (and QT_VERSION) for the tests (I think there is a USE_QT_API that can be set for test but I think we are not using it here either). What do you think @ccordoba12 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that's a good idea but I don't understand very well how to set up those env vars in our CIs.

Loading