diff options
author | Shyamnath Premnadh <shyamnath.premnadh@qt.io> | 2023-03-23 12:41:05 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-03-29 16:17:42 +0000 |
commit | 6cec7beec77368e26d54a2837755c5758fa59e52 (patch) | |
tree | f0eaf800ccb3206e22da9fc85c6405d29ef4c08c /sources | |
parent | 5c272b4a8b7cf1ed850e33b7ec5d15d09c47ecbf (diff) |
Deployment: Refactor to enable Android support
- moving code into functions so that Android can re-use them
- new file "deploy_util.py" to store all the common utility functions
that can be reused for Android deployment tool
- new option "--name" to set the application name
- Change some print statements to log statements
- Adapt depoyment tests
- In default.spec, rename "packages" to "desktop_packages"
- Fix relative Qml file error in config.py i.e. it errors when the
path is already relative
Task-number: PYSIDE-1612
Change-Id: Iaaea8837cc362b3cc8035b96247194c4a9679579
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
(cherry picked from commit 66fb8337365d88638ebabe3e6e5684a4cae5109f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'sources')
-rw-r--r-- | sources/pyside-tools/deploy.py | 202 | ||||
-rw-r--r-- | sources/pyside-tools/deploy.pyproject | 3 | ||||
-rw-r--r-- | sources/pyside-tools/deploy_lib/__init__.py | 6 | ||||
-rw-r--r-- | sources/pyside-tools/deploy_lib/config.py | 114 | ||||
-rw-r--r-- | sources/pyside-tools/deploy_lib/default.spec | 4 | ||||
-rw-r--r-- | sources/pyside-tools/deploy_lib/deploy_util.py | 95 | ||||
-rw-r--r-- | sources/pyside-tools/deploy_lib/nuitka_helper.py | 3 | ||||
-rw-r--r-- | sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py | 4 |
8 files changed, 266 insertions, 165 deletions
diff --git a/sources/pyside-tools/deploy.py b/sources/pyside-tools/deploy.py index 418efd56d..043018eea 100644 --- a/sources/pyside-tools/deploy.py +++ b/sources/pyside-tools/deploy.py @@ -1,161 +1,110 @@ # Copyright (C) 2022 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 -"""pyside6-deploy deployment tool - - How does it work? - - Running "pyside6-deploy path/to/main_file" will - 1. Create a pysidedeploy.spec config file to control the overall deployment process - 2. Prompt the user to create a virtual environment (if not in one already) - If yes, virtual environment is created in the current folder - If no, uses the system wide python - 3. Install all dependencies and figure out Qt nuitka optimizations - 2. Use the spec file by android deploy tool or nuitka (desktop), to - create the executable - - Desktop deployment: Wrapper around Nuitka with support for Windows, - Linux, Mac - 1. for non-QML cases, only required modules are included - 2. for QML cases, all modules are included because of all QML - plugins getting included with nuitka - - For other ways of using the tool: - 1. pyside6-deploy (incase main file is called main.py) - 2. pyside6-deploy -c /path/to/config_file -""" +""" pyside6-deploy deployment tool -import argparse -import logging -import sys -from pathlib import Path -import shutil -import traceback -from textwrap import dedent + Deployment tool that uses Nuitka to deploy PySide6 applications to various Desktop (Windows, + Linux, macOS) platforms. -from deploy_lib import Config, PythonExecutable, MAJOR_VERSION + How does it work? -EXE_FORMAT = ".exe" if sys.platform == "win32" else ".bin" + Desktop Deployment: + Command: pyside6-deploy path/to/main_file + pyside6-deploy (incase main file is called main.py) + pyside6-deploy -c /path/to/config_file + Platforms Supported: Linux, Windows, macOS + Module Binary inclusion: + 1. for non-QML cases, only required modules are included + 2. for QML cases, all modules are included because of all QML plugins getting included + with nuitka -def config_option_exists(): - return True if any(item in sys.argv for item in ["--config-file", "-c"]) else False + Config file: + On the first run of the tool, it creates a config file called pysidedeploy.spec which + controls the various characteristic of the deployment. Users can simply change the value + in this config file to achieve different properties ie. change the application name, + deployment platform etc. + Note: This file is used by both pyside6-deploy and pyside6-android-deploy -def main_py_exists(): - return (Path.cwd() / "main.py").exists() +""" +import argparse +import logging +from pathlib import Path +import traceback +from textwrap import dedent -def clean(purge_path: Path): - """remove the generated deployment files""" - if purge_path.exists(): - shutil.rmtree(purge_path) - logging.info("[DEPLOY] deployment directory purged") - else: - print(f"{purge_path} does not exist") +from deploy_lib import (setup_python, get_config, cleanup, install_python_dependencies, finalize, + config_option_exists, Config, MAJOR_VERSION) -def main(main_file: Path = None, config_file: Path = None, init: bool = False, +def main(main_file: Path = None, name: str = None, config_file: Path = None, init: bool = False, loglevel=logging.WARNING, dry_run: bool = False, keep_deployment_files: bool = False, force: bool = False): + logging.basicConfig(level=loglevel) if config_file and Path(config_file).exists(): - config_file = Path(config_file) + config_file = Path(config_file).resolve() if not config_file and not main_file.exists(): print(dedent(""" - Directory does not contain main.py file - Please specify the main python entrypoint file or the config file - Run "pyside6-deploy --help" to see info about cli options + Directory does not contain main.py file. + Please specify the main python entrypoint file or the config file. + Run "pyside6-deploy desktop --help" to see info about cli options. pyside6-deploy exiting...""")) return - if main_file: - if main_file.parent != Path.cwd(): - config_file = main_file.parent / "pysidedeploy.spec" - else: - config_file = Path.cwd() / "pysidedeploy.spec" - # Nuitka command to run command_str = None - - logging.info("[DEPLOY] Start") generated_files_path = None - try: - 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 in virtualenv. 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("Exiting ...") - sys.exit(0) - - python = PythonExecutable(dry_run=dry_run) - logging.info(f"[DEPLOY] using python at {sys.executable}") - - config = Config(config_file=config_file, source_file=main_file, - python_exe=python.exe, dry_run=dry_run) - - source_file = config.project_dir / config.source_file - generated_files_path = source_file.parent / "deployment" - if generated_files_path.exists(): - clean(generated_files_path) - - if not init: - # install packages needed for deployment - print("[DEPLOY] Installing dependencies \n") - packages = config.get_value("python", "packages").split(",") - python.install(packages=packages) - # nuitka requires patchelf to make patchelf rpath changes for some Qt files - if sys.platform.startswith("linux"): - python.install(packages=["patchelf"]) - - if config.project_dir == Path.cwd(): - final_exec_path = config.project_dir.relative_to(Path.cwd()) - else: - final_exec_path = config.project_dir - final_exec_path = Path( - config.set_or_fetch( - config_property_val=final_exec_path, config_property_key="exec_directory" - ) - ).absolute() + config = None + logging.info("[DEPLOY] Start") - if not dry_run: - config.update_config() + python = setup_python(dry_run=dry_run, force=force, init=init) + config = get_config(python_exe=python.exe, dry_run=dry_run, config_file=config_file, + main_file=main_file) + + # set application name + if name: + config.title = name + + source_file = config.project_dir / config.source_file + + generated_files_path = source_file.parent / "deployment" + cleanup(generated_files_path=generated_files_path, config=config) + + install_python_dependencies(config=config, python=python, init=init, + packages="desktop_packages") - if init: - # config file created above. Exiting. - logging.info(f"[DEPLOY]: Config file {config.config_file} created") - return + # writing config file + if not dry_run: + config.update_config() + if init: + # config file created above. Exiting. + logging.info(f"[DEPLOY]: Config file {config.config_file} created") + return + + try: # create executable if not dry_run: - print("[DEPLOY] Deploying application") + logging.info("[DEPLOY] Deploying application") + command_str = python.create_executable( source_file=source_file, extra_args=config.get_value("nuitka", "extra_args"), config=config, - ) + ) except Exception: - print(f"Exception occurred: {traceback.format_exc()}") + print(f"[DEPLOY] Exception occurred: {traceback.format_exc()}") finally: - # clean up generated deployment files and copy executable into - # final_exec_path - if (not keep_deployment_files and generated_files_path and generated_files_path.exists()): - generated_exec_path = generated_files_path / (source_file.stem + EXE_FORMAT) - if generated_exec_path.exists() and final_exec_path: - shutil.copy(generated_exec_path, final_exec_path) - print( - f"[DEPLOY] Executed file created in " - f"{final_exec_path / (source_file.stem + EXE_FORMAT)}" - ) - clean(generated_files_path) + if generated_files_path and config: + finalize(generated_files_path=generated_files_path, config=config) + if not keep_deployment_files: + cleanup(generated_files_path=generated_files_path, config=Config) logging.info("[DEPLOY] End") return command_str @@ -163,7 +112,8 @@ def main(main_file: Path = None, config_file: Path = None, init: bool = False, if __name__ == "__main__": parser = argparse.ArgumentParser( - description=f"This tool deploys PySide{MAJOR_VERSION} to different platforms", + description=(f"This tool deploys PySide{MAJOR_VERSION} to Desktop (Windows, Linux, macOS)" + "platforms"), formatter_class=argparse.RawTextHelpFormatter, ) @@ -179,18 +129,20 @@ if __name__ == "__main__": help="Create pysidedeploy.spec file, if it doesn't already exists") parser.add_argument( - "-v", "--verbose", help="run in verbose mode", action="store_const", + "-v", "--verbose", help="Run in verbose mode", action="store_const", dest="loglevel", const=logging.INFO) - parser.add_argument("--dry-run", action="store_true", help="show the commands to be run") + parser.add_argument("--dry-run", action="store_true", help="Show the commands to be run") parser.add_argument( "--keep-deployment-files", action="store_true", - help="keep the generated deployment files generated") + help="Keep the generated deployment files generated") - parser.add_argument("-f", "--force", action="store_true", help="force all input prompts") + parser.add_argument("-f", "--force", action="store_true", help="Force all input prompts") + + parser.add_argument("--name", type=str, help="Application name") args = parser.parse_args() - main(args.main_file, args.config_file, args.init, args.loglevel, args.dry_run, - args.keep_deployment_files, args.force) + main(args.main_file, args.name, args.config_file, args.init, args.loglevel, args.dry_run, + args.keep_deployment_files, args.force) diff --git a/sources/pyside-tools/deploy.pyproject b/sources/pyside-tools/deploy.pyproject index 121369f34..c0085fbe1 100644 --- a/sources/pyside-tools/deploy.pyproject +++ b/sources/pyside-tools/deploy.pyproject @@ -1,5 +1,6 @@ { "files": ["deploy.py", "deploy_lib/__init__.py", "deploy_lib/commands.py", "deploy_lib/config.py", "deploy_lib/default.spec", "deploy_lib/nuitka_helper.py", "deploy_lib/pyside_icon.jpg", - "deploy_lib/python_helper.py"] + "deploy_lib/python_helper.py", "deploy_lib/deploy_util.py" + ] } diff --git a/sources/pyside-tools/deploy_lib/__init__.py b/sources/pyside-tools/deploy_lib/__init__.py index 2ba281d78..1aa7ef9cc 100644 --- a/sources/pyside-tools/deploy_lib/__init__.py +++ b/sources/pyside-tools/deploy_lib/__init__.py @@ -1,9 +1,13 @@ # Copyright (C) 2022 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 sys MAJOR_VERSION = 6 +EXE_FORMAT = ".exe" if sys.platform == "win32" else ".bin" from .commands import run_command from .nuitka_helper import Nuitka -from .config import Config +from .config import BaseConfig, Config from .python_helper import PythonExecutable +from .deploy_util import (cleanup, finalize, get_config, setup_python, install_python_dependencies, + config_option_exists) diff --git a/sources/pyside-tools/deploy_lib/config.py b/sources/pyside-tools/deploy_lib/config.py index 375cb3554..2575ab063 100644 --- a/sources/pyside-tools/deploy_lib/config.py +++ b/sources/pyside-tools/deploy_lib/config.py @@ -15,12 +15,9 @@ from .commands import run_qmlimportscanner EXCLUDED_QML_PLUGINS = {"QtQuick", "QtQuick3D", "QtCharts", "QtWebEngine", "QtTest", "QtSensors"} -class Config: - """ - Wrapper class around config file, whose options are used to control the executable creation - """ +class BaseConfig: - def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool): + def __init__(self, config_file: Path, dry_run: bool, comment_prefixes: str = "/") -> None: self.config_file = config_file self.parser = ConfigParser(comment_prefixes="/", allow_no_value=True) if not self.config_file.exists(): @@ -30,10 +27,45 @@ class Config: else: self.config_file = Path(__file__).parent / "default.spec" else: - print(f"Using existing config file {config_file}") + logging.info(f"Using existing config file {config_file}") self.parser.read(self.config_file) - self.dry_run = dry_run + def update_config(self): + logging.info(f"[DEPLOY] Creating {self.config_file}") + with open(self.config_file, "w+") as config_file: + self.parser.write(config_file, space_around_delimiters=True) + + def set_value(self, section: str, key: str, new_value: str): + try: + current_value = self.get_value(section, key, ignore_fail=True) + if current_value != new_value: + self.parser.set(section, key, new_value) + except configparser.NoOptionError: + logging.warning(f"[DEPLOY] Key {key} does not exist") + except configparser.NoSectionError: + logging.warning(f"[DEPLOY] Section {section} does not exist") + + def get_value(self, section: str, key: str, ignore_fail: bool = False): + try: + return self.parser.get(section, key) + except configparser.NoOptionError: + if not ignore_fail: + logging.warning(f"[DEPLOY] Key {key} does not exist") + except configparser.NoSectionError: + if not ignore_fail: + logging.warning(f"[DEPLOY] Section {section} does not exist") + + +class Config(BaseConfig): + """ + Wrapper class around pysidedeploy.spec file, whose options are used to control the executable + creation + """ + + def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool): + super().__init__(config_file, dry_run) + + self._dry_run = dry_run # set source_file self.source_file = Path( self.set_or_fetch(config_property_val=source_file, config_property_key="input_file") @@ -48,12 +80,19 @@ class Config: ) ) + self.title = self.get_value("app", "title") self.project_dir = None if self.get_value("app", "project_dir"): self.project_dir = Path(self.get_value("app", "project_dir")).absolute() else: self._find_and_set_project_dir() + self.exe_dir = None + if self.get_value("app", "exec_directory"): + self.exe_dir = Path(self.get_value("app", "exec_directory")).absolute() + else: + self._find_and_set_exe_dir() + self.project_data: ProjectData = None if self.get_value("app", "project_file"): project_file = Path(self.get_value("app", "project_file")).absolute() @@ -74,29 +113,6 @@ class Config: else: self._find_and_set_excluded_qml_plugins() - def update_config(self): - logging.info(f"[DEPLOY] Creating {self.config_file}") - with open(self.config_file, "w+") as config_file: - self.parser.write(config_file, space_around_delimiters=True) - - def set_value(self, section: str, key: str, new_value: str): - try: - current_value = self.get_value(section, key) - if current_value != new_value: - self.parser.set(section, key, new_value) - except configparser.NoOptionError: - logging.warning(f"[DEPLOY] key {key} does not exist") - except configparser.NoSectionError: - logging.warning(f"[DEPLOY] section {section} does not exist") - - def get_value(self, section: str, key: str): - try: - return self.parser.get(section, key) - except configparser.NoOptionError: - logging.warning(f"[DEPLOY] key {key} does not exist") - except configparser.NoSectionError: - logging.warning(f"[DEPLOY] section {section} does not exist") - def set_or_fetch(self, config_property_val, config_property_key, config_property_group="app"): """ Write to config_file if 'config_property_key' is known without config_file @@ -110,10 +126,13 @@ class Config: elif self.get_value(config_property_group, config_property_key): return self.get_value(config_property_group, config_property_key) else: - logging.exception( + raise RuntimeError( f"[DEPLOY] No {config_property_key} specified in config file or as cli option" ) - raise + + @property + def dry_run(self): + return self._dry_run @property def qml_files(self): @@ -132,6 +151,15 @@ class Config: self._project_dir = project_dir @property + def title(self): + return self._title + + @title.setter + def title(self, title): + self._title = title + self.set_value("app", "title", title) + + @property def source_file(self): return self._source_file @@ -155,6 +183,14 @@ class Config: def excluded_qml_plugins(self, excluded_qml_plugins): self._excluded_qml_plugins = excluded_qml_plugins + @property + def exe_dir(self): + return self._exe_dir + + @exe_dir.setter + def exe_dir(self, exe_dir): + self._exe_dir = exe_dir + def _find_and_set_qml_files(self): """Fetches all the qml_files in the folder and sets them if the field qml_files is empty in the config_dir""" @@ -208,7 +244,8 @@ class Config: self.set_value( "qt", "qml_files", - ",".join([str(file.relative_to(self.project_dir)) for file in self.qml_files]), + ",".join([str(file.absolute().relative_to(self.project_dir)) + for file in self.qml_files]), ) logging.info("[DEPLOY] QML files identified and set in config_file") @@ -257,3 +294,14 @@ class Config: if self.excluded_qml_plugins: self.set_value("qt", "excluded_qml_plugins", ",".join(self.excluded_qml_plugins)) + + def _find_and_set_exe_dir(self): + if self.project_dir == Path.cwd(): + self.exe_dir = self.project_dir.relative_to(Path.cwd()) + else: + self.exe_dir = self.project_dir + self.exe_dir = Path( + self.set_or_fetch( + config_property_val=self.exe_dir, config_property_key="exec_directory" + ) + ).absolute() diff --git a/sources/pyside-tools/deploy_lib/default.spec b/sources/pyside-tools/deploy_lib/default.spec index 812861a70..d63fb46b7 100644 --- a/sources/pyside-tools/deploy_lib/default.spec +++ b/sources/pyside-tools/deploy_lib/default.spec @@ -1,7 +1,7 @@ [app] # Title of your application -title = My Application +title = pyside_app_demo # Project Directory. The general assumption is that project_dir is the parent directory # of input_file @@ -24,7 +24,7 @@ python_path = # python packages to install # ordered-set: increase compile time performance of nuitka packaging # zstandard: provides final executable size optimization -packages = nuitka==1.4.8,ordered_set,zstandard +desktop_packages = nuitka==1.4.8,ordered_set,zstandard [qt] diff --git a/sources/pyside-tools/deploy_lib/deploy_util.py b/sources/pyside-tools/deploy_lib/deploy_util.py new file mode 100644 index 000000000..843a21f5a --- /dev/null +++ b/sources/pyside-tools/deploy_lib/deploy_util.py @@ -0,0 +1,95 @@ +# 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 sys +import logging +import shutil + +from pathlib import Path +from .config import Config +from .python_helper import PythonExecutable +from . import EXE_FORMAT + + +def config_option_exists(): + for argument in sys.argv: + if any(item in argument for item in ["--config-file", "-c"]): + return True + + return False + + +def cleanup(generated_files_path: Path, config: Config): + """ + Cleanup the generated build folders/files + """ + if generated_files_path.exists(): + shutil.rmtree(generated_files_path) + logging.info("[DEPLOY] Deployment directory purged") + elif not config.dry_run: + logging.info(f"[DEPLOY] {generated_files_path} does not exist") + + +def get_config(python_exe: Path, dry_run: bool = False, config_file: Path = None, main_file: + Path = None): + """ + Sets up a new deployment configuration or use an existing config file + """ + if main_file and not config_file: + if main_file.parent != Path.cwd(): + config_file = main_file.parent / "pysidedeploy.spec" + else: + config_file = Path.cwd() / "pysidedeploy.spec" + + config = Config(config_file=config_file, source_file=main_file, python_exe=python_exe, + dry_run=dry_run) + + return config + + +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): + """ + Installs the python package dependencies for the target deployment platform + """ + if not init: + # install packages needed for deployment + logging.info("[DEPLOY] Installing dependencies \n") + packages = config.get_value("python", packages).split(",") + python.install(packages=packages) + # nuitka requires patchelf to make patchelf rpath changes for some Qt files + if sys.platform.startswith("linux"): + python.install(packages=["patchelf"]) + + +def finalize(generated_files_path: Path, config: Config): + """ + Copy the executable into the final location + """ + generated_exec_path = generated_files_path / (config.source_file.stem + EXE_FORMAT) + if generated_exec_path.exists() and config.exe_dir: + 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))}") diff --git a/sources/pyside-tools/deploy_lib/nuitka_helper.py b/sources/pyside-tools/deploy_lib/nuitka_helper.py index 83144c7fe..cc91ab924 100644 --- a/sources/pyside-tools/deploy_lib/nuitka_helper.py +++ b/sources/pyside-tools/deploy_lib/nuitka_helper.py @@ -3,6 +3,7 @@ import os import sys +import logging from pathlib import Path from typing import List @@ -36,7 +37,7 @@ class Nuitka: output_dir = source_file.parent / "deployment" if not dry_run: output_dir.mkdir(parents=True, exist_ok=True) - print("[DEPLOY] Running Nuitka") + logging.info("[DEPLOY] Running Nuitka") command = self.nuitka + [ os.fspath(source_file), "--follow-imports", diff --git a/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py b/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py index e822ff01c..f695dfd83 100644 --- a/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py +++ b/sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py @@ -95,7 +95,7 @@ class TestPySide6Deploy(unittest.TestCase): self.assertEqual(config_obj.get_value("app", "input_file"), "tetrix.py") self.assertEqual(config_obj.get_value("app", "project_dir"), ".") self.assertEqual(config_obj.get_value("app", "exec_directory"), ".") - self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.4.8,ordered_set,zstandard") + self.assertEqual(config_obj.get_value("python", "desktop_packages"), "nuitka==1.4.8,ordered_set,zstandard") self.assertEqual(config_obj.get_value("qt", "qml_files"), "") self.assertEqual( config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True" @@ -149,7 +149,7 @@ class TestPySide6Deploy(unittest.TestCase): self.assertEqual(config_obj.get_value("app", "input_file"), "main.py") self.assertEqual(config_obj.get_value("app", "project_dir"), ".") self.assertEqual(config_obj.get_value("app", "exec_directory"), ".") - self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.4.8,ordered_set,zstandard") + self.assertEqual(config_obj.get_value("python", "desktop_packages"), "nuitka==1.4.8,ordered_set,zstandard") self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml") self.assertEqual( config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True" |