Files
aufbau2csv/venv3_12/Lib/site-packages/cx_Freeze/hooks/_qthooks.py

439 lines
17 KiB
Python

"""A collection of functions which are the base to hooks for PyQt5, PyQt6,
PySide2 and PySide6.
"""
from __future__ import annotations
import json
import os
import sys
from contextlib import suppress
from functools import lru_cache
from pathlib import Path
from typing import TYPE_CHECKING
from cx_Freeze._compat import IS_CONDA, IS_MACOS, IS_MINGW, IS_WINDOWS
if TYPE_CHECKING:
from cx_Freeze.finder import ModuleFinder
from cx_Freeze.module import Module
def _qt_implementation(module: Module) -> str:
"""Helper function to get the name of the Qt implementation."""
return module.name.split(".")[0]
@lru_cache(maxsize=None)
def _qt_libraryinfo_paths(name: str) -> dict[str, tuple[Path, Path]]:
"""Cache the QtCore library paths."""
try:
qtcore = __import__(name, fromlist=["QtCore"]).QtCore
except RuntimeError:
print("WARNING: Tried to load multiple incompatible Qt ", end="")
print("wrappers. Some incorrect files may be copied.")
return {}
# get paths from QLibraryInfo
source_paths: dict[str, Path] = {}
lib = qtcore.QLibraryInfo
major_version = lib.version().majorVersion()
if major_version == 6:
if hasattr(lib.LibraryPath, "__members__"):
for key, value in lib.LibraryPath.__members__.items():
source_paths[key] = Path(lib.path(value))
else:
for key, value in lib.__dict__.items():
if isinstance(value, lib.LibraryPath):
source_paths[key] = Path(lib.path(value))
else:
for key, value in lib.__dict__.items():
if isinstance(value, (lib.LibraryLocation, int)):
source_paths[key] = Path(lib.location(value))
qt_root_dir = Path(qtcore.__file__).parent
# if QLibraryInfo has incomplete information
if not source_paths.get("PluginsPath"):
# Qt Plugins can be in a plugins directory next to the Qt libraries
plugins_path = qt_root_dir / "plugins"
if not plugins_path.exists():
plugins_path = qt_root_dir / "Qt5" / "plugins" # PyQt5 5.15.4
# or in a special location in conda-forge
if not plugins_path.exists():
plugins_path = Path(sys.base_prefix, "Library", "plugins")
# default location
if not plugins_path.exists():
plugins_path = qt_root_dir / "Qt" / "plugins"
source_paths["PluginsPath"] = plugins_path
source_paths.setdefault("PrefixPath", source_paths["PluginsPath"].parent)
prefix_path = source_paths["PrefixPath"]
source_paths.setdefault("DataPath", prefix_path)
source_paths.setdefault("LibrariesPath", prefix_path / "lib")
source_paths.setdefault("SettingsPath", ".")
if name in ("PySide2", "PySide6") and IS_WINDOWS and not IS_CONDA:
source_paths["BinariesPath"] = prefix_path
source_paths["LibraryExecutablesPath"] = prefix_path
# set the target paths
data: dict[str, tuple[Path, Path]] = {}
target_base = Path("lib", name)
with suppress(ValueError):
target_base = target_base / prefix_path.relative_to(qt_root_dir)
if name == "PyQt5" and prefix_path.name != "Qt5":
# conda pyqt
target_base = target_base / "Qt5"
# set some defaults or use relative path
for key, source in source_paths.items():
if key == "SettingsPath": # Check for SettingsPath first
target = Path("Contents/Resources" if IS_MACOS else ".")
elif name in ("PySide2", "PySide6") and IS_WINDOWS and not IS_CONDA:
target = target_base / source.relative_to(prefix_path)
elif key in ("ArchDataPath", "DataPath", "PrefixPath"):
target = target_base
elif key == "BinariesPath":
target = target_base / "bin"
elif key == "LibrariesPath":
target = target_base / "lib"
elif key == "LibraryExecutablesPath":
target = target_base / (
"bin" if IS_WINDOWS or IS_MINGW else "libexec"
)
elif key == "PluginsPath":
target = target_base / "plugins"
elif key == "TranslationsPath":
target = target_base / "translations"
elif source == Path("."):
target = target_base
else:
target = target_base / source.relative_to(prefix_path)
data[key] = source.resolve(), target
# debug
if os.environ.get("QT_DEBUG"):
print("QLibraryInfo:")
for key, (source, target) in sorted(data.items()):
print(" ", key, source, "->", target)
return data
def get_qt_paths(name: str, variable: str) -> tuple[Path, Path]:
"""Helper function to get the source and target path of Qt variable."""
libraryinfo_paths = _qt_libraryinfo_paths(name)
source_path, target_path = libraryinfo_paths[variable]
return (source_path, target_path)
def _get_qt_files(
name: str, variable: str, arg: str
) -> list[tuple[Path, Path]]:
"""Helper function to get Qt plugins, resources, translations, etc."""
source_path, target_path = get_qt_paths(name, variable)
if source_path.joinpath(arg).is_dir():
source_path = source_path / arg
target_path = target_path / arg
pattern = "*"
else:
pattern = arg
return [
(source, target_path / source.name)
for source in source_path.glob(pattern)
]
def get_qt_plugins_paths(name: str, plugins: str) -> list[tuple[Path, Path]]:
"""Helper function to get a list of source and target paths of Qt plugins,
indicated to be used in include_files.
"""
return _get_qt_files(name, "PluginsPath", plugins)
def copy_qt_files(
finder: ModuleFinder, name: str, variable: str, arg: str
) -> None:
"""Helper function to copy Qt plugins, resources, translations, etc."""
for source_path, target_path in _get_qt_files(name, variable, arg):
finder.include_files(source_path, target_path)
def load_qt_phonon(finder: ModuleFinder, module: Module) -> None:
"""In Windows, phonon5.dll requires an additional dll phonon_ds94.dll to
be present in the build directory inside a folder phonon_backend.
"""
if IS_WINDOWS or IS_MINGW:
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "phonon_backend")
def load_qt_qt3dinput(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "3dinputdevices")
def load_qt_qt3drender(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "sceneparsers")
copy_qt_files(finder, name, "PluginsPath", "geometryloaders")
copy_qt_files(finder, name, "PluginsPath", "renderplugins")
copy_qt_files(finder, name, "PluginsPath", "renderers")
def load_qt_qtbluetooth(finder: ModuleFinder, module: Module) -> None:
"""Include translations for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "TranslationsPath", "qtconnectivity_*.qm")
def load_qt_qtcore(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
variable = "BinariesPath" if IS_WINDOWS else "LibrariesPath"
for source, target in _get_qt_files(name, variable, "*"):
finder.lib_files[source] = target
def load_qt_qtdesigner(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "designer")
copy_qt_files(finder, name, "TranslationsPath", "designer_*.qm")
def load_qt_qtgui(finder: ModuleFinder, module: Module) -> None:
"""There is a chance that QtGui will use some image formats, then, add the
image format plugins.
"""
name = _qt_implementation(module)
for plugin_name in (
"accessiblebridge",
"platforms",
"platforms/darwin",
"xcbglintegrations",
"platformthemes",
"platforminputcontexts",
"generic",
"iconengines",
"imageformats",
"egldeviceintegrations",
"wayland-graphics-integration-client",
"wayland-inputdevice-integration",
"wayland-decoration-client",
"wayland-shell-integration",
"wayland-graphics-integration-server",
"wayland-hardware-layer-integration",
):
copy_qt_files(finder, name, "PluginsPath", plugin_name)
copy_qt_files(finder, name, "TranslationsPath", "qt_??.qm")
copy_qt_files(finder, name, "TranslationsPath", "qt_??_??.qm")
copy_qt_files(finder, name, "TranslationsPath", "qtbase_*.qm")
# old names?
copy_qt_files(finder, name, "PluginsPath", "accessible")
copy_qt_files(finder, name, "PluginsPath", "pictureformats")
def load_qt_qtlocation(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "geoservices")
copy_qt_files(finder, name, "TranslationsPath", "qtlocation_*.qm")
def load_qt_qtmultimedia(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "multimedia")
copy_qt_files(finder, name, "TranslationsPath", "qtmultimedia_*.qm")
# ?
copy_qt_files(finder, name, "PluginsPath", "audio")
copy_qt_files(finder, name, "PluginsPath", "mediaservice")
copy_qt_files(finder, name, "PluginsPath", "playlistformats")
copy_qt_files(finder, name, "PluginsPath", "resourcepolicy")
copy_qt_files(finder, name, "PluginsPath", "video")
def load_qt_qtnetwork(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "networkaccess")
copy_qt_files(finder, name, "PluginsPath", "networkinformation")
copy_qt_files(finder, name, "PluginsPath", "tls")
copy_qt_files(finder, name, "PluginsPath", "bearer") # ?
def load_qt_qtpositioning(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "position")
def load_qt_qtprintsupport(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "printsupport")
if IS_WINDOWS:
copy_qt_files(finder, name, "PrefixPath", "Qt?Pdf*.dll")
def load_qt_qtqml(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "qmllint") # pyqt6
copy_qt_files(finder, name, "PluginsPath", "qmltooling")
copy_qt_files(finder, name, "QmlImportsPath", "*")
finder.include_module(f"{name}.QtQuick")
def load_qt_qtquick(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "scenegraph")
def load_qt_qtquick3d(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "assetimporters")
def load_qt_qtscript(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "script")
def load_qt_qtscxml(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "scxmldatamodel")
def load_qt_qtsensors(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "sensors")
copy_qt_files(finder, name, "PluginsPath", "sensorgestures") # pyqt6
def load_qt_qtserialbus(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "canbus")
def load_qt_qtserialport(finder: ModuleFinder, module: Module) -> None:
"""Include translations for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "TranslationsPath", "qtserialport_*.qm")
def load_qt_qtsql(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "sqldrivers")
def load_qt_qttexttospeech(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "texttospeech")
def load_qt_qtvirtualkeyboard(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "virtualkeyboard")
def load_qt_qtwebenginecore(finder: ModuleFinder, module: Module) -> None:
"""Include module dependency and QtWebEngineProcess files."""
name = _qt_implementation(module)
distribution = module.parent.distribution
environment = (distribution and distribution.installer) or "pip"
if IS_WINDOWS:
for filename in (
"QtWebEngineProcess.exe",
"d3dcompiler_47.dll",
"libEGL.dll",
"libGLESv2.dll",
"opengl32sw.dll",
):
# pyside2 - only QtWebEngineProcess is in LibraryExecutablesPath
# pyside6 - like pyside2, but the two lib*.dll are missing
copy_qt_files(finder, name, "ArchDataPath", filename)
# pyqt5 - all files listed in LibraryExecutablesPath
copy_qt_files(finder, name, "LibraryExecutablesPath", filename)
elif IS_MACOS and environment == "pip": # pip wheels for macOS
source_path, _ = get_qt_paths(name, "LibrariesPath")
source_framework = source_path / "QtWebEngineCore.framework"
# QtWebEngineProcess
finder.include_files(source_framework / "Helpers", "share")
# QtWebEngineCore resources
source_resources = source_framework / "Resources"
if source_resources.exists():
target_datapath = get_qt_paths(name, "DataPath")[1]
for resource in source_resources.iterdir():
if resource.name == "Info.plist":
continue
if resource.name == "qtwebengine_locales":
target = get_qt_paths(name, "TranslationsPath")[1]
else:
target = target_datapath / "resources"
finder.include_files(
resource,
target / resource.name,
copy_dependent_files=False,
)
else:
# wheels for Linux or conda-forge Linux and macOS
copy_qt_files(
finder, name, "LibraryExecutablesPath", "QtWebEngineProcess"
)
if environment == "conda": # conda-forge Linux and macOS
prefix = Path(sys.prefix)
conda_meta = prefix / "conda-meta"
pkg = next(conda_meta.glob("nss-*.json"))
files = json.loads(pkg.read_text(encoding="utf_8"))["files"]
for file in files:
source = prefix / file
if source.match("lib*.so") or source.match("lib*.dylib"):
finder.include_files(source, f"lib/{source.name}")
else:
copy_qt_files(finder, name, "LibraryExecutablesPath", "libnss*.*")
copy_qt_files(finder, name, "DataPath", "resources")
copy_qt_files(finder, name, "TranslationsPath", "qtwebengine_*.qm")
copy_qt_files(finder, name, "TranslationsPath", "qtwebengine_locales")
def load_qt_qtwebenginewidgets(finder: ModuleFinder, module: Module) -> None:
"""Include data and plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "LibrariesPath", "*WebEngineWidgets.*")
copy_qt_files(finder, name, "PluginsPath", "webview")
def load_qt_qtwebsockets(finder: ModuleFinder, module: Module) -> None:
"""Include translations for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "TranslationsPath", "qtwebsockets_*.qm")
def load_qt_qtwidgets(finder: ModuleFinder, module: Module) -> None:
"""Include plugins for the module."""
name = _qt_implementation(module)
copy_qt_files(finder, name, "PluginsPath", "styles")
def load_qt_uic(finder: ModuleFinder, module: Module) -> None:
"""The uic module makes use of "plugins" that need to be read directly and
cannot be frozen; the PyQt5.QtWebKit and PyQt5.QtNetwork modules are
also implicity loaded.
"""
name = _qt_implementation(module)
source_dir = module.path[0] / "widget-plugins"
if source_dir.exists():
finder.include_files(source_dir, f"{name}.uic.widget-plugins")