aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside-tools/deploy_lib/deploy_util.py
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside-tools/deploy_lib/deploy_util.py')
-rw-r--r--sources/pyside-tools/deploy_lib/deploy_util.py153
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)