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

hooks: qt: update hooks for PySide6/PyQt6 v6.5.0 #7549

Merged
merged 9 commits into from Apr 10, 2023
14 changes: 14 additions & 0 deletions PyInstaller/hooks/hook-PyQt6.QtSpatialAudio.py
@@ -0,0 +1,14 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------

from PyInstaller.utils.hooks.qt import add_qt6_dependencies

hiddenimports, binaries, datas = add_qt6_dependencies(__file__)
14 changes: 14 additions & 0 deletions PyInstaller/hooks/hook-PyQt6.QtTextToSpeech.py
@@ -0,0 +1,14 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------

from PyInstaller.utils.hooks.qt import add_qt6_dependencies

hiddenimports, binaries, datas = add_qt6_dependencies(__file__)
14 changes: 14 additions & 0 deletions PyInstaller/hooks/hook-PySide6.QtLocation.py
@@ -0,0 +1,14 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------

from PyInstaller.utils.hooks.qt import add_qt6_dependencies

hiddenimports, binaries, datas = add_qt6_dependencies(__file__)
14 changes: 14 additions & 0 deletions PyInstaller/hooks/hook-PySide6.QtSerialBus.py
@@ -0,0 +1,14 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------

from PyInstaller.utils.hooks.qt import add_qt6_dependencies

hiddenimports, binaries, datas = add_qt6_dependencies(__file__)
14 changes: 14 additions & 0 deletions PyInstaller/hooks/hook-PySide6.QtTextToSpeech.py
@@ -0,0 +1,14 @@
#-----------------------------------------------------------------------------
# Copyright (c) 2013-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------

from PyInstaller.utils.hooks.qt import add_qt6_dependencies

hiddenimports, binaries, datas = add_qt6_dependencies(__file__)
6 changes: 4 additions & 2 deletions PyInstaller/utils/hooks/qt/__init__.py
Expand Up @@ -195,6 +195,7 @@ def _load_qt_info(self):
@isolated.decorate
def _read_qt_library_info(package):
import os
import sys
import importlib

# Import the Qt-based package
Expand All @@ -204,7 +205,7 @@ def _read_qt_library_info(package):
QCoreApplication = QtCore.QCoreApplication

# QLibraryInfo is not always valid until a QCoreApplication is instantiated.
app = QCoreApplication([]) # noqa: F841
app = QCoreApplication(sys.argv) # noqa: F841

# Qt6 deprecated QLibraryInfo.location() in favor of QLibraryInfo.path(), and
# QLibraryInfo.LibraryLocation enum was replaced by QLibraryInfo.LibraryPath.
Expand Down Expand Up @@ -586,6 +587,7 @@ def collect_qtnetwork_files(self):
# Check if QtNetwork supports SSL
@isolated.decorate
def _ssl_enabled(package):
import sys
import importlib

# Import the Qt-based package
Expand All @@ -597,7 +599,7 @@ def _ssl_enabled(package):
QSslSocket = QtNetwork.QSslSocket

# Instantiate QCoreApplication to suppress warnings
app = QCoreApplication([]) # noqa: F841
app = QCoreApplication(sys.argv) # noqa: F841

return QSslSocket.supportsSsl()

