diff options
Diffstat (limited to 'sources/pyside-tools/deploy_lib/deploy_util.py')
-rw-r--r-- | sources/pyside-tools/deploy_lib/deploy_util.py | 153 |
1 files changed, 108 insertions, 45 deletions
diff --git a/sources/pyside-tools/deploy_lib/deploy_util.py b/sources/pyside-tools/deploy_lib/deploy_util.py index a8ca58611..b20c9c8cb 100644 --- a/sources/pyside-tools/deploy_lib/deploy_util.py +++ b/sources/pyside-tools/deploy_lib/deploy_util.py @@ -1,14 +1,18 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +import ast +import re +import os +import warnings import logging import shutil import sys from pathlib import Path +from typing import List -from . import EXE_FORMAT +from . import EXE_FORMAT, IMPORT_WARNING_PYSIDE from .config import Config -from .python_helper import PythonExecutable def config_option_exists(): @@ -62,49 +66,6 @@ def create_config_file(dry_run: bool = False, config_file: Path = None, main_fil return config_file -def setup_python(dry_run: bool, force: bool, init: bool): - """ - Sets up Python venv for deployment, and return a wrapper around the venv environment - """ - python = None - response = "yes" - # checking if inside virtual environment - if not PythonExecutable.is_venv() and not force and not dry_run and not init: - response = input(("You are not using a virtual environment. pyside6-deploy needs to install" - " a few Python packages for deployment to work seamlessly. \n" - "Proceed? [Y/n]")) - - if response.lower() in ["no", "n"]: - print("[DEPLOY] Exiting ...") - sys.exit(0) - - python = PythonExecutable(dry_run=dry_run) - logging.info(f"[DEPLOY] Using python at {sys.executable}") - - return python - - -def install_python_dependencies(config: Config, python: PythonExecutable, init: bool, - packages: str, is_android: bool = False): - """ - Installs the python package dependencies for the target deployment platform - """ - packages = config.get_value("python", packages).split(",") - if not init: - # install packages needed for deployment - logging.info("[DEPLOY] Installing dependencies") - python.install(packages=packages) - # nuitka requires patchelf to make patchelf rpath changes for some Qt files - if sys.platform.startswith("linux") and not is_android: - python.install(packages=["patchelf"]) - elif is_android: - # install only buildozer - logging.info("[DEPLOY] Installing buildozer") - buildozer_package_with_version = ([package for package in packages - if package.startswith("buildozer")]) - python.install(packages=list(buildozer_package_with_version)) - - def finalize(config: Config): """ Copy the executable into the final location @@ -115,3 +76,105 @@ def finalize(config: Config): shutil.copy(generated_exec_path, config.exe_dir) print("[DEPLOY] Executed file created in " f"{str(config.exe_dir / (config.source_file.stem + EXE_FORMAT))}") + + +def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None, + project_data=None): + """ + Searches all the python files in the project to find all the PySide modules used by + the application. + """ + all_modules = set() + mod_pattern = re.compile("PySide6.Qt(?P<mod_name>.*)") + + def pyside_imports(py_file: Path): + modules = [] + contents = py_file.read_text(encoding="utf-8") + try: + tree = ast.parse(contents) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom): + main_mod_name = node.module + if main_mod_name.startswith("PySide6"): + if main_mod_name == "PySide6": + # considers 'from PySide6 import QtCore' + for imported_module in node.names: + full_mod_name = imported_module.name + if full_mod_name.startswith("Qt"): + modules.append(full_mod_name[2:]) + continue + + # considers 'from PySide6.QtCore import Qt' + match = mod_pattern.search(main_mod_name) + if match: + mod_name = match.group("mod_name") + modules.append(mod_name) + else: + logging.warning(( + f"[DEPLOY] Unable to find module name from{ast.dump(node)}")) + + if isinstance(node, ast.Import): + for imported_module in node.names: + full_mod_name = imported_module.name + if full_mod_name == "PySide6": + logging.warning(IMPORT_WARNING_PYSIDE.format(str(py_file))) + except Exception as e: + raise RuntimeError(f"[DEPLOY] Finding module import failed on file {str(py_file)} with " + f"error {e}") + + return set(modules) + + py_candidates = [] + ignore_dirs = ["__pycache__", "env", "venv", "deployment"] + + if project_data: + py_candidates = project_data.python_files + ui_candidates = project_data.ui_files + qrc_candidates = project_data.qrc_files + ui_py_candidates = None + qrc_ui_candidates = None + + if ui_candidates: + ui_py_candidates = [(file.parent / f"ui_{file.stem}.py") for file in ui_candidates + if (file.parent / f"ui_{file.stem}.py").exists()] + + if len(ui_py_candidates) != len(ui_candidates): + warnings.warn("[DEPLOY] The number of uic files and their corresponding Python" + " files don't match.", category=RuntimeWarning) + + py_candidates.extend(ui_py_candidates) + + if qrc_candidates: + qrc_ui_candidates = [(file.parent / f"rc_{file.stem}.py") for file in qrc_candidates + if (file.parent / f"rc_{file.stem}.py").exists()] + + if len(qrc_ui_candidates) != len(qrc_candidates): + warnings.warn("[DEPLOY] The number of qrc files and their corresponding Python" + " files don't match.", category=RuntimeWarning) + + py_candidates.extend(qrc_ui_candidates) + + for py_candidate in py_candidates: + all_modules = all_modules.union(pyside_imports(py_candidate)) + return list(all_modules) + + # incase there is not .pyproject file, search all python files in project_dir, except + # ignore_dirs + if extra_ignore_dirs: + ignore_dirs.extend(extra_ignore_dirs) + + # find relevant .py files + _walk = os.walk(project_dir) + for root, dirs, files in _walk: + dirs[:] = [d for d in dirs if d not in ignore_dirs and not d.startswith(".")] + for py_file in files: + if py_file.endswith(".py"): + py_candidates.append(Path(root) / py_file) + + for py_candidate in py_candidates: + all_modules = all_modules.union(pyside_imports(py_candidate)) + + if not all_modules: + ValueError("[DEPLOY] No PySide6 modules were found") + + return list(all_modules) |