From 003a35be3d908431c25f7fa9d7a7dd6beb8e0fe1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 12 Feb 2022 21:47:51 +1100 Subject: Fix errors on selection of additional packages (#959) * Fix errors on selection of additional packages * Fix flake8 * Added the new /groups/search/json/?name=x endpoint merged today * Fixed flake8 complaint * Forgot to do json.loads() on the HTTP request result * Update package selection * Fix flake8 Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum --- archinstall/lib/menu/text_input.py | 17 +++++++++ archinstall/lib/packages/packages.py | 73 ++++++++++++++++++------------------ archinstall/lib/user_interaction.py | 38 +++++++++++-------- 3 files changed, 76 insertions(+), 52 deletions(-) create mode 100644 archinstall/lib/menu/text_input.py (limited to 'archinstall/lib') diff --git a/archinstall/lib/menu/text_input.py b/archinstall/lib/menu/text_input.py new file mode 100644 index 00000000..05ca0f22 --- /dev/null +++ b/archinstall/lib/menu/text_input.py @@ -0,0 +1,17 @@ +import readline + + +class TextInput: + def __init__(self, prompt: str, prefilled_text=''): + self._prompt = prompt + self._prefilled_text = prefilled_text + + def _hook(self): + readline.insert_text(self._prefilled_text) + readline.redisplay() + + def run(self) -> str: + readline.set_pre_input_hook(self._hook) + result = input(self._prompt) + readline.set_pre_input_hook() + return result diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index 5b94aa59..26d8ff68 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -1,17 +1,18 @@ +import json import ssl import urllib.request -import json -from typing import Dict, Any +from typing import Dict, Any, Tuple, List + +from ..exceptions import PackageError, SysCallError from ..general import SysCommand from ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage -from ..exceptions import PackageError, SysCallError, RequirementError BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/?name={package}' # BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/' -BASE_GROUP_URL = 'https://archlinux.org/groups/x86_64/{group}/' +BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/?name={group}' -def find_group(name :str) -> bool: +def group_search(name :str) -> List[PackageSearchResult]: # TODO UPSTREAM: Implement /json/ for the groups search ssl_context = ssl.create_default_context() ssl_context.check_hostname = False @@ -20,15 +21,15 @@ def find_group(name :str) -> bool: response = urllib.request.urlopen(BASE_GROUP_URL.format(group=name), context=ssl_context) except urllib.error.HTTPError as err: if err.code == 404: - return False + return [] else: raise err # Just to be sure some code didn't slip through the exception - if response.code == 200: - return True + data = response.read().decode('UTF-8') + + return [PackageSearchResult(**package) for package in json.loads(data)['results']] - return False def package_search(package :str) -> PackageSearch: """ @@ -49,28 +50,24 @@ def package_search(package :str) -> PackageSearch: return PackageSearch(**json.loads(data)) -class IsGroup(BaseException): - pass -def find_package(package :str) -> PackageSearchResult: +def find_package(package :str) -> List[PackageSearchResult]: data = package_search(package) + results = [] - if not data.results: - # Check if the package is actually a group - if find_group(package): - # TODO: Until upstream adds a JSON result for group searches - # there is no way we're going to parse HTML reliably. - raise IsGroup("Implement group search") - - raise PackageError(f"Could not locate {package} while looking for repository category") + for result in data.results: + if result.pkgname == package: + results.append(result) # If we didn't find the package in the search results, # odds are it's a group package - for result in data.results: - if result.pkgname == package: - return result + if not results: + # Check if the package is actually a group + for result in group_search(package): + results.append(result) + + return results - raise PackageError(f"Could not locate {package} in result while looking for repository category") def find_packages(*names :str) -> Dict[str, Any]: """ @@ -78,23 +75,25 @@ def find_packages(*names :str) -> Dict[str, Any]: The function itself is rather slow, so consider not sending to many packages to the search query. """ - return {package: find_package(package) for package in names} + result = {} + for package in names: + for found_package in find_package(package): + result[package] = found_package + + return result -def validate_package_list(packages: list) -> bool: +def validate_package_list(packages :list) -> Tuple[list, list]: """ Validates a list of given packages. - Raises `RequirementError` if one or more packages are not found. + return: Tuple of lists containing valid packavges in the first and invalid + packages in the second entry """ - invalid_packages = [ - package - for package in packages - if not find_package(package)['results'] and not find_group(package) - ] - if invalid_packages: - raise RequirementError(f"Invalid package names: {invalid_packages}") + valid_packages = {package for package in packages if find_package(package)} + invalid_packages = set(packages) - valid_packages + + return list(valid_packages), list(invalid_packages) - return True def installed_package(package :str) -> LocalPackage: package_info = {} @@ -105,5 +104,5 @@ def installed_package(package :str) -> LocalPackage: package_info[key.strip().lower().replace(' ', '_')] = value.strip() except SysCallError: pass - - return LocalPackage(**package_info) \ No newline at end of file + + return LocalPackage(**package_info) diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index 202b14a4..66ad3e2a 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -12,6 +12,8 @@ from collections.abc import Iterable from typing import List, Any, Optional, Dict, Union, TYPE_CHECKING # https://stackoverflow.com/a/39757388/929999 +from .menu.text_input import TextInput + if TYPE_CHECKING: from .disk.partition import Partition @@ -32,6 +34,7 @@ from .translation import Translation from .disk.validators import fs_types from .packages.packages import validate_package_list + # TODO: These can be removed after the move to simple_menu.py def get_terminal_height() -> int: return shutil.get_terminal_size().lines @@ -390,28 +393,33 @@ def ask_for_audio_selection(desktop :bool = True) -> str: return selected_audio -# TODO: Remove? Moved? -def ask_additional_packages_to_install(packages :List[str] = None) -> List[str]: +def ask_additional_packages_to_install(pre_set_packages :List[str] = []) -> List[str]: # Additional packages (with some light weight error handling for invalid package names) print(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) print(_('If you desire a web browser, such as firefox or chromium, you may specify it in the following prompt.')) - while True: - packages = [p for p in input( - _('Write additional packages to install (space separated, leave blank to skip): ') - ).split(' ') if len(p)] + def read_packages(already_defined: list = []) -> list: + display = ' '.join(already_defined) + input_packages = TextInput( + _('Write additional packages to install (space separated, leave blank to skip): '), + display + ).run() + return input_packages.split(' ') if input_packages else [] + + pre_set_packages = pre_set_packages if pre_set_packages else [] + packages = read_packages(pre_set_packages) + while True: if len(packages): # Verify packages that were given - try: - print(_("Verifying that additional packages exist (this might take a few seconds)")) - validate_package_list(packages) - break - except RequirementError as e: - log(e, fg='red') - else: - # no additional packages were selected, which we'll allow - break + print(_("Verifying that additional packages exist (this might take a few seconds)")) + valid, invalid = validate_package_list(packages) + + if invalid: + log(f"Some packages could not be found in the repository: {invalid}", level=logging.WARNING, fg='red') + packages = read_packages(valid) + continue + break return packages -- cgit v1.2.3-54-g00ecf