Expand Down
16 changes: 8 additions & 8 deletions PyInstaller/utils/hooks/qt/_modules_info.py
Expand Up @@ -249,20 +249,19 @@ def __init__(self, module, shared_lib=None, translation=None, plugins=None, bind
_QtModuleDef("QtHttpServer", shared_lib="HttpServer", bindings=["PySide6"]),

# *** qt/qtlocation ***
# Qt5-only Qt module.
# QtLocation was reintroduced in Qt6 v6.5.0.
_QtModuleDef(
"QtLocation",
shared_lib="Location",
translation="qtlocation",
plugins=["geoservices"],
bindings=["PySide2", "PyQt5"]
bindings=["PySide2", "PyQt5", "PySide6"]
),
_QtModuleDef(
"QtPositioning",
shared_lib="Positioning",
translation="qtlocation",
plugins=["position"],
bindings=["PySide2", "PyQt5"]
),

# *** qt/qtmacextras ***
Expand Down Expand Up @@ -291,8 +290,8 @@ def __init__(self, module, shared_lib=None, translation=None, plugins=None, bind
bindings=["PySide6", "PyQt6"]
),
_QtModuleDef("QtMultimediaWidgets", shared_lib="MultimediaWidgets"),
# Qt6-only Qt module; python module is available in PySide6 >= 6.4.0
_QtModuleDef("QtSpatialAudio", shared_lib="SpatialAudio", bindings=["PySide6"]),
# Qt6-only Qt module; python module is available in PySide6 >= 6.4.0 and PyQt6 >= 6.5.0
_QtModuleDef("QtSpatialAudio", shared_lib="SpatialAudio", bindings=["PySide6", "PyQt6"]),

# *** qt/qtnetworkauth ***
# QtNetworkAuth python module is available in all bindings but PySide2.
Expand Down Expand Up @@ -350,16 +349,17 @@ def __init__(self, module, shared_lib=None, translation=None, plugins=None, bind

# *** qt/qtserialbus ***
# No python module; shared library -> plugins association entry.
_QtModuleDef(None, shared_lib="SerialBus", plugins=["canbus"]),
# PySide6 6.5.0 introduced python module.
_QtModuleDef(None, shared_lib="SerialBus", plugins=["canbus"], bindings=["!PySide6"]),
_QtModuleDef("QtSerialBus", shared_lib="SerialBus", plugins=["canbus"], bindings=["PySide6"]),

# *** qt/qtsvg ***
_QtModuleDef("QtSvg", shared_lib="Svg"),
# Qt6-only Qt module.
_QtModuleDef("QtSvgWidgets", shared_lib="SvgWidgets", bindings=["PySide6", "PyQt6"]),

# *** qt/qtspeech ***
# Qt5-only Qt module.
_QtModuleDef("QtTextToSpeech", shared_lib="TextToSpeech"),
_QtModuleDef("QtTextToSpeech", shared_lib="TextToSpeech", plugins=["texttospeech"]),

# *** qt/qttools ***
# QtDesigner python module is available in all bindings but PySide2.
Expand Down
2 changes: 2 additions & 0 deletions news/7549.hooks.1.rst
@@ -0,0 +1,2 @@
Add hook for ``PyQt6.QtTextToSpeech`` module, which was added in
``PyQt6`` 6.4 series.
2 changes: 2 additions & 0 deletions news/7549.hooks.2.rst
@@ -0,0 +1,2 @@
Add hook for ``PyQt6.QtSpatialAudio`` module, which was added in
``PyQt6`` 6.5.0.
3 changes: 3 additions & 0 deletions news/7549.hooks.rst
@@ -0,0 +1,3 @@
Extend ``PySide6`` hooks for ``PySide6`` 6.5.0 compatibility: add hooks
for ``QtLocation``, ``QtTextToSpeech``, and ``QtSerialBus`` modules
that were introduced in ``PySide`` 6.5.0.
48 changes: 34 additions & 14 deletions tests/functional/test_qt.py
Expand Up @@ -18,7 +18,7 @@
from PyInstaller.compat import is_win, is_darwin
from PyInstaller.utils.hooks import is_module_satisfies, can_import_module
from PyInstaller.utils.hooks.qt import get_qt_library_info
from PyInstaller.utils.tests import importorskip, requires, xfail, skipif
from PyInstaller.utils.tests import importorskip, requires, skipif

PYQT5_NEED_OPENGL = pytest.mark.skipif(
is_module_satisfies('PyQt5 <= 5.10.1'),
Expand Down Expand Up @@ -144,18 +144,28 @@ def test_Qt_QtQml(pyi_builder, QtPyLib):
)


@pytest.mark.parametrize(
'QtPyLib', [
qt_param('PyQt5'),
qt_param('PyQt6'),
qt_param('PySide2', marks=xfail(is_win, reason='PySide2 wheels on Windows do not include SSL DLLs.')),
qt_param('PySide6', marks=xfail(is_win, reason='PySide6 wheels on Windows do not include SSL DLLs.')),
]
)
@QtPyLibs
def test_Qt_QtNetwork_SSL_support(pyi_builder, QtPyLib):
# Skip the test if QtNetwork does not support SSL (e.g., due to lack of compatible OpenSSL shared library on the
# test system).
@isolated.decorate
def check_ssl_support(package):
import sys
import importlib
QtCore = importlib.import_module('.QtCore', package)
QtNetwork = importlib.import_module('.QtNetwork', package)
app = QtCore.QCoreApplication(sys.argv) # noqa: F841
return QtNetwork.QSslSocket.supportsSsl()

if not check_ssl_support(QtPyLib):
pytest.skip('QtNetwork does not support SSL on this platform.')

pyi_builder.test_source(
"""
import sys
from {0}.QtCore import QCoreApplication
from {0}.QtNetwork import QSslSocket
app = QCoreApplication(sys.argv)
assert QSslSocket.supportsSsl()
""".format(QtPyLib), **USE_WINDOWED_KWARG
)
Expand All @@ -165,11 +175,12 @@ def test_Qt_QtNetwork_SSL_support(pyi_builder, QtPyLib):
def test_Qt_QTranslate(pyi_builder, QtPyLib):
pyi_builder.test_source(
"""
import sys
from {0}.QtWidgets import QApplication
from {0}.QtCore import QTranslator, QLocale, QLibraryInfo

# Initialize Qt default translations
app = QApplication([])
app = QApplication(sys.argv)
translator = QTranslator()
locale = QLocale('de_DE')
if hasattr(QLibraryInfo, 'path'):
Expand Down Expand Up @@ -209,7 +220,7 @@ def test_Qt_Ui_file(tmpdir, pyi_builder, data_dir, QtPyLib):
is_qt6 = '{0}' in {{'PyQt6', 'PySide6'}}
is_pyqt = '{0}' in {{'PyQt5', 'PyQt6'}}

app = QApplication([])
app = QApplication(sys.argv)

# In Qt6, QtQuick supports multiple render APIs and automatically selects one.
# However, QtQuickWidgets.QQuickWidget that is used by the test UI file supports only OpenGL,
Expand Down Expand Up @@ -303,7 +314,7 @@ def _test_Qt_QtWebEngineWidgets(pyi_builder, qt_flavor):
</html>
'''

app = QApplication([])
app = QApplication(sys.argv)

class JSResultTester:

Expand Down Expand Up @@ -373,7 +384,7 @@ def _test_Qt_QtWebEngineQuick(pyi_builder, qt_flavor):
from {0}.QtWebEngine import QtWebEngine as QtWebEngineQuick
QtWebEngineQuick.initialize()

app = QGuiApplication([])
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.loadData(b'''
import QtQuick 2.0
Expand Down Expand Up @@ -459,11 +470,19 @@ def test_Qt_QtWebEngineQuick_PyQt6(pyi_builder):


@requires('PySide6 >= 6.2.2')
@pytest.mark.skipif(
is_module_satisfies('PySide6 == 6.5.0') and is_win,
reason='PySide6 6.5.0 PyPI wheels for Windows are missing opengl32sw.dll.'
)
def test_Qt_QtWebEngineWidgets_PySide6(pyi_builder):
_test_Qt_QtWebEngineWidgets(pyi_builder, 'PySide6')


@requires('PySide6 >= 6.2.2')
@pytest.mark.skipif(
is_module_satisfies('PySide6 == 6.5.0') and is_win,
reason='PySide6 6.5.0 PyPI wheels for Windows are missing opengl32sw.dll.'
)
def test_Qt_QtWebEngineQuick_PySide6(pyi_builder):
_test_Qt_QtWebEngineQuick(pyi_builder, 'PySide6')

Expand Down Expand Up @@ -496,10 +515,11 @@ def test_Qt_QtMultimedia_player_init(pyi_builder, QtPyLib):
def test_Qt_QtMultimedia_with_true_property(pyi_builder, QtPyLib):
pyi_builder.test_source(
"""
import sys
from {0} import QtCore, QtMultimedia
from __feature__ import true_property

app = QtCore.QCoreApplication()
app = QtCore.QCoreApplication(sys.argv)
""".format(QtPyLib), **USE_WINDOWED_KWARG
)

Expand Down
14 changes: 7 additions & 7 deletions tests/requirements-libraries.txt
Expand Up @@ -29,7 +29,7 @@ pygments==2.14.0
PyGObject==3.44.1; sys_platform == 'linux'
# Current PySide2 wheels explicitly require python < 3.11
PySide2==5.15.2.1; python_version < '3.11'
PySide6==6.4.3
PySide6==6.5.0
# PyQt5 and add-on packages
PyQt5==5.15.9
PyQt3D==5.15.6
Expand All @@ -40,13 +40,13 @@ PyQtPurchasing==5.15.5
QScintilla==2.13.4
PyQtWebEngine==5.15.6
# PyQt6 and add-on packages
PyQt6==6.4.2
PyQt6-3D==6.4.0
PyQt6-Charts==6.4.0
PyQt6-DataVisualization==6.4.0
PyQt6-NetworkAuth==6.4.0
PyQt6==6.5.0
PyQt6-3D==6.5.0
PyQt6-Charts==6.5.0
PyQt6-DataVisualization==6.5.0
PyQt6-NetworkAuth==6.5.0
PyQt6-QScintilla==2.13.4
PyQt6-WebEngine==6.4.0
PyQt6-WebEngine==6.5.0
python-dateutil==2.8.2
pytz==2023.3
requests==2.28.2
Expand Down