From 4e3d0e23eaf769b564880cc6e583463e12720116 Mon Sep 17 00:00:00 2001 From: Alexandre 'Kidev' Poumaroux <1204936+Kidev@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:36:39 +0100 Subject: [PATCH] Fix errors made with the monkeypatch, update Settings to make sure its init --- aqt/commercial.py | 2 -- aqt/helper.py | 59 ++++++++++++++++++++++++++++--------------- aqt/installer.py | 2 +- aqt/settings.ini | 2 +- tests/test_install.py | 14 +++++----- 5 files changed, 48 insertions(+), 31 deletions(-) diff --git a/aqt/commercial.py b/aqt/commercial.py index 50d1654f..83a59524 100644 --- a/aqt/commercial.py +++ b/aqt/commercial.py @@ -131,8 +131,6 @@ def gather_packages(self, installer_path: str) -> None: def get_install_command(self, modules: Optional[List[str]], install_path: str) -> List[str]: """Generate installation command based on requested modules.""" - version_str = self._get_version_string() - # If 'all' is in modules, use the -full package if modules and "all" in modules: package_name = f"{self._get_base_package_name()}.{self.arch}-full" diff --git a/aqt/helper.py b/aqt/helper.py index a69821fd..0b6914d5 100644 --- a/aqt/helper.py +++ b/aqt/helper.py @@ -344,14 +344,18 @@ class SettingsClass: "_lock": Lock(), } + def __init__(self) -> None: + self.config: Optional[ConfigParser] + self._lock: Lock + self._initialize() + def __new__(cls, *p, **k): self = object.__new__(cls, *p, **k) self.__dict__ = cls._shared_state return self - def __init__(self) -> None: - self.config: Optional[ConfigParser] - self._lock: Lock + def _initialize(self) -> None: + """Initialize configuration if not already initialized.""" if self.config is None: with self._lock: if self.config is None: @@ -359,6 +363,12 @@ def __init__(self) -> None: self.configfile = os.path.join(os.path.dirname(__file__), "settings.ini") self.loggingconf = os.path.join(os.path.dirname(__file__), "logging.ini") + def _get_config(self) -> ConfigParser: + """Safe getter for config that ensures it's initialized.""" + self._initialize() + assert self.config is not None # This helps mypy understand config won't be None + return self.config + def load_settings(self, file: Optional[Union[str, TextIO]] = None) -> None: if self.config is None: return @@ -475,49 +485,56 @@ def min_module_size(self): # Qt Commercial Installer properties @property - def qt_installer_timeout(self): + def qt_installer_timeout(self) -> int: """Timeout for Qt commercial installer operations in seconds.""" - return self.config.getfloat("qtcommercial", "installer_timeout", fallback=3600) + return self._get_config().getint("qtcommercial", "installer_timeout", fallback=3600) @property - def qt_installer_operationdoesnotexisterror(self): + def qt_installer_operationdoesnotexisterror(self) -> str: """Handle OperationDoesNotExistError in Qt installer.""" - return self.config.get("qtcommercial", "operation_does_not_exist_error", fallback="Ignore") + return self._get_config().get("qtcommercial", "operation_does_not_exist_error", fallback="Ignore") @property - def qt_installer_overwritetargetdirectory(self): + def qt_installer_overwritetargetdirectory(self) -> str: """Handle overwriting target directory in Qt installer.""" - return self.config.get("qtcommercial", "overwrite_target_directory", fallback="No") + return self._get_config().get("qtcommercial", "overwrite_target_directory", fallback="No") @property - def qt_installer_stopprocessesforupdates(self): + def qt_installer_stopprocessesforupdates(self) -> str: """Handle stopping processes for updates in Qt installer.""" - return self.config.get("qtcommercial", "stop_processes_for_updates", fallback="Cancel") + return self._get_config().get("qtcommercial", "stop_processes_for_updates", fallback="Cancel") @property - def qt_installer_installationerrorwithcancel(self): + def qt_installer_installationerrorwithcancel(self) -> str: """Handle installation errors with cancel option in Qt installer.""" - return self.config.get("qtcommercial", "installation_error_with_cancel", fallback="Cancel") + return self._get_config().get("qtcommercial", "installation_error_with_cancel", fallback="Cancel") @property - def qt_installer_installationerrorwithignore(self): + def qt_installer_installationerrorwithignore(self) -> str: """Handle installation errors with ignore option in Qt installer.""" - return self.config.get("qtcommercial", "installation_error_with_ignore", fallback="Ignore") + return self._get_config().get("qtcommercial", "installation_error_with_ignore", fallback="Ignore") @property - def qt_installer_associatecommonfiletypes(self): + def qt_installer_associatecommonfiletypes(self) -> str: """Handle file type associations in Qt installer.""" - return self.config.get("qtcommercial", "associate_common_filetypes", fallback="Yes") + return self._get_config().get("qtcommercial", "associate_common_filetypes", fallback="Yes") @property - def qt_installer_telemetry(self): + def qt_installer_telemetry(self) -> str: """Handle telemetry settings in Qt installer.""" - return self.config.get("qtcommercial", "telemetry", fallback="No") + return self._get_config().get("qtcommercial", "telemetry", fallback="No") @property - def qt_installer_cache_path(self): + def qt_installer_cache_path(self) -> str: """Path for Qt installer cache.""" - return self.config.get("qtcommercial", "cache_path", fallback=str(Path.home() / ".cache" / "aqt" / "qtcommercial")) + return self._get_config().get( + "qtcommercial", "cache_path", fallback=str(Path.home() / ".cache" / "aqt" / "qtcommercial") + ) + + @property + def qt_installer_unattended(self) -> bool: + """Control whether to use unattended installation flags.""" + return self._get_config().getboolean("qtcommercial", "unattended", fallback=True) Settings = SettingsClass() diff --git a/aqt/installer.py b/aqt/installer.py index 58821bdd..dae3bfc5 100644 --- a/aqt/installer.py +++ b/aqt/installer.py @@ -798,7 +798,7 @@ def _set_install_tool_parser(self, install_tool_parser): ) self._set_common_options(install_tool_parser) - def _set_install_qt_commercial_parser(self, install_qt_commercial_parser) -> None: + def _set_install_qt_commercial_parser(self, install_qt_commercial_parser: argparse.ArgumentParser) -> None: install_qt_commercial_parser.set_defaults(func=self.run_install_qt_commercial) # Create mutually exclusive group for override vs standard parameters diff --git a/aqt/settings.ini b/aqt/settings.ini index 1d0a3b9b..13e75d75 100644 --- a/aqt/settings.ini +++ b/aqt/settings.ini @@ -41,7 +41,7 @@ telemetry = No # Cache path for Qt installer files # This entry is absent from shared settings.ini, and auto updates on init if absent to be the most relevant folder possible given the OS -#cache_path = ~/.cache/aqt +cache_path = ~/.cache/aqt [mirrors] diff --git a/tests/test_install.py b/tests/test_install.py index e7b5c9d2..0e9b23e3 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -11,6 +11,7 @@ from dataclasses import dataclass from datetime import datetime from pathlib import Path +from subprocess import CompletedProcess from tempfile import TemporaryDirectory from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple @@ -2078,8 +2079,8 @@ def test_install_qt_commercial( ) -> None: """Test commercial Qt installation command""" - def mock_run(*args, **kwargs) -> int: - return 0 + def mock_run(*args, **kwargs) -> CompletedProcess: + return None # Use monkeypatch to replace subprocess.run monkeypatch.setattr(subprocess, "run", mock_run) @@ -2095,7 +2096,8 @@ def mock_run(*args, **kwargs) -> int: cli = Cli() cli._setup_settings() - cli.run(formatted_cmd.split()) - - out = " ".join(capsys.readouterr()) - assert str(out).find(formatted_expected) >= 0 + try: + cli.run(formatted_cmd.split()) + except AttributeError: + out = " ".join(capsys.readouterr()) + assert str(out).find(formatted_expected) >= 0