index : archinstall32 | |
Archlinux32 installer | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | archinstall/lib/user_interaction/__init__.py | 2 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/disk_conf.py | 51 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/general_conf.py | 138 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/locale_conf.py | 35 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/manage_users_conf.py | 177 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/network_conf.py | 51 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/partitioning_conf.py | 171 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/save_conf.py | 27 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/subvolume_config.py | 36 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/system_conf.py | 126 | ||||
-rw-r--r-- | archinstall/lib/user_interaction/utils.py | 16 |
diff --git a/archinstall/lib/user_interaction/__init__.py b/archinstall/lib/user_interaction/__init__.py index b0174d94..8aba4b4d 100644 --- a/archinstall/lib/user_interaction/__init__.py +++ b/archinstall/lib/user_interaction/__init__.py @@ -1,5 +1,5 @@ from .save_conf import save_config -from .manage_users_conf import ask_for_superuser_account, ask_for_additional_users +from .manage_users_conf import ask_for_additional_users from .backwards_compatible_conf import generic_select, generic_multi_select from .locale_conf import select_locale_lang, select_locale_enc from .system_conf import select_kernel, select_harddrives, select_driver, ask_for_bootloader, ask_for_swap diff --git a/archinstall/lib/user_interaction/disk_conf.py b/archinstall/lib/user_interaction/disk_conf.py index 9238a766..371d052f 100644 --- a/archinstall/lib/user_interaction/disk_conf.py +++ b/archinstall/lib/user_interaction/disk_conf.py @@ -1,18 +1,18 @@ from __future__ import annotations -from typing import Any, Dict, TYPE_CHECKING +from typing import Any, Dict, TYPE_CHECKING, Optional from .partitioning_conf import manage_new_and_existing_partitions, get_default_partition_layout from ..disk import BlockDevice from ..exceptions import DiskError from ..menu import Menu -from ..output import log +from ..menu.menu import MenuSelectionType if TYPE_CHECKING: _: Any -def ask_for_main_filesystem_format(advanced_options=False): +def ask_for_main_filesystem_format(advanced_options=False) -> str: options = {'btrfs': 'btrfs', 'ext4': 'ext4', 'xfs': 'xfs', 'f2fs': 'f2fs'} advanced = {'ntfs': 'ntfs'} @@ -22,7 +22,7 @@ def ask_for_main_filesystem_format(advanced_options=False): prompt = _('Select which filesystem your main partition should use') choice = Menu(prompt, options, skip=False).run() - return choice + return choice.value def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: @@ -30,27 +30,36 @@ def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: for device in block_devices: layout = manage_new_and_existing_partitions(device) - result[device.path] = layout return result -def select_disk_layout(block_devices: list, advanced_options=False) -> Dict[str, Any]: +def select_disk_layout(preset: Optional[Dict[str, Any]], block_devices: list, advanced_options=False) -> Optional[Dict[str, Any]]: wipe_mode = str(_('Wipe all selected drives and use a best-effort default partition layout')) custome_mode = str(_('Select what to do with each individual drive (followed by partition usage)')) modes = [wipe_mode, custome_mode] - print(modes) - mode = Menu(_('Select what you wish to do with the selected block devices'), modes, skip=False).run() + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Select what you wish to do with the selected block devices'), + modes, + explode_on_interrupt=True, + explode_warning=warning + ).run() - if mode == wipe_mode: - return get_default_partition_layout(block_devices, advanced_options) - else: - return select_individual_blockdevice_usage(block_devices) + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return None + case MenuSelectionType.Selection: + if choice.value == wipe_mode: + return get_default_partition_layout(block_devices, advanced_options) + else: + return select_individual_blockdevice_usage(block_devices) -def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> BlockDevice: +def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> Optional[BlockDevice]: """ Asks the user to select a harddrive from the `dict_o_disks` selection. Usually this is combined with :ref:`archinstall.list_drives`. @@ -63,19 +72,15 @@ def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> BlockDevice: """ drives = sorted(list(dict_o_disks.keys())) if len(drives) >= 1: - for index, drive in enumerate(drives): - print( - f"{index}: {drive} ({dict_o_disks[drive]['size'], dict_o_disks[drive].device, dict_o_disks[drive]['label']})" - ) + title = str(_('You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)')) + '\n' + title += str(_('Select one of the disks or skip and use /mnt as default')) - log("You can skip selecting a drive and partitioning and use whatever drive-setup is mounted at /mnt (experimental)", - fg="yellow") + choice = Menu(title, drives).run() - drive = Menu('Select one of the disks or skip and use "/mnt" as default"', drives).run() - if not drive: - return drive + if choice.type_ == MenuSelectionType.Esc: + return None - drive = dict_o_disks[drive] + drive = dict_o_disks[choice.value] return drive raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.') diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py index c42e9e27..d4dc60db 100644 --- a/archinstall/lib/user_interaction/general_conf.py +++ b/archinstall/lib/user_interaction/general_conf.py @@ -3,6 +3,9 @@ from __future__ import annotations import logging from typing import List, Any, Optional, Dict, TYPE_CHECKING +import archinstall + +from ..menu.menu import MenuSelectionType from ..menu.text_input import TextInput from ..locale_helpers import list_keyboard_languages, list_timezones @@ -22,11 +25,12 @@ def ask_ntp(preset: bool = True) -> bool: prompt = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n')) prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor more information, please check the Arch wiki')) if preset: - preset_val = 'yes' + preset_val = Menu.yes() else: - preset_val = 'no' - choice = Menu(prompt, ['yes', 'no'], skip=False, preset_values=preset_val, default_option='yes').run() - return False if choice == 'no' else True + preset_val = Menu.no() + choice = Menu(prompt, Menu.yes_no(), skip=False, preset_values=preset_val, default_option=Menu.yes()).run() + + return False if choice.value == Menu.no() else True def ask_hostname(preset: str = None) -> str: @@ -38,23 +42,31 @@ def ask_for_a_timezone(preset: str = None) -> str: timezones = list_timezones() default = 'UTC' - selected_tz = Menu(_('Select a timezone'), - list(timezones), - skip=False, - preset_values=preset, - default_option=default).run() + choice = Menu( + _('Select a timezone'), + list(timezones), + preset_values=preset, + default_option=default + ).run() - return selected_tz + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return choice.value def ask_for_audio_selection(desktop: bool = True, preset: str = None) -> str: - audio = 'pipewire' if desktop else 'none' - choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', 'none'] - selected_audio = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=audio, skip=False).run() - return selected_audio + no_audio = str(_('No audio server')) + choices = ['pipewire', 'pulseaudio'] if desktop else ['pipewire', 'pulseaudio', no_audio] + default = 'pipewire' if desktop else no_audio + + choice = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=default).run() + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return choice.value -def select_language(default_value: str, preset_value: str = None) -> str: + +def select_language(preset_value: str = None) -> str: """ Asks the user to select a language Usually this is combined with :ref:`archinstall.list_keyboard_languages`. @@ -64,16 +76,19 @@ def select_language(default_value: str, preset_value: str = None) -> str: """ kb_lang = list_keyboard_languages() # sort alphabetically and then by length - # it's fine if the list is big because the Menu - # allows for searching anyways sorted_kb_lang = sorted(sorted(list(kb_lang)), key=len) - selected_lang = Menu(_('Select Keyboard layout'), - sorted_kb_lang, - default_option=default_value, - preset_values=preset_value, - sort=False).run() - return selected_lang + selected_lang = Menu( + _('Select keyboard layout'), + sorted_kb_lang, + preset_values=preset_value, + sort=False + ).run() + + if selected_lang.value is None: + return preset_value + + return selected_lang.value def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: @@ -89,24 +104,27 @@ def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]: else: preselected = list(preset_values.keys()) mirrors = list_mirrors() - selected_mirror = Menu(_('Select one of the regions to download packages from'), - list(mirrors.keys()), - preset_values=preselected, - multi=True).run() + selected_mirror = Menu( + _('Select one of the regions to download packages from'), + list(mirrors.keys()), + preset_values=preselected, + multi=True, + explode_on_interrupt=True + ).run() - if selected_mirror is not None: - return {selected: mirrors[selected] for selected in selected_mirror} - - return {} + match selected_mirror.type_: + case MenuSelectionType.Ctrl_c: return {} + case MenuSelectionType.Esc: return preset_values + case _: return {selected: mirrors[selected] for selected in selected_mirror.value} def select_archinstall_language(default='English'): - languages = Translation.get_all_names() - language = Menu(_('Select Archinstall language'), languages, default_option=default).run() + languages = Translation.get_available_lang() + language = Menu(_('Archinstall language'), languages, default_option=default).run() return language -def select_profile() -> Optional[Profile]: +def select_profile(preset) -> Optional[Profile]: """ # Asks the user to select a profile from the available profiles. # @@ -124,13 +142,27 @@ def select_profile() -> Optional[Profile]: options[option] = profile title = _('This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments') - - selection = Menu(title=title, p_options=list(options.keys())).run() - - if selection is not None: - return options[selection] - - return None + warning = str(_('Are you sure you want to reset this setting?')) + + selection = Menu( + title=title, + p_options=list(options.keys()), + explode_on_interrupt=True, + explode_warning=warning + ).run() + + match selection.type_: + case MenuSelectionType.Selection: + return options[selection.value] if selection.value is not None else None + case MenuSelectionType.Ctrl_c: + archinstall.storage['profile_minimal'] = False + archinstall.storage['_selected_servers'] = [] + archinstall.storage['_desktop_profile'] = None + archinstall.arguments['desktop-environment'] = None + archinstall.arguments['gfx_driver_packages'] = None + return None + case MenuSelectionType.Esc: + return None def ask_additional_packages_to_install(pre_set_packages: List[str] = []) -> List[str]: @@ -171,14 +203,16 @@ def select_additional_repositories(preset: List[str]) -> List[str]: repositories = ["multilib", "testing"] - additional_repositories = Menu(_('Choose which optional additional repositories to enable'), - repositories, - sort=False, - multi=True, - preset_values=preset, - default_option=[]).run() - - if additional_repositories is not None: - return additional_repositories - - return [] + choice = Menu( + _('Choose which optional additional repositories to enable'), + repositories, + sort=False, + multi=True, + preset_values=preset, + explode_on_interrupt=True + ).run() + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return [] + case MenuSelectionType.Selection: return choice.value diff --git a/archinstall/lib/user_interaction/locale_conf.py b/archinstall/lib/user_interaction/locale_conf.py index d48018cf..15720064 100644 --- a/archinstall/lib/user_interaction/locale_conf.py +++ b/archinstall/lib/user_interaction/locale_conf.py @@ -4,32 +4,39 @@ from typing import Any, TYPE_CHECKING from ..locale_helpers import list_locales from ..menu import Menu +from ..menu.menu import MenuSelectionType if TYPE_CHECKING: _: Any -def select_locale_lang(default: str, preset: str = None) -> str: +def select_locale_lang(preset: str = None) -> str: locales = list_locales() locale_lang = set([locale.split()[0] for locale in locales]) - selected_locale = Menu(_('Choose which locale language to use'), - locale_lang, - sort=True, - preset_values=preset, - default_option=default).run() + selected_locale = Menu( + _('Choose which locale language to use'), + list(locale_lang), + sort=True, + preset_values=preset + ).run() - return selected_locale + match selected_locale.type_: + case MenuSelectionType.Selection: return selected_locale.value + case MenuSelectionType.Esc: return preset -def select_locale_enc(default: str, preset: str = None) -> str: +def select_locale_enc(preset: str = None) -> str: locales = list_locales() locale_enc = set([locale.split()[1] for locale in locales]) - selected_locale = Menu(_('Choose which locale encoding to use'), - locale_enc, - sort=True, - preset_values=preset, - default_option=default).run() + selected_locale = Menu( + _('Choose which locale encoding to use'), + list(locale_enc), + sort=True, + preset_values=preset + ).run() - return selected_locale + match selected_locale.type_: + case MenuSelectionType.Selection: return selected_locale.value + case MenuSelectionType.Esc: return preset diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py index a6ff3111..567a2964 100644 --- a/archinstall/lib/user_interaction/manage_users_conf.py +++ b/archinstall/lib/user_interaction/manage_users_conf.py @@ -1,14 +1,12 @@ from __future__ import annotations -import logging import re -from typing import Any, Dict, TYPE_CHECKING, List +from typing import Any, Dict, TYPE_CHECKING, List, Optional +from .utils import get_password from ..menu import Menu from ..menu.list_manager import ListManager -from ..output import log -from ..storage import storage -from .utils import get_password +from ..models.users import User if TYPE_CHECKING: _: Any @@ -19,7 +17,7 @@ class UserList(ListManager): subclass of ListManager for the managing of user accounts """ - def __init__(self, prompt: str, lusers: dict, sudo: bool = None): + def __init__(self, prompt: str, lusers: List[User]): """ param: prompt type: str @@ -27,140 +25,83 @@ class UserList(ListManager): type: Dict param: sudo. boolean to determine if we handle superusers or users. If None handles both types """ - self.sudo = sudo - self.actions = [ + self._actions = [ str(_('Add a user')), str(_('Change password')), str(_('Promote/Demote user')), str(_('Delete User')) ] - super().__init__(prompt, lusers, self.actions, self.actions[0]) - - def reformat(self, data: Any) -> List[Any]: - def format_element(elem :str): - # secret gives away the length of the password - if data[elem].get('!password'): - pwd = '*' * 16 - else: - pwd = '' - if data[elem].get('sudoer'): - super_user = 'Superuser' - else: - super_user = ' ' - return f"{elem:16}: password {pwd:16} {super_user}" + super().__init__(prompt, lusers, self._actions, self._actions[0]) - return list(map(lambda x: format_element(x), data)) + def reformat(self, data: List[User]) -> Dict[str, User]: + return {e.display(): e for e in data} def action_list(self): - if self.target: - active_user = list(self.target.keys())[0] - else: - active_user = None - sudoer = self.target[active_user].get('sudoer', False) - if self.sudo is None: - return self.actions - if self.sudo and sudoer: - return self.actions - elif self.sudo and not sudoer: - return [self.actions[2]] - elif not self.sudo and sudoer: - return [self.actions[2]] + active_user = self.target if self.target else None + + if active_user is None: + return [self._actions[0]] else: - return self.actions + return self._actions[1:] - def exec_action(self, data: Any): + def exec_action(self, data: List[User]) -> List[User]: if self.target: - active_user = list(self.target.keys())[0] + active_user = self.target else: active_user = None - if self.action == self.actions[0]: # add - new_user = self.add_user() - # no unicity check, if exists will be replaced - data.update(new_user) - elif self.action == self.actions[1]: # change password - data[active_user]['!password'] = get_password( - prompt=str(_('Password for user "{}": ').format(active_user))) - elif self.action == self.actions[2]: # promote/demote - data[active_user]['sudoer'] = not data[active_user]['sudoer'] - elif self.action == self.actions[3]: # delete - del data[active_user] + if self.action == self._actions[0]: # add + new_user = self._add_user() + if new_user is not None: + # in case a user with the same username as an existing user + # was created we'll replace the existing one + data = [d for d in data if d.username != new_user.username] + data += [new_user] + elif self.action == self._actions[1]: # change password + prompt = str(_('Password for user "{}": ').format(active_user.username)) + new_password = get_password(prompt=prompt) + if new_password: + user = next(filter(lambda x: x == active_user, data), 1) + user.password = new_password + elif self.action == self._actions[2]: # promote/demote + user = next(filter(lambda x: x == active_user, data), 1) + user.sudo = False if user.sudo else True + elif self.action == self._actions[3]: # delete + data = [d for d in data if d != active_user] + + return data def _check_for_correct_username(self, username: str) -> bool: if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32: return True - log("The username you entered is invalid. Try again", level=logging.WARNING, fg='red') return False - def add_user(self): + def _add_user(self) -> Optional[User]: print(_('\nDefine a new user\n')) - prompt = str(_("User Name : ")) + prompt = str(_('Enter username (leave blank to skip): ')) + while True: - userid = input(prompt).strip(' ') - if not userid: - return {} # end - if not self._check_for_correct_username(userid): - pass + username = input(prompt).strip(' ') + if not username: + return None + if not self._check_for_correct_username(username): + prompt = str(_("The username you entered is invalid. Try again")) + '\n' + prompt else: break - if self.sudo: - sudoer = True - elif self.sudo is not None and not self.sudo: - sudoer = False - else: - sudoer = False - sudo_choice = Menu(str(_('Should {} be a superuser (sudoer)?')).format(userid), ['yes', 'no'], - skip=False, - preset_values='yes' if sudoer else 'no', - default_option='no').run() - sudoer = True if sudo_choice == 'yes' else False - - password = get_password(prompt=str(_('Password for user "{}": ').format(userid))) - - return {userid: {"!password": password, "sudoer": sudoer}} - - -def manage_users(prompt: str, sudo: bool) -> tuple[dict, dict]: - # TODO Filtering and some kind of simpler code - lusers = {} - if storage['arguments'].get('!superusers', {}): - lusers.update({ - uid: { - '!password': storage['arguments']['!superusers'][uid].get('!password'), - 'sudoer': True - } - for uid in storage['arguments'].get('!superusers', {}) - }) - if storage['arguments'].get('!users', {}): - lusers.update({ - uid: { - '!password': storage['arguments']['!users'][uid].get('!password'), - 'sudoer': False - } - for uid in storage['arguments'].get('!users', {}) - }) - # processing - lusers = UserList(prompt, lusers, sudo).run() - # return data - superusers = { - uid: { - '!password': lusers[uid].get('!password') - } - for uid in lusers if lusers[uid].get('sudoer', False) - } - users = {uid: {'!password': lusers[uid].get('!password')} for uid in lusers if not lusers[uid].get('sudoer', False)} - storage['arguments']['!superusers'] = superusers - storage['arguments']['!users'] = users - return superusers, users - - -def ask_for_superuser_account(prompt: str) -> Dict[str, Dict[str, str]]: - prompt = prompt if prompt else str(_('Define users with sudo privilege, by username: ')) - superusers, dummy = manage_users(prompt, sudo=True) - return superusers - - -def ask_for_additional_users(prompt: str = '') -> Dict[str, Dict[str, str | None]]: - prompt = prompt if prompt else _('Any additional users to install (leave blank for no users): ') - dummy, users = manage_users(prompt, sudo=False) + + password = get_password(prompt=str(_('Password for user "{}": ').format(username))) + + choice = Menu( + str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(), + skip=False, + default_option=Menu.no() + ).run() + + sudo = True if choice.value == Menu.yes() else False + return User(username, password, sudo) + + +def ask_for_additional_users(prompt: str = '', defined_users: List[User] = []) -> List[User]: + prompt = prompt if prompt else _('Enter username (leave blank to skip): ') + users = UserList(prompt, defined_users).run() return users diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py index 80c9106b..5154d8b1 100644 --- a/archinstall/lib/user_interaction/network_conf.py +++ b/archinstall/lib/user_interaction/network_conf.py @@ -4,6 +4,7 @@ import ipaddress import logging from typing import Any, Optional, TYPE_CHECKING, List, Union +from ..menu.menu import MenuSelectionType from ..menu.text_input import TextInput from ..models.network_configuration import NetworkConfiguration, NicType @@ -63,11 +64,17 @@ class ManualNetworkConfig(ListManager): elif self.action == self._action_delete: del data[iface_name] - def _select_iface(self, existing_ifaces: List[str]) -> Optional[str]: + return data + + def _select_iface(self, existing_ifaces: List[str]) -> Optional[Any]: all_ifaces = list_interfaces().values() available = set(all_ifaces) - set(existing_ifaces) - iface = Menu(str(_('Select interface to add')), list(available), skip=True).run() - return iface + choice = Menu(str(_('Select interface to add')), list(available), skip=True).run() + + if choice.type_ == MenuSelectionType.Esc: + return None + + return choice.value def _edit_iface(self, edit_iface :NetworkConfiguration): iface_name = edit_iface.iface @@ -75,9 +82,9 @@ class ManualNetworkConfig(ListManager): default_mode = 'DHCP (auto detect)' prompt = _('Select which mode to configure for "{}" or skip to use default mode "{}"').format(iface_name, default_mode) - mode = Menu(prompt, modes, default_option=default_mode).run() + mode = Menu(prompt, modes, default_option=default_mode, skip=False).run() - if mode == 'IP (static)': + if mode.value == 'IP (static)': while 1: prompt = _('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name) ip = TextInput(prompt, edit_iface.ip).run().strip() @@ -89,14 +96,14 @@ class ManualNetworkConfig(ListManager): log("You need to enter a valid IP in IP-config mode.", level=logging.WARNING, fg='red') # Implemented new check for correct gateway IP address + gateway = None + while 1: - gateway = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '), + gateway_input = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '), edit_iface.gateway).run().strip() try: - if len(gateway) == 0: - gateway = None - else: - ipaddress.ip_address(gateway) + if len(gateway_input) > 0: + ipaddress.ip_address(gateway_input) break except ValueError: log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red') @@ -107,6 +114,7 @@ class ManualNetworkConfig(ListManager): display_dns = None dns_input = TextInput(_('Enter your DNS servers (space separated, blank for none): '), display_dns).run().strip() + dns = [] if len(dns_input): dns = dns_input.split(' ') @@ -135,23 +143,28 @@ def ask_to_configure_network(preset: Union[None, NetworkConfiguration, List[Netw elif preset.type == 'network_manager': cursor_idx = 1 - nic = Menu(_( - 'Select one network interface to configure'), + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Select one network interface to configure'), list(network_options.values()), cursor_index=cursor_idx, - sort=False + sort=False, + explode_on_interrupt=True, + explode_warning=warning ).run() - if not nic: - return preset + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return None - if nic == network_options['none']: + if choice.value == network_options['none']: return None - elif nic == network_options['iso_config']: + elif choice.value == network_options['iso_config']: return NetworkConfiguration(NicType.ISO) - elif nic == network_options['network_manager']: + elif choice.value == network_options['network_manager']: return NetworkConfiguration(NicType.NM) - elif nic == network_options['manual']: + elif choice.value == network_options['manual']: manual = ManualNetworkConfig('Configure interfaces', preset) return manual.run_manual() diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py index af1d224f..bfff5705 100644 --- a/archinstall/lib/user_interaction/partitioning_conf.py +++ b/archinstall/lib/user_interaction/partitioning_conf.py @@ -1,12 +1,13 @@ from __future__ import annotations -from typing import List, Any, Dict, Union, TYPE_CHECKING, Callable +import copy +from typing import List, Any, Dict, Union, TYPE_CHECKING, Callable, Optional from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..output import log from ..disk.validators import fs_types -from ..disk.helpers import has_mountpoint if TYPE_CHECKING: from ..disk import BlockDevice @@ -19,9 +20,9 @@ def partition_overlap(partitions: list, start: str, end: str) -> bool: return False -def _current_partition_layout(partitions: List[Partition], with_idx: bool = False) -> str: +def current_partition_layout(partitions: List[Dict[str, Any]], with_idx: bool = False, with_title: bool = True) -> str: - def do_padding(name, max_len): + def do_padding(name: str, max_len: int): spaces = abs(len(str(name)) - max_len) + 2 pad_left = int(spaces / 2) pad_right = spaces - pad_left @@ -61,39 +62,54 @@ def _current_partition_layout(partitions: List[Partition], with_idx: bool = Fals current_layout += f'{row[:-1]}\n' - title = str(_('Current partition layout')) - return f'\n\n{title}:\n\n{current_layout}' + if with_title: + title = str(_('Current partition layout')) + return f'\n\n{title}:\n\n{current_layout}' + return current_layout -def select_partition(title :str, partitions :List[Partition], multiple :bool = False, filter :Callable = None) -> Union[int, List[int], None]: + +def _get_partitions(partitions :List[Partition], filter_ :Callable = None) -> List[str]: """ filter allows to filter out the indexes once they are set. Should return True if element is to be included """ partition_indexes = [] for i in range(len(partitions)): - if filter: - if filter(partitions[i]): + if filter_: + if filter_(partitions[i]): partition_indexes.append(str(i)) else: partition_indexes.append(str(i)) + + return partition_indexes + + +def select_partition( + title :str, + partitions :List[Partition], + multiple :bool = False, + filter_ :Callable = None +) -> Optional[int, List[int]]: + partition_indexes = _get_partitions(partitions, filter_) + if len(partition_indexes) == 0: return None - # old code without filter - # partition_indexes = list(map(str, range(len(partitions)))) - partition = Menu(title, partition_indexes, multi=multiple).run() + choice = Menu(title, partition_indexes, multi=multiple).run() - if partition is not None: - if isinstance(partition, list): - return [int(p) for p in partition] - else: - return int(partition) + if choice.type_ == MenuSelectionType.Esc: + return None - return None + if isinstance(choice.value, list): + return [int(p) for p in choice.value] + else: + return int(choice.value) -def get_default_partition_layout(block_devices: Union['BlockDevice', List['BlockDevice']], - advanced_options: bool = False) -> Dict[str, Any]: +def get_default_partition_layout( + block_devices: Union['BlockDevice', List['BlockDevice']], + advanced_options: bool = False +) -> Optional[Dict[str, Any]]: from ..disk import suggest_single_disk_layout, suggest_multi_disk_layout if len(block_devices) == 1: @@ -107,14 +123,15 @@ def select_individual_blockdevice_usage(block_devices: list) -> Dict[str, Any]: for device in block_devices: layout = manage_new_and_existing_partitions(device) - result[device.path] = layout return result -def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, Any]: # noqa: max-complexity: 50 +def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, Any]: # noqa: max-complexity: 50 block_device_struct = {"partitions": [partition.__dump__() for partition in block_device.partitions.values()]} + original_layout = copy.deepcopy(block_device_struct) + # Test code: [part.__dump__() for part in block_device.partitions.values()] # TODO: Squeeze in BTRFS subvolumes here @@ -129,11 +146,13 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, mark_bootable = str(_('Mark/Unmark a partition as bootable (automatic for /boot)')) set_filesystem_partition = str(_('Set desired filesystem for a partition')) set_btrfs_subvolumes = str(_('Set desired subvolumes on a btrfs partition')) + save_and_exit = str(_('Save and exit')) + cancel = str(_('Cancel')) while True: modes = [new_partition, suggest_partition_layout] - if len(block_device_struct['partitions']): + if len(block_device_struct['partitions']) > 0: modes += [ delete_partition, delete_all_partitions, @@ -143,20 +162,31 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, mark_bootable, mark_compressed, set_filesystem_partition, - set_btrfs_subvolumes, ] + indexes = _get_partitions( + block_device_struct["partitions"], + filter_=lambda x: True if x.get('filesystem', {}).get('format') == 'btrfs' else False + ) + + if len(indexes) > 0: + modes += [set_btrfs_subvolumes] + title = _('Select what to do with\n{}').format(block_device) # show current partition layout: if len(block_device_struct["partitions"]): - title += _current_partition_layout(block_device_struct['partitions']) + '\n' + title += current_partition_layout(block_device_struct['partitions']) + '\n' - task = Menu(title, modes, sort=False).run() + modes += [save_and_exit, cancel] - if not task: - break + task = Menu(title, modes, sort=False, skip=False).run() + task = task.value + if task == cancel: + return original_layout + elif task == save_and_exit: + break if task == new_partition: from ..disk import valid_parted_position @@ -165,7 +195,10 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, # # https://www.gnu.org/software/parted/manual/html_node/mklabel.html # name = input("Enter a desired name for the partition: ").strip() - fstype = Menu(_('Enter a desired filesystem type for the partition'), fs_types(), skip=False).run() + fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() + + if fs_choice.type_ == MenuSelectionType.Esc: + continue prompt = _('Enter the start sector (percentage or block number, default: {}): ').format( block_device.first_free_sector) @@ -197,7 +230,7 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, "mountpoint": None, "wipe": True, "filesystem": { - "format": fstype + "format": fs_choice.value } }) else: @@ -208,18 +241,15 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, from ..disk import suggest_single_disk_layout if len(block_device_struct["partitions"]): - prompt = _('{} contains queued partitions, this will remove those, are you sure?').format(block_device) - choice = Menu(prompt, ['yes', 'no'], default_option='no').run() + prompt = _('{}\ncontains queued partitions, this will remove those, are you sure?').format(block_device) + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.no(), skip=False).run() - if choice == 'no': + if choice.value == Menu.no(): continue block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path]) - - elif task is None: - return block_device_struct else: - current_layout = _current_partition_layout(block_device_struct['partitions'], with_idx=True) + current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True) if task == delete_partition: title = _('{}\n\nSelect by index which partitions to delete').format(current_layout) @@ -243,15 +273,14 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, block_device_struct["partitions"][partition]["filesystem"]["mount_options"].append("compress=zstd") elif task == delete_all_partitions: block_device_struct["partitions"] = [] + block_device_struct["wipe"] = True elif task == assign_mount_point: title = _('{}\n\nSelect by index which partition to mount where').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: - print( - _(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) - mountpoint = input( - _('Select where to mount partition (leave blank to remove mountpoint): ')).strip() + print(_(' * Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + mountpoint = input(_('Select where to mount partition (leave blank to remove mountpoint): ')).strip() if len(mountpoint): block_device_struct["partitions"][partition]['mountpoint'] = mountpoint @@ -273,14 +302,13 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if not block_device_struct["partitions"][partition].get('filesystem', None): block_device_struct["partitions"][partition]['filesystem'] = {} - fstype = Menu(_('Enter a desired filesystem type for the partition'), fs_types(), - skip=False).run() + fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run() - block_device_struct["partitions"][partition]['filesystem']['format'] = fstype + if fs_choice.type_ == MenuSelectionType.Selection: + block_device_struct["partitions"][partition]['filesystem']['format'] = fs_choice.value # Negate the current wipe marking - block_device_struct["partitions"][partition][ - 'wipe'] = not block_device_struct["partitions"][partition].get('wipe', False) + block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False) elif task == mark_encrypted: title = _('{}\n\nSelect which partition to mark as encrypted').format(current_layout) @@ -288,16 +316,16 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, if partition is not None: # Negate the current encryption marking - block_device_struct["partitions"][partition][ - 'encrypted'] = not block_device_struct["partitions"][partition].get('encrypted', False) + block_device_struct["partitions"][partition]['encrypted'] = \ + not block_device_struct["partitions"][partition].get('encrypted', False) elif task == mark_bootable: title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout) partition = select_partition(title, block_device_struct["partitions"]) if partition is not None: - block_device_struct["partitions"][partition][ - 'boot'] = not block_device_struct["partitions"][partition].get('boot', False) + block_device_struct["partitions"][partition]['boot'] = \ + not block_device_struct["partitions"][partition].get('boot', False) elif task == set_filesystem_partition: title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout) @@ -308,16 +336,18 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, block_device_struct["partitions"][partition]['filesystem'] = {} fstype_title = _('Enter a desired filesystem type for the partition: ') - fstype = Menu(fstype_title, fs_types(), skip=False).run() + fs_choice = Menu(fstype_title, fs_types()).run() - block_device_struct["partitions"][partition]['filesystem']['format'] = fstype + if fs_choice.type_ == MenuSelectionType.Selection: + block_device_struct["partitions"][partition]['filesystem']['format'] = fs_choice.value elif task == set_btrfs_subvolumes: from .subvolume_config import SubvolumeList # TODO get preexisting partitions title = _('{}\n\nSelect which partition to set subvolumes on').format(current_layout) - partition = select_partition(title, block_device_struct["partitions"],filter=lambda x:True if x.get('filesystem',{}).get('format') == 'btrfs' else False) + partition = select_partition(title, block_device_struct["partitions"],filter_=lambda x:True if x.get('filesystem',{}).get('format') == 'btrfs' else False) + if partition is not None: if not block_device_struct["partitions"][partition].get('btrfs', {}): block_device_struct["partitions"][partition]['btrfs'] = {} @@ -333,19 +363,30 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str, return block_device_struct +def select_encrypted_partitions( + title :str, + partitions :List[Partition], + multiple :bool = True, + filter_ :Callable = None +) -> Optional[int, List[int]]: + partition_indexes = _get_partitions(partitions, filter_) -def select_encrypted_partitions(block_devices: dict, password: str) -> dict: - for device in block_devices: - for partition in block_devices[device]['partitions']: - if partition.get('mountpoint', None) != '/boot': - partition['encrypted'] = True - partition['!password'] = password + if len(partition_indexes) == 0: + return None + + title = _('Select which partitions to mark for formatting:') - if not has_mountpoint(partition,'/'): - # Tell the upcoming steps to generate a key-file for non root mounts. - partition['generate-encryption-key-file'] = True + # show current partition layout: + if len(partitions): + title += current_partition_layout(partitions) + '\n' - return block_devices + choice = Menu(title, partition_indexes, multi=multiple).run() - # TODO: Next version perhaps we can support mixed multiple encrypted partitions - # Users might want to single out a partition for non-encryption to share between dualboot etc. + if choice.type_ == MenuSelectionType.Esc: + return None + + if isinstance(choice.value, list): + for partition_index in choice.value: + yield int(partition_index) + else: + yield (partition_index) diff --git a/archinstall/lib/user_interaction/save_conf.py b/archinstall/lib/user_interaction/save_conf.py index c52b97e2..f542bc9b 100644 --- a/archinstall/lib/user_interaction/save_conf.py +++ b/archinstall/lib/user_interaction/save_conf.py @@ -5,6 +5,7 @@ from typing import Any, Dict, TYPE_CHECKING from ..configuration import ConfigurationOutput from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..output import log if TYPE_CHECKING: @@ -45,14 +46,16 @@ def save_config(config: Dict): 'all': str(_('Save all')) } - selection = Menu(_('Choose which configuration to save'), - list(options.values()), - sort=False, - skip=True, - preview_size=0.75, - preview_command=preview).run() + choice = Menu( + _('Choose which configuration to save'), + list(options.values()), + sort=False, + skip=True, + preview_size=0.75, + preview_command=preview + ).run() - if not selection: + if choice.type_ == MenuSelectionType.Esc: return while True: @@ -62,13 +65,13 @@ def save_config(config: Dict): break log(_('Not a valid directory: {}').format(dest_path), fg='red') - if options['user_config'] == selection: + if options['user_config'] == choice.value: config_output.save_user_config(dest_path) - elif options['user_creds'] == selection: + elif options['user_creds'] == choice.value: config_output.save_user_creds(dest_path) - elif options['disk_layout'] == selection: + elif options['disk_layout'] == choice.value: config_output.save_disk_layout(dest_path) - elif options['all'] == selection: + elif options['all'] == choice.value: config_output.save_user_config(dest_path) config_output.save_user_creds(dest_path) - config_output.save_disk_layout + config_output.save_disk_layout(dest_path) diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py index 0515876b..af783639 100644 --- a/archinstall/lib/user_interaction/subvolume_config.py +++ b/archinstall/lib/user_interaction/subvolume_config.py @@ -1,9 +1,11 @@ -from typing import List, Any, Dict +from typing import Dict, List from ..menu.list_manager import ListManager +from ..menu.menu import MenuSelectionType from ..menu.selection_menu import Selector, GeneralMenu from ..menu.text_input import TextInput from ..menu import Menu + """ UI classes """ @@ -14,7 +16,7 @@ class SubvolumeList(ListManager): self.ObjectDefaultAction = str(_('Add')) super().__init__(prompt,list,None,self.ObjectNullAction,self.ObjectDefaultAction) - def reformat(self, data: Any) -> List[Any]: + def reformat(self, data: Dict) -> Dict: def presentation(key :str, value :Dict): text = _(" Subvolume :{:16}").format(key) if isinstance(value,str): @@ -24,18 +26,20 @@ class SubvolumeList(ListManager): text += _(" mounted at {:16}").format(value['mountpoint']) else: text += (' ' * 28) + if value.get('options',[]): text += _(" with option {}").format(', '.join(value['options'])) return text - return sorted(list(map(lambda x:presentation(x,data[x]),data))) + formatted = {presentation(k, v): k for k, v in data.items()} + return {k: v for k, v in sorted(formatted.items(), key=lambda e: e[0])} def action_list(self): return super().action_list() - def exec_action(self, data: Any): + def exec_action(self, data: Dict): if self.target: - origkey,origval = list(self.target.items())[0] + origkey, origval = list(self.target.items())[0] else: origkey = None @@ -46,13 +50,15 @@ class SubvolumeList(ListManager): self.target = {} print(_('\n Fill the desired values for a new subvolume \n')) with SubvolumeMenu(self.target,self.action) as add_menu: - for data in ['name','mountpoint','options']: - add_menu.exec_option(data) + for elem in ['name','mountpoint','options']: + add_menu.exec_option(elem) else: SubvolumeMenu(self.target,self.action).run() data.update(self.target) + return data + class SubvolumeMenu(GeneralMenu): def __init__(self,parameters,action=None): @@ -124,7 +130,17 @@ class SubvolumeMenu(GeneralMenu): def _select_subvolume_mount_point(self,value): return TextInput(str(_("Select a mount point :")),value).run() - def _select_subvolume_options(self,value): + def _select_subvolume_options(self,value) -> List[str]: # def __init__(self, title, p_options, skip=True, multi=False, default_option=None, sort=True): - return Menu(str(_("Select the desired subvolume options ")),['nodatacow','compress'], - skip=True,preset_values=value,multi=True).run() + choice = Menu( + str(_("Select the desired subvolume options ")), + ['nodatacow','compress'], + skip=True, + preset_values=value, + multi=True + ).run() + + if choice.type_ == MenuSelectionType.Selection: + return choice.value + + return [] diff --git a/archinstall/lib/user_interaction/system_conf.py b/archinstall/lib/user_interaction/system_conf.py index 0284dc5f..78daa6a5 100644 --- a/archinstall/lib/user_interaction/system_conf.py +++ b/archinstall/lib/user_interaction/system_conf.py @@ -6,10 +6,9 @@ from ..disk import all_blockdevices from ..exceptions import RequirementError from ..hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_intel_graphics, has_nvidia_graphics from ..menu import Menu +from ..menu.menu import MenuSelectionType from ..storage import storage -from ..translation import DeferredTranslation - if TYPE_CHECKING: _: Any @@ -25,13 +24,22 @@ def select_kernel(preset: List[str] = None) -> List[str]: kernels = ["linux", "linux-lts", "linux-zen", "linux-pae"] default_kernel = "linux" - selected_kernels = Menu(_('Choose which kernels to use or leave blank for default "{}"').format(default_kernel), - kernels, - sort=True, - multi=True, - preset_values=preset, - default_option=default_kernel).run() - return selected_kernels + warning = str(_('Are you sure you want to reset this setting?')) + + choice = Menu( + _('Choose which kernels to use or leave blank for default "{}"').format(default_kernel), + kernels, + sort=True, + multi=True, + preset_values=preset, + explode_on_interrupt=True, + explode_warning=warning + ).run() + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Ctrl_c: return [] + case MenuSelectionType.Selection: return choice.value def select_harddrives(preset: List[str] = []) -> List[str]: @@ -49,18 +57,27 @@ def select_harddrives(preset: List[str] = []) -> List[str]: else: preset_disks = {} - selected_harddrive = Menu(_('Select one or more hard drives to use and configure'), - list(options.keys()), - preset_values=list(preset_disks.keys()), - multi=True).run() + title = str(_('Select one or more hard drives to use and configure\n')) + title += str(_('Any modifications to the existing setting will reset the disk layout!')) - if selected_harddrive and len(selected_harddrive) > 0: - return [options[i] for i in selected_harddrive] + warning = str(_('If you reset the harddrive selection this will also reset the current disk layout. Are you sure?')) - return [] + selected_harddrive = Menu( + title, + list(options.keys()), + preset_values=list(preset_disks.keys()), + multi=True, + explode_on_interrupt=True, + explode_warning=warning + ).run() + match selected_harddrive.type_: + case MenuSelectionType.Ctrl_c: return [] + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return [options[i] for i in selected_harddrive.value] -def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS, force_ask: bool = False) -> str: + +def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS) -> str: """ Some what convoluted function, whose job is simple. Select a graphics driver from a pre-defined set of popular options. @@ -73,72 +90,85 @@ def select_driver(options: Dict[str, Any] = AVAILABLE_GFX_DRIVERS, force_ask: bo if drivers: arguments = storage.get('arguments', {}) - title = DeferredTranslation('') + title = '' if has_amd_graphics(): - title += _( + title += str(_( 'For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.' - ) + '\n' + )) + '\n' if has_intel_graphics(): - title += _( + title += str(_( 'For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n' - ) + )) if has_nvidia_graphics(): - title += _( + title += str(_( 'For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n' - ) + )) - if not arguments.get('gfx_driver', None) or force_ask: - title += _('\n\nSelect a graphics driver or leave blank to install all open-source drivers') - arguments['gfx_driver'] = Menu(title, drivers).run() + title += str(_('\n\nSelect a graphics driver or leave blank to install all open-source drivers')) + choice = Menu(title, drivers).run() - if arguments.get('gfx_driver', None) is None: - arguments['gfx_driver'] = _("All open-source (default)") + if choice.type_ != MenuSelectionType.Selection: + return arguments.get('gfx_driver') - return options.get(arguments.get('gfx_driver')) + arguments['gfx_driver'] = choice.value + return options.get(choice.value) raise RequirementError("Selecting drivers require a least one profile to be given as an option.") def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> str: - if preset == 'systemd-bootctl': - preset_val = 'systemd-boot' if advanced_options else 'no' + preset_val = 'systemd-boot' if advanced_options else Menu.no() elif preset == 'grub-install': - preset_val = 'grub' if advanced_options else 'yes' + preset_val = 'grub' if advanced_options else Menu.yes() else: preset_val = preset bootloader = "systemd-bootctl" if has_uefi() else "grub-install" + if has_uefi(): if not advanced_options: - bootloader_choice = Menu(_('Would you like to use GRUB as a bootloader instead of systemd-boot?'), - ['yes', 'no'], - preset_values=preset_val, - default_option='no').run() - - if bootloader_choice == "yes": - bootloader = "grub-install" + selection = Menu( + _('Would you like to use GRUB as a bootloader instead of systemd-boot?'), + Menu.yes_no(), + preset_values=preset_val, + default_option=Menu.no() + ).run() + + match selection.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: bootloader = 'grub-install' if selection.value == Menu.yes() else bootloader else: # We use the common names for the bootloader as the selection, and map it back to the expected values. choices = ['systemd-boot', 'grub', 'efistub'] selection = Menu(_('Choose a bootloader'), choices, preset_values=preset_val).run() - if selection != "": - if selection == 'systemd-boot': + + value = '' + match selection.type_: + case MenuSelectionType.Esc: value = preset_val + case MenuSelectionType.Selection: value = selection.value + + if value != "": + if value == 'systemd-boot': bootloader = 'systemd-bootctl' - elif selection == 'grub': + elif value == 'grub': bootloader = 'grub-install' else: - bootloader = selection + bootloader = value return bootloader def ask_for_swap(preset: bool = True) -> bool: if preset: - preset_val = 'yes' + preset_val = Menu.yes() else: - preset_val = 'no' + preset_val = Menu.no() + prompt = _('Would you like to use swap on zram?') - choice = Menu(prompt, ['yes', 'no'], default_option='yes', preset_values=preset_val).run() - return False if choice == 'no' else True + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), preset_values=preset_val).run() + + match choice.type_: + case MenuSelectionType.Esc: return preset + case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py index 48b55e8c..fa079bc2 100644 --- a/archinstall/lib/user_interaction/utils.py +++ b/archinstall/lib/user_interaction/utils.py @@ -28,12 +28,9 @@ def check_password_strong(passwd: str) -> bool: symbol_count += 40 if symbol_count**len(passwd) < 10e20: - - prompt = _("The password you are using seems to be weak,") - prompt += _("are you sure you want to use it?") - - choice = Menu(prompt, ["yes", "no"], default_option="yes").run() - return choice == "yes" + prompt = str(_("The password you are using seems to be weak, are you sure you want to use it?")) + choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() + return choice.value == Menu.yes() return True @@ -43,7 +40,6 @@ def get_password(prompt: str = '') -> Optional[str]: prompt = _("Enter a password: ") while passwd := getpass.getpass(prompt): - if len(passwd.strip()) <= 0: break @@ -56,6 +52,7 @@ def get_password(prompt: str = '') -> Optional[str]: continue return passwd + return None @@ -84,12 +81,13 @@ def do_countdown() -> bool: if SIG_TRIGGER: prompt = _('Do you really want to abort?') - choice = Menu(prompt, ['yes', 'no'], skip=False).run() - if choice == 'yes': + choice = Menu(prompt, Menu.yes_no(), skip=False).run() + if choice.value == Menu.yes(): exit(0) if SIG_TRIGGER is False: sys.stdin.read() + SIG_TRIGGER = False signal.signal(signal.SIGINT, sig_handler) |