Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/user_interaction
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib/user_interaction')
-rw-r--r--archinstall/lib/user_interaction/__init__.py2
-rw-r--r--archinstall/lib/user_interaction/disk_conf.py51
-rw-r--r--archinstall/lib/user_interaction/general_conf.py138
-rw-r--r--archinstall/lib/user_interaction/locale_conf.py35
-rw-r--r--archinstall/lib/user_interaction/manage_users_conf.py177
-rw-r--r--archinstall/lib/user_interaction/network_conf.py51
-rw-r--r--archinstall/lib/user_interaction/partitioning_conf.py171
-rw-r--r--archinstall/lib/user_interaction/save_conf.py27
-rw-r--r--archinstall/lib/user_interaction/subvolume_config.py36
-rw-r--r--archinstall/lib/user_interaction/system_conf.py126
-rw-r--r--archinstall/lib/user_interaction/utils.py16
11 files changed, 459 insertions, 371 deletions
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)