parent
ffa130fe99
commit
b186fb3f64
|
|
@ -51,12 +51,12 @@ class DesktopProfile(Profile):
|
|||
|
||||
return None
|
||||
|
||||
def _do_on_select_profiles(self) -> None:
|
||||
async def _do_on_select_profiles(self) -> None:
|
||||
for profile in self.current_selection:
|
||||
profile.do_on_select()
|
||||
await profile.do_on_select()
|
||||
|
||||
@override
|
||||
def do_on_select(self) -> SelectResult:
|
||||
async def do_on_select(self) -> SelectResult:
|
||||
items = [
|
||||
MenuItem(
|
||||
p.name,
|
||||
|
|
@ -69,7 +69,7 @@ class DesktopProfile(Profile):
|
|||
group = MenuItemGroup(items, sort_items=True, sort_case_sensitive=False)
|
||||
group.set_selected_by_value(self.current_selection)
|
||||
|
||||
result = Selection[Self](
|
||||
result = await Selection[Self](
|
||||
group,
|
||||
multi=True,
|
||||
allow_reset=True,
|
||||
|
|
@ -80,7 +80,7 @@ class DesktopProfile(Profile):
|
|||
match result.type_:
|
||||
case ResultType.Selection:
|
||||
self.current_selection = result.get_values()
|
||||
self._do_on_select_profiles()
|
||||
await self._do_on_select_profiles()
|
||||
return SelectResult.NewSelection
|
||||
case ResultType.Skip:
|
||||
return SelectResult.SameSelection
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class HyprlandProfile(XorgProfile):
|
|||
return [pref]
|
||||
return []
|
||||
|
||||
def _select_seat_access(self) -> None:
|
||||
async def _select_seat_access(self) -> None:
|
||||
# need to activate seat service and add to seat group
|
||||
header = tr('Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')
|
||||
header += '\n' + tr('Choose an option to give Hyprland access to your hardware') + '\n'
|
||||
|
|
@ -56,7 +56,7 @@ class HyprlandProfile(XorgProfile):
|
|||
default = self.custom_settings.get('seat_access', None)
|
||||
group.set_default_by_value(default)
|
||||
|
||||
result = Selection[SeatAccess](
|
||||
result = await Selection[SeatAccess](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -66,5 +66,5 @@ class HyprlandProfile(XorgProfile):
|
|||
self.custom_settings['seat_access'] = result.get_value().value
|
||||
|
||||
@override
|
||||
def do_on_select(self) -> None:
|
||||
self._select_seat_access()
|
||||
async def do_on_select(self) -> None:
|
||||
await self._select_seat_access()
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class LabwcProfile(XorgProfile):
|
|||
return [pref]
|
||||
return []
|
||||
|
||||
def _select_seat_access(self) -> None:
|
||||
async def _select_seat_access(self) -> None:
|
||||
# need to activate seat service and add to seat group
|
||||
header = tr('labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')
|
||||
header += '\n' + tr('Choose an option to give labwc access to your hardware') + '\n'
|
||||
|
|
@ -53,7 +53,7 @@ class LabwcProfile(XorgProfile):
|
|||
default = self.custom_settings.get('seat_access', None)
|
||||
group.set_default_by_value(default)
|
||||
|
||||
result = Selection[SeatAccess](
|
||||
result = await Selection[SeatAccess](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -63,5 +63,5 @@ class LabwcProfile(XorgProfile):
|
|||
self.custom_settings['seat_access'] = result.get_value().value
|
||||
|
||||
@override
|
||||
def do_on_select(self) -> None:
|
||||
self._select_seat_access()
|
||||
async def do_on_select(self) -> None:
|
||||
await self._select_seat_access()
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class NiriProfile(XorgProfile):
|
|||
return [pref]
|
||||
return []
|
||||
|
||||
def _select_seat_access(self) -> None:
|
||||
async def _select_seat_access(self) -> None:
|
||||
# need to activate seat service and add to seat group
|
||||
header = tr('niri needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')
|
||||
header += '\n' + tr('Choose an option to give niri access to your hardware') + '\n'
|
||||
|
|
@ -61,7 +61,7 @@ class NiriProfile(XorgProfile):
|
|||
default = self.custom_settings.get('seat_access', None)
|
||||
group.set_default_by_value(default)
|
||||
|
||||
result = Selection[SeatAccess](
|
||||
result = await Selection[SeatAccess](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -71,5 +71,5 @@ class NiriProfile(XorgProfile):
|
|||
self.custom_settings['seat_access'] = result.get_value().value
|
||||
|
||||
@override
|
||||
def do_on_select(self) -> None:
|
||||
self._select_seat_access()
|
||||
async def do_on_select(self) -> None:
|
||||
await self._select_seat_access()
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class SwayProfile(XorgProfile):
|
|||
return [pref]
|
||||
return []
|
||||
|
||||
def _select_seat_access(self) -> None:
|
||||
async def _select_seat_access(self) -> None:
|
||||
# need to activate seat service and add to seat group
|
||||
header = tr('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')
|
||||
header += '\n' + tr('Choose an option to give Sway access to your hardware') + '\n'
|
||||
|
|
@ -63,7 +63,7 @@ class SwayProfile(XorgProfile):
|
|||
default = self.custom_settings.get('seat_access', None)
|
||||
group.set_default_by_value(default)
|
||||
|
||||
result = Selection[SeatAccess](
|
||||
result = await Selection[SeatAccess](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -73,5 +73,5 @@ class SwayProfile(XorgProfile):
|
|||
self.custom_settings['seat_access'] = result.get_value().value
|
||||
|
||||
@override
|
||||
def do_on_select(self) -> None:
|
||||
self._select_seat_access()
|
||||
async def do_on_select(self) -> None:
|
||||
await self._select_seat_access()
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class Profile:
|
|||
"""
|
||||
return {}
|
||||
|
||||
def do_on_select(self) -> SelectResult | None:
|
||||
async def do_on_select(self) -> SelectResult | None:
|
||||
"""
|
||||
Hook that will be called when a profile is selected
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class ServerProfile(Profile):
|
|||
)
|
||||
|
||||
@override
|
||||
def do_on_select(self) -> SelectResult:
|
||||
async def do_on_select(self) -> SelectResult:
|
||||
items = [
|
||||
MenuItem(
|
||||
p.name,
|
||||
|
|
@ -36,7 +36,7 @@ class ServerProfile(Profile):
|
|||
group = MenuItemGroup(items, sort_items=True)
|
||||
group.set_selected_by_value(self.current_selection)
|
||||
|
||||
result = Selection[Self](
|
||||
result = await Selection[Self](
|
||||
group,
|
||||
allow_reset=True,
|
||||
allow_skip=True,
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
|
|||
)
|
||||
|
||||
@override
|
||||
def run(self) -> ApplicationConfiguration:
|
||||
super().run()
|
||||
async def show(self) -> ApplicationConfiguration | None:
|
||||
_ = await super().show()
|
||||
return self._app_config
|
||||
|
||||
def _define_menu_options(self) -> list[MenuItem]:
|
||||
|
|
@ -116,13 +116,13 @@ class ApplicationMenu(AbstractSubMenu[ApplicationConfiguration]):
|
|||
return None
|
||||
|
||||
|
||||
def select_power_management(preset: PowerManagementConfiguration | None = None) -> PowerManagementConfiguration | None:
|
||||
async def select_power_management(preset: PowerManagementConfiguration | None = None) -> PowerManagementConfiguration | None:
|
||||
group = MenuItemGroup.from_enum(PowerManagement)
|
||||
|
||||
if preset:
|
||||
group.set_focus_by_value(preset.power_management)
|
||||
|
||||
result = Selection[PowerManagement](
|
||||
result = await Selection[PowerManagement](
|
||||
group,
|
||||
allow_skip=True,
|
||||
allow_reset=True,
|
||||
|
|
@ -137,11 +137,11 @@ def select_power_management(preset: PowerManagementConfiguration | None = None)
|
|||
return None
|
||||
|
||||
|
||||
def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfiguration | None:
|
||||
async def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfiguration | None:
|
||||
header = tr('Would you like to configure Bluetooth?') + '\n'
|
||||
preset_val = preset.enabled if preset else False
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
preset=preset_val,
|
||||
|
|
@ -156,11 +156,11 @@ def select_bluetooth(preset: BluetoothConfiguration | None) -> BluetoothConfigur
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_print_service(preset: PrintServiceConfiguration | None) -> PrintServiceConfiguration | None:
|
||||
async def select_print_service(preset: PrintServiceConfiguration | None) -> PrintServiceConfiguration | None:
|
||||
header = tr('Would you like to configure the print service?') + '\n'
|
||||
preset_val = preset.enabled if preset else False
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
preset=preset_val,
|
||||
|
|
@ -176,14 +176,14 @@ def select_print_service(preset: PrintServiceConfiguration | None) -> PrintServi
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_audio(preset: AudioConfiguration | None = None) -> AudioConfiguration | None:
|
||||
async def select_audio(preset: AudioConfiguration | None = None) -> AudioConfiguration | None:
|
||||
items = [MenuItem(a.value, value=a) for a in Audio]
|
||||
group = MenuItemGroup(items)
|
||||
|
||||
if preset:
|
||||
group.set_focus_by_value(preset.audio)
|
||||
|
||||
result = Selection[Audio](
|
||||
result = await Selection[Audio](
|
||||
group,
|
||||
header=tr('Select audio configuration'),
|
||||
allow_skip=True,
|
||||
|
|
@ -198,13 +198,13 @@ def select_audio(preset: AudioConfiguration | None = None) -> AudioConfiguration
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_firewall(preset: FirewallConfiguration | None = None) -> FirewallConfiguration | None:
|
||||
async def select_firewall(preset: FirewallConfiguration | None = None) -> FirewallConfiguration | None:
|
||||
group = MenuItemGroup.from_enum(Firewall)
|
||||
|
||||
if preset:
|
||||
group.set_focus_by_value(preset.firewall)
|
||||
|
||||
result = Selection[Firewall](
|
||||
result = await Selection[Firewall](
|
||||
group,
|
||||
allow_skip=True,
|
||||
allow_reset=True,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from archinstall.lib.output import debug, error, logger, warn
|
|||
from archinstall.lib.plugins import load_plugin
|
||||
from archinstall.lib.translationhandler import Language, tr, translation_handler
|
||||
from archinstall.lib.version import get_version
|
||||
from archinstall.tui.ui.components import tui
|
||||
|
||||
|
||||
@p_dataclass
|
||||
|
|
@ -491,16 +492,17 @@ class ArchConfigHandler:
|
|||
debug(f'Error decrypting credentials file: {err}')
|
||||
raise err from err
|
||||
else:
|
||||
incorrect_password = False
|
||||
header = tr('Enter credentials file decryption password')
|
||||
wrong_pwd_text = tr('Incorrect password')
|
||||
prompt = header
|
||||
|
||||
while True:
|
||||
prompt = f'{header}\n\n' + tr('Incorrect password') if incorrect_password else ''
|
||||
|
||||
decryption_pwd = get_password(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
skip_confirmation=True,
|
||||
decryption_pwd: Password | None = tui.run(
|
||||
lambda p=prompt: get_password( # type: ignore[misc]
|
||||
header=p,
|
||||
allow_skip=False,
|
||||
skip_confirmation=True,
|
||||
)
|
||||
)
|
||||
|
||||
if not decryption_pwd:
|
||||
|
|
@ -512,7 +514,7 @@ class ArchConfigHandler:
|
|||
except ValueError as err:
|
||||
if 'Invalid password' in str(err):
|
||||
debug('Incorrect credentials file decryption password')
|
||||
incorrect_password = True
|
||||
prompt = f'{header}' + f'\n\n{wrong_pwd_text}'
|
||||
else:
|
||||
debug(f'Error decrypting credentials file: {err}')
|
||||
raise err from err
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]):
|
|||
)
|
||||
|
||||
@override
|
||||
def run(self) -> AuthenticationConfiguration | None:
|
||||
return super().run()
|
||||
async def show(self) -> AuthenticationConfiguration | None:
|
||||
return await super().show()
|
||||
|
||||
def _define_menu_options(self) -> list[MenuItem]:
|
||||
return [
|
||||
|
|
@ -56,9 +56,9 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]):
|
|||
),
|
||||
]
|
||||
|
||||
def _create_user_account(self, preset: list[User] | None = None) -> list[User]:
|
||||
async def _create_user_account(self, preset: list[User] | None = None) -> list[User]:
|
||||
preset = [] if preset is None else preset
|
||||
users = select_users(preset=preset)
|
||||
users = await select_users(preset=preset)
|
||||
return users
|
||||
|
||||
def _prev_users(self, item: MenuItem) -> str | None:
|
||||
|
|
@ -99,12 +99,12 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]):
|
|||
return None
|
||||
|
||||
|
||||
def select_root_password() -> Password | None:
|
||||
password = get_password(header=tr('Enter root password'), allow_skip=True)
|
||||
async def select_root_password() -> Password | None:
|
||||
password = await get_password(header=tr('Enter root password'), allow_skip=True)
|
||||
return password
|
||||
|
||||
|
||||
def select_u2f_login(preset: U2FLoginConfiguration | None) -> U2FLoginConfiguration | None:
|
||||
async def select_u2f_login(preset: U2FLoginConfiguration | None) -> U2FLoginConfiguration | None:
|
||||
devices = Fido2.get_fido2_devices()
|
||||
if not devices:
|
||||
return None
|
||||
|
|
@ -118,7 +118,7 @@ def select_u2f_login(preset: U2FLoginConfiguration | None) -> U2FLoginConfigurat
|
|||
if preset is not None:
|
||||
group.set_selected_by_value(preset.u2f_login_method)
|
||||
|
||||
result = Selection[U2FLoginMethod](
|
||||
result = await Selection[U2FLoginMethod](
|
||||
group,
|
||||
allow_skip=True,
|
||||
allow_reset=True,
|
||||
|
|
@ -129,7 +129,7 @@ def select_u2f_login(preset: U2FLoginConfiguration | None) -> U2FLoginConfigurat
|
|||
u2f_method = result.get_value()
|
||||
header = tr('Enable passwordless sudo?')
|
||||
|
||||
result_sudo = Confirmation(
|
||||
result_sudo = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
preset=False,
|
||||
|
|
|
|||
|
|
@ -86,12 +86,12 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
|
|||
return tr('Will install to custom location with NVRAM entry')
|
||||
|
||||
@override
|
||||
def run(self) -> BootloaderConfiguration:
|
||||
super().run()
|
||||
async def show(self) -> BootloaderConfiguration:
|
||||
_ = await super().show()
|
||||
return self._bootloader_conf
|
||||
|
||||
def _select_bootloader(self, preset: Bootloader | None) -> Bootloader | None:
|
||||
bootloader = select_bootloader(preset, self._uefi, self._skip_boot)
|
||||
async def _select_bootloader(self, preset: Bootloader | None) -> Bootloader | None:
|
||||
bootloader = await select_bootloader(preset, self._uefi, self._skip_boot)
|
||||
|
||||
if bootloader:
|
||||
# Update UKI option based on bootloader
|
||||
|
|
@ -117,10 +117,10 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
|
|||
|
||||
return bootloader
|
||||
|
||||
def _select_uki(self, preset: bool) -> bool:
|
||||
async def _select_uki(self, preset: bool) -> bool:
|
||||
prompt = tr('Would you like to use unified kernel images?') + '\n'
|
||||
|
||||
result = Confirmation(header=prompt, allow_skip=True, preset=preset).show()
|
||||
result = await Confirmation(header=prompt, allow_skip=True, preset=preset).show()
|
||||
|
||||
match result.type_:
|
||||
case ResultType.Skip:
|
||||
|
|
@ -130,7 +130,7 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
|
|||
case ResultType.Reset:
|
||||
raise ValueError('Unhandled result type')
|
||||
|
||||
def _select_removable(self, preset: bool) -> bool:
|
||||
async def _select_removable(self, preset: bool) -> bool:
|
||||
prompt = (
|
||||
tr('Would you like to install the bootloader to the default removable media search location?')
|
||||
+ '\n\n'
|
||||
|
|
@ -162,7 +162,7 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
|
|||
+ '\n'
|
||||
)
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=True,
|
||||
preset=preset,
|
||||
|
|
@ -177,7 +177,7 @@ class BootloaderMenu(AbstractSubMenu[BootloaderConfiguration]):
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_bootloader(
|
||||
async def select_bootloader(
|
||||
preset: Bootloader | None,
|
||||
uefi: bool,
|
||||
skip_boot: bool = False,
|
||||
|
|
@ -202,7 +202,7 @@ def select_bootloader(
|
|||
group.set_default_by_value(default)
|
||||
group.set_focus_by_value(preset)
|
||||
|
||||
result = Selection[Bootloader](
|
||||
result = await Selection[Bootloader](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@ class ConfigurationOutput:
|
|||
debug(' -- Chosen configuration --')
|
||||
debug(self.user_config_to_json())
|
||||
|
||||
def confirm_config(self) -> bool:
|
||||
async def confirm_config(self) -> bool:
|
||||
header = f'{tr("The specified configuration will be applied")}. '
|
||||
header += tr('Would you like to continue?') + '\n'
|
||||
|
||||
group = MenuItemGroup.yes_no()
|
||||
group.set_preview_for_all(lambda x: self.user_config_to_json())
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
group=group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -123,7 +123,7 @@ class ConfigurationOutput:
|
|||
self.save_user_creds(save_path, password=password)
|
||||
|
||||
|
||||
def save_config(config: ArchConfig) -> None:
|
||||
async def save_config(config: ArchConfig) -> None:
|
||||
def preview(item: MenuItem) -> str | None:
|
||||
match item.value:
|
||||
case 'user_config':
|
||||
|
|
@ -161,7 +161,7 @@ def save_config(config: ArchConfig) -> None:
|
|||
]
|
||||
|
||||
group = MenuItemGroup(items)
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
allow_skip=True,
|
||||
preview_location='right',
|
||||
|
|
@ -178,7 +178,7 @@ def save_config(config: ArchConfig) -> None:
|
|||
readline.set_completer_delims('\t\n=')
|
||||
readline.parse_and_bind('tab: complete')
|
||||
|
||||
dest_path = prompt_dir(
|
||||
dest_path = await prompt_dir(
|
||||
tr('Enter a directory for the configuration(s) to be saved') + '\n',
|
||||
allow_skip=True,
|
||||
)
|
||||
|
|
@ -188,7 +188,7 @@ def save_config(config: ArchConfig) -> None:
|
|||
|
||||
header = tr('Do you want to save the configuration file(s) to {}?').format(dest_path)
|
||||
|
||||
save_result = Confirmation(
|
||||
save_result = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
preset=True,
|
||||
|
|
@ -205,7 +205,7 @@ def save_config(config: ArchConfig) -> None:
|
|||
|
||||
header = tr('Do you want to encrypt the user_credentials.json file?')
|
||||
|
||||
enc_result = Confirmation(
|
||||
enc_result = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
preset=False,
|
||||
|
|
@ -214,7 +214,7 @@ def save_config(config: ArchConfig) -> None:
|
|||
enc_password: str | None = None
|
||||
if enc_result.type_ == ResultType.Selection:
|
||||
if enc_result.get_value():
|
||||
password = get_password(
|
||||
password = await get_password(
|
||||
header=tr('Credentials file encryption password'),
|
||||
allow_skip=True,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
]
|
||||
|
||||
@override
|
||||
def run(self) -> DiskLayoutConfiguration | None: # type: ignore[override]
|
||||
config: DiskMenuConfig | None = super().run()
|
||||
async def show(self) -> DiskLayoutConfiguration | None: # type: ignore[override]
|
||||
config: DiskMenuConfig | None = await super().show()
|
||||
if config is None:
|
||||
return None
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
|
||||
return False
|
||||
|
||||
def _select_disk_encryption(self, preset: DiskEncryption | None) -> DiskEncryption | None:
|
||||
async def _select_disk_encryption(self, preset: DiskEncryption | None) -> DiskEncryption | None:
|
||||
disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value
|
||||
lvm_config: LvmConfiguration | None = self._item_group.find_by_key('lvm_config').value
|
||||
|
||||
|
|
@ -134,12 +134,12 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
if not DiskEncryption.validate_enc(modifications, lvm_config):
|
||||
return None
|
||||
|
||||
disk_encryption = DiskEncryptionMenu(modifications, lvm_config=lvm_config, preset=preset).run()
|
||||
disk_encryption = await DiskEncryptionMenu(modifications, lvm_config=lvm_config, preset=preset).show()
|
||||
|
||||
return disk_encryption
|
||||
|
||||
def _select_disk_layout_config(self, preset: DiskLayoutConfiguration | None) -> DiskLayoutConfiguration | None:
|
||||
disk_config = select_disk_config(preset)
|
||||
async def _select_disk_layout_config(self, preset: DiskLayoutConfiguration | None) -> DiskLayoutConfiguration | None:
|
||||
disk_config = await select_disk_config(preset)
|
||||
|
||||
if disk_config != preset:
|
||||
self._menu_item_group.find_by_key('lvm_config').value = None
|
||||
|
|
@ -147,20 +147,20 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
|
||||
return disk_config
|
||||
|
||||
def _select_lvm_config(self, preset: LvmConfiguration | None) -> LvmConfiguration | None:
|
||||
async def _select_lvm_config(self, preset: LvmConfiguration | None) -> LvmConfiguration | None:
|
||||
disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value
|
||||
|
||||
if not disk_config:
|
||||
return preset
|
||||
|
||||
lvm_config = select_lvm_config(disk_config, preset=preset)
|
||||
lvm_config = await select_lvm_config(disk_config, preset=preset)
|
||||
|
||||
if lvm_config != preset:
|
||||
self._menu_item_group.find_by_key('disk_encryption').value = None
|
||||
|
||||
return lvm_config
|
||||
|
||||
def _select_btrfs_snapshots(self, preset: SnapshotConfig | None) -> SnapshotConfig | None:
|
||||
async def _select_btrfs_snapshots(self, preset: SnapshotConfig | None) -> SnapshotConfig | None:
|
||||
preset_type = preset.snapshot_type if preset else None
|
||||
|
||||
group = MenuItemGroup.from_enum(
|
||||
|
|
@ -169,7 +169,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
preset=preset_type,
|
||||
)
|
||||
|
||||
result = Selection[SnapshotType](
|
||||
result = await Selection[SnapshotType](
|
||||
group,
|
||||
allow_reset=True,
|
||||
allow_skip=True,
|
||||
|
|
|
|||
|
|
@ -98,9 +98,9 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
|||
),
|
||||
]
|
||||
|
||||
def _select_lvm_vols(self, preset: list[LvmVolume]) -> list[LvmVolume]:
|
||||
async def _select_lvm_vols(self, preset: list[LvmVolume]) -> list[LvmVolume]:
|
||||
if self._lvm_config:
|
||||
return select_lvm_vols_to_encrypt(self._lvm_config, preset=preset)
|
||||
return await select_lvm_vols_to_encrypt(self._lvm_config, preset=preset)
|
||||
return []
|
||||
|
||||
def _check_dep_enc_type(self) -> bool:
|
||||
|
|
@ -122,8 +122,8 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
|||
return False
|
||||
|
||||
@override
|
||||
def run(self) -> DiskEncryption | None:
|
||||
enc_config = super().run()
|
||||
async def show(self) -> DiskEncryption | None:
|
||||
enc_config = await super().show()
|
||||
if enc_config is None:
|
||||
return None
|
||||
|
||||
|
|
@ -233,7 +233,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
|||
return None
|
||||
|
||||
|
||||
def select_encryption_type(
|
||||
async def select_encryption_type(
|
||||
lvm_config: LvmConfiguration | None = None,
|
||||
preset: EncryptionType | None = None,
|
||||
) -> EncryptionType | None:
|
||||
|
|
@ -253,7 +253,7 @@ def select_encryption_type(
|
|||
group = MenuItemGroup(items)
|
||||
group.set_focus_by_value(preset_value)
|
||||
|
||||
result = Selection[EncryptionType](
|
||||
result = await Selection[EncryptionType](
|
||||
group,
|
||||
header=tr('Select encryption type'),
|
||||
allow_skip=True,
|
||||
|
|
@ -269,9 +269,9 @@ def select_encryption_type(
|
|||
return result.get_value()
|
||||
|
||||
|
||||
def select_encrypted_password() -> Password | None:
|
||||
async def select_encrypted_password() -> Password | None:
|
||||
header = tr('Enter disk encryption password (leave blank for no encryption)') + '\n'
|
||||
password = get_password(
|
||||
password = await get_password(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
)
|
||||
|
|
@ -279,7 +279,7 @@ def select_encrypted_password() -> Password | None:
|
|||
return password
|
||||
|
||||
|
||||
def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None:
|
||||
async def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None:
|
||||
header = tr('Select a FIDO2 device to use for HSM') + '\n'
|
||||
|
||||
try:
|
||||
|
|
@ -290,7 +290,7 @@ def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None:
|
|||
if fido_devices:
|
||||
group = MenuHelper(data=fido_devices).create_menu_group()
|
||||
|
||||
result = Selection[Fido2Device](
|
||||
result = await Selection[Fido2Device](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
|
|
@ -307,7 +307,7 @@ def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None:
|
|||
return None
|
||||
|
||||
|
||||
def select_partitions_to_encrypt(
|
||||
async def select_partitions_to_encrypt(
|
||||
modification: list[DeviceModification],
|
||||
preset: list[PartitionModification],
|
||||
) -> list[PartitionModification]:
|
||||
|
|
@ -324,7 +324,7 @@ def select_partitions_to_encrypt(
|
|||
group = MenuItemGroup.from_objects(partitions)
|
||||
group.set_selected_by_value(preset)
|
||||
|
||||
result = Table[PartitionModification](
|
||||
result = await Table[PartitionModification](
|
||||
header=tr('Select disks for the installation'),
|
||||
group=group,
|
||||
allow_skip=True,
|
||||
|
|
@ -343,7 +343,7 @@ def select_partitions_to_encrypt(
|
|||
return []
|
||||
|
||||
|
||||
def select_lvm_vols_to_encrypt(
|
||||
async def select_lvm_vols_to_encrypt(
|
||||
lvm_config: LvmConfiguration,
|
||||
preset: list[LvmVolume],
|
||||
) -> list[LvmVolume]:
|
||||
|
|
@ -353,7 +353,7 @@ def select_lvm_vols_to_encrypt(
|
|||
group = MenuItemGroup.from_objects(volumes)
|
||||
group.set_selected_by_value(preset)
|
||||
|
||||
result = Table[LvmVolume](
|
||||
result = await Table[LvmVolume](
|
||||
header=tr('Select disks for the installation'),
|
||||
group=group,
|
||||
allow_skip=True,
|
||||
|
|
@ -372,7 +372,7 @@ def select_lvm_vols_to_encrypt(
|
|||
return []
|
||||
|
||||
|
||||
def select_iteration_time(preset: int | None = None) -> int | None:
|
||||
async def select_iteration_time(preset: int | None = None) -> int | None:
|
||||
header = tr('Enter iteration time for LUKS encryption (in milliseconds)') + '\n'
|
||||
header += tr('Higher values increase security but slow down boot time') + '\n'
|
||||
header += tr(f'Default: {DEFAULT_ITER_TIME}ms, Recommended range: 1000-60000') + '\n'
|
||||
|
|
@ -388,7 +388,7 @@ def select_iteration_time(preset: int | None = None) -> int | None:
|
|||
except ValueError:
|
||||
return tr('Please enter a valid number')
|
||||
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
default_value=str(preset) if preset else str(DEFAULT_ITER_TIME),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ from archinstall.lib.disk.lvm import (
|
|||
lvm_vol_reduce,
|
||||
)
|
||||
from archinstall.lib.disk.utils import udev_sync
|
||||
from archinstall.lib.interactions.general_conf import confirm_abort
|
||||
from archinstall.lib.luks import Luks2
|
||||
from archinstall.lib.models.device import (
|
||||
DiskEncryption,
|
||||
|
|
@ -29,7 +28,6 @@ from archinstall.lib.models.device import (
|
|||
Unit,
|
||||
)
|
||||
from archinstall.lib.output import debug, info
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
|
||||
class FilesystemHandler:
|
||||
|
|
@ -37,7 +35,7 @@ class FilesystemHandler:
|
|||
self._disk_config = disk_config
|
||||
self._enc_config = disk_config.disk_encryption
|
||||
|
||||
def perform_filesystem_operations(self, show_countdown: bool = True) -> None:
|
||||
def perform_filesystem_operations(self) -> None:
|
||||
if self._disk_config.config_type == DiskLayoutType.Pre_mount:
|
||||
debug('Disk layout configuration is set to pre-mount, not performing any operations')
|
||||
return
|
||||
|
|
@ -48,9 +46,6 @@ class FilesystemHandler:
|
|||
debug('No modifications required')
|
||||
return
|
||||
|
||||
if show_countdown:
|
||||
self._final_warning()
|
||||
|
||||
# Setup the blockdevice, filesystem (and optionally encryption).
|
||||
# Once that's done, we'll hand over to perform_installation()
|
||||
|
||||
|
|
@ -330,19 +325,3 @@ class FilesystemHandler:
|
|||
largest_vol.safe_dev_path,
|
||||
Size(256, Unit.MiB, SectorSize.default()),
|
||||
)
|
||||
|
||||
def _final_warning(self) -> bool:
|
||||
# Issue a final warning before we continue with something un-revertable.
|
||||
# We count down from 5 to 0.
|
||||
out = tr('Starting device modifications in ')
|
||||
print(out, end='', flush=True)
|
||||
|
||||
try:
|
||||
countdown = '\n5...4...3...2...1\n'
|
||||
for c in countdown:
|
||||
print(c, end='', flush=True)
|
||||
time.sleep(0.25)
|
||||
except KeyboardInterrupt:
|
||||
confirm_abort()
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -189,8 +189,8 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
def get_part_mods(disk_segments: list[DiskSegment]) -> list[PartitionModification]:
|
||||
return [s.segment for s in disk_segments if isinstance(s.segment, PartitionModification)]
|
||||
|
||||
def show(self) -> DeviceModification | None:
|
||||
disk_segments = super()._run()
|
||||
async def show(self) -> DeviceModification | None:
|
||||
disk_segments = await super()._run()
|
||||
|
||||
if not disk_segments:
|
||||
return None
|
||||
|
|
@ -199,12 +199,12 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
return DeviceModification(self._device, self._wipe, partitions)
|
||||
|
||||
@override
|
||||
def _run_actions_on_entry(self, entry: DiskSegment) -> None:
|
||||
async def _run_actions_on_entry(self, entry: DiskSegment) -> None:
|
||||
# Do not create a menu when the segment is free space
|
||||
if isinstance(entry.segment, FreeSpace):
|
||||
self._data = self.handle_action('', entry, self._data)
|
||||
self._data = await self.handle_action('', entry, self._data)
|
||||
else:
|
||||
super()._run_actions_on_entry(entry)
|
||||
await super()._run_actions_on_entry(entry)
|
||||
|
||||
@override
|
||||
def selected_action_display(self, selection: DiskSegment) -> str:
|
||||
|
|
@ -267,7 +267,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
return [o for o in options if o not in not_filter]
|
||||
|
||||
@override
|
||||
def handle_action(
|
||||
async def handle_action(
|
||||
self,
|
||||
action: str,
|
||||
entry: DiskSegment | None,
|
||||
|
|
@ -278,20 +278,20 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
match action_key:
|
||||
case 'suggest_partition_layout':
|
||||
part_mods = self.get_part_mods(data)
|
||||
device_mod = self._suggest_partition_layout(part_mods)
|
||||
device_mod = await self._suggest_partition_layout(part_mods)
|
||||
if device_mod and device_mod.partitions:
|
||||
data = self.as_segments(device_mod.partitions)
|
||||
self._wipe = device_mod.wipe
|
||||
self._prompt = self._info + self.wipe_str()
|
||||
case 'remove_added_partitions':
|
||||
if self._reset_confirmation():
|
||||
if await self._reset_confirmation():
|
||||
data = [s for s in data if isinstance(s.segment, PartitionModification) and s.segment.is_exists_or_modify()]
|
||||
elif isinstance(entry.segment, PartitionModification):
|
||||
partition = entry.segment
|
||||
action_key = [k for k, v in self._actions.items() if v == action][0]
|
||||
match action_key:
|
||||
case 'assign_mountpoint':
|
||||
new_mountpoint = self._prompt_mountpoint()
|
||||
new_mountpoint = await self._prompt_mountpoint()
|
||||
if not partition.is_swap():
|
||||
if partition.is_home():
|
||||
partition.invert_flag(PartitionFlag.LINUX_HOME)
|
||||
|
|
@ -307,7 +307,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
partition.flags = []
|
||||
partition.set_flag(PartitionFlag.LINUX_HOME)
|
||||
case 'mark_formatting':
|
||||
self._prompt_formatting(partition)
|
||||
await self._prompt_formatting(partition)
|
||||
case 'mark_bootable':
|
||||
if not partition.is_swap():
|
||||
partition.invert_flag(PartitionFlag.BOOT)
|
||||
|
|
@ -322,7 +322,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
partition.invert_flag(PartitionFlag.ESP)
|
||||
partition.invert_flag(PartitionFlag.XBOOTLDR)
|
||||
case 'set_filesystem':
|
||||
fs_type = self._prompt_partition_fs_type()
|
||||
fs_type = await self._prompt_partition_fs_type()
|
||||
|
||||
if partition.is_swap():
|
||||
partition.invert_flag(PartitionFlag.SWAP)
|
||||
|
|
@ -339,13 +339,14 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
case 'btrfs_mark_nodatacow':
|
||||
self._toggle_mount_option(partition, BtrfsMountOption.nodatacow)
|
||||
case 'btrfs_set_subvolumes':
|
||||
self._set_btrfs_subvolumes(partition)
|
||||
await self._set_btrfs_subvolumes(partition)
|
||||
case 'delete_partition':
|
||||
data = self._delete_partition(partition, data)
|
||||
else:
|
||||
part_mods = self.get_part_mods(data)
|
||||
index = data.index(entry)
|
||||
part_mods.insert(index, self._create_new_partition(entry.segment))
|
||||
part = await self._create_new_partition(entry.segment)
|
||||
part_mods.insert(index, part)
|
||||
data = self.as_segments(part_mods)
|
||||
|
||||
return data
|
||||
|
|
@ -378,8 +379,8 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
else:
|
||||
partition.mount_options = [o for o in partition.mount_options if o != option.value]
|
||||
|
||||
def _set_btrfs_subvolumes(self, partition: PartitionModification) -> None:
|
||||
subvols = SubvolumeMenu(
|
||||
async def _set_btrfs_subvolumes(self, partition: PartitionModification) -> None:
|
||||
subvols = await SubvolumeMenu(
|
||||
partition.btrfs_subvols,
|
||||
None,
|
||||
).show()
|
||||
|
|
@ -387,7 +388,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
if subvols is not None:
|
||||
partition.btrfs_subvols = subvols
|
||||
|
||||
def _prompt_formatting(self, partition: PartitionModification) -> None:
|
||||
async def _prompt_formatting(self, partition: PartitionModification) -> None:
|
||||
# an existing partition can toggle between Exist or Modify
|
||||
if partition.is_modify():
|
||||
partition.status = ModificationStatus.Exist
|
||||
|
|
@ -400,27 +401,27 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
# it's safe to change the filesystem for this partition.
|
||||
if partition.fs_type == FilesystemType.Crypto_luks:
|
||||
prompt = tr('This partition is currently encrypted, to format it a filesystem has to be specified') + '\n'
|
||||
fs_type = self._prompt_partition_fs_type(prompt)
|
||||
fs_type = await self._prompt_partition_fs_type(prompt)
|
||||
partition.fs_type = fs_type
|
||||
|
||||
if fs_type == FilesystemType.Btrfs:
|
||||
partition.mountpoint = None
|
||||
|
||||
def _prompt_mountpoint(self) -> Path:
|
||||
async def _prompt_mountpoint(self) -> Path:
|
||||
header = tr('Partition mount-points are relative to inside the installation, the boot would be /boot as an example.') + '\n\n'
|
||||
header += tr('Enter a mountpoint')
|
||||
|
||||
mountpoint = prompt_dir(header, validate=False, allow_skip=False)
|
||||
mountpoint = await prompt_dir(header, validate=False, allow_skip=False)
|
||||
assert mountpoint
|
||||
|
||||
return mountpoint
|
||||
|
||||
def _prompt_partition_fs_type(self, prompt: str | None = None) -> FilesystemType:
|
||||
async def _prompt_partition_fs_type(self, prompt: str | None = None) -> FilesystemType:
|
||||
fs_types = filter(lambda fs: fs != FilesystemType.Crypto_luks, FilesystemType)
|
||||
items = [MenuItem(fs.value, value=fs) for fs in fs_types]
|
||||
group = MenuItemGroup(items, sort_items=False)
|
||||
|
||||
result = Selection[FilesystemType](
|
||||
result = await Selection[FilesystemType](
|
||||
group,
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
|
|
@ -464,7 +465,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
|
||||
return size
|
||||
|
||||
def _prompt_size(self, free_space: FreeSpace) -> Size:
|
||||
async def _prompt_size(self, free_space: FreeSpace) -> Size:
|
||||
def validate(value: str | None) -> str | None:
|
||||
if not value:
|
||||
return None
|
||||
|
|
@ -491,7 +492,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
max_size = free_space.length
|
||||
prompt += tr('Enter a size (default: {}): ').format(max_size.format_highest())
|
||||
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=f'{prompt}\b',
|
||||
allow_skip=True,
|
||||
validator_callback=validate,
|
||||
|
|
@ -515,14 +516,14 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
assert size
|
||||
return size
|
||||
|
||||
def _create_new_partition(self, free_space: FreeSpace) -> PartitionModification:
|
||||
length = self._prompt_size(free_space)
|
||||
async def _create_new_partition(self, free_space: FreeSpace) -> PartitionModification:
|
||||
length = await self._prompt_size(free_space)
|
||||
|
||||
fs_type = self._prompt_partition_fs_type()
|
||||
fs_type = await self._prompt_partition_fs_type()
|
||||
|
||||
mountpoint = None
|
||||
if fs_type not in (FilesystemType.Btrfs, FilesystemType.LinuxSwap):
|
||||
mountpoint = self._prompt_mountpoint()
|
||||
mountpoint = await self._prompt_mountpoint()
|
||||
|
||||
partition = PartitionModification(
|
||||
status=ModificationStatus.Create,
|
||||
|
|
@ -544,10 +545,10 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
|
||||
return partition
|
||||
|
||||
def _reset_confirmation(self) -> bool:
|
||||
async def _reset_confirmation(self) -> bool:
|
||||
prompt = tr('This will remove all newly added partitions, continue?') + '\n'
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
allow_reset=False,
|
||||
|
|
@ -555,27 +556,27 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
|
||||
return result.item() == MenuItem.yes()
|
||||
|
||||
def _suggest_partition_layout(
|
||||
async def _suggest_partition_layout(
|
||||
self,
|
||||
data: list[PartitionModification],
|
||||
) -> DeviceModification | None:
|
||||
# if modifications have been done already, inform the user
|
||||
# that this operation will erase those modifications
|
||||
if any([not entry.exists() for entry in data]):
|
||||
if not self._reset_confirmation():
|
||||
if not await self._reset_confirmation():
|
||||
return None
|
||||
|
||||
from archinstall.lib.interactions.disk_conf import suggest_single_disk_layout
|
||||
|
||||
return suggest_single_disk_layout(self._device)
|
||||
return await suggest_single_disk_layout(self._device)
|
||||
|
||||
|
||||
def manual_partitioning(
|
||||
async def manual_partitioning(
|
||||
device_mod: DeviceModification,
|
||||
partition_table: PartitionTable,
|
||||
) -> DeviceModification | None:
|
||||
menu_list = PartitioningList(device_mod, partition_table)
|
||||
mod = menu_list.show()
|
||||
mod = await menu_list.show()
|
||||
|
||||
if not mod:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -28,20 +28,20 @@ class SubvolumeMenu(ListManager[SubvolumeModification]):
|
|||
prompt,
|
||||
)
|
||||
|
||||
def show(self) -> list[SubvolumeModification] | None:
|
||||
return super()._run()
|
||||
async def show(self) -> list[SubvolumeModification] | None:
|
||||
return await super()._run()
|
||||
|
||||
@override
|
||||
def selected_action_display(self, selection: SubvolumeModification) -> str:
|
||||
return str(selection.name)
|
||||
|
||||
def _add_subvolume(self, preset: SubvolumeModification | None = None) -> SubvolumeModification | None:
|
||||
async def _add_subvolume(self, preset: SubvolumeModification | None = None) -> SubvolumeModification | None:
|
||||
def validate(value: str | None) -> str | None:
|
||||
if value:
|
||||
return None
|
||||
return tr('Value cannot be empty')
|
||||
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=tr('Enter subvolume name'),
|
||||
allow_skip=True,
|
||||
default_value=str(preset.name) if preset else None,
|
||||
|
|
@ -61,7 +61,7 @@ class SubvolumeMenu(ListManager[SubvolumeModification]):
|
|||
header = f'{tr("Subvolume name")}: {name}\n\n'
|
||||
header += tr('Enter subvolume mountpoint')
|
||||
|
||||
path = prompt_dir(
|
||||
path = await prompt_dir(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
validate=True,
|
||||
|
|
@ -74,14 +74,14 @@ class SubvolumeMenu(ListManager[SubvolumeModification]):
|
|||
return SubvolumeModification(Path(name), path)
|
||||
|
||||
@override
|
||||
def handle_action(
|
||||
async def handle_action(
|
||||
self,
|
||||
action: str,
|
||||
entry: SubvolumeModification | None,
|
||||
data: list[SubvolumeModification],
|
||||
) -> list[SubvolumeModification]:
|
||||
if action == self._actions[0]:
|
||||
new_subvolume = self._add_subvolume()
|
||||
new_subvolume = await self._add_subvolume()
|
||||
|
||||
if new_subvolume is not None:
|
||||
# in case a user with the same username as an existing user
|
||||
|
|
@ -90,7 +90,7 @@ class SubvolumeMenu(ListManager[SubvolumeModification]):
|
|||
data += [new_subvolume]
|
||||
elif entry is not None:
|
||||
if action == self._actions[1]:
|
||||
new_subvolume = self._add_subvolume(entry)
|
||||
new_subvolume = await self._add_subvolume(entry)
|
||||
|
||||
if new_subvolume is not None:
|
||||
# we'll remove the original subvolume and add the modified version
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import sys
|
||||
from typing import override
|
||||
|
||||
from archinstall.lib.applications.application_menu import ApplicationMenu
|
||||
|
|
@ -11,7 +10,7 @@ from archinstall.lib.hardware import SysInfo
|
|||
from archinstall.lib.interactions.general_conf import add_number_of_parallel_downloads, select_hostname, select_ntp, select_timezone
|
||||
from archinstall.lib.interactions.system_conf import select_kernel, select_swap
|
||||
from archinstall.lib.locale.locale_menu import LocaleMenu
|
||||
from archinstall.lib.menu.abstract_menu import CONFIG_KEY, AbstractMenu
|
||||
from archinstall.lib.menu.abstract_menu import AbstractMenu, SpecialMenuKey
|
||||
from archinstall.lib.mirrors import MirrorListHandler, MirrorMenu
|
||||
from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration
|
||||
from archinstall.lib.models.authentication import AuthenticationConfiguration
|
||||
|
|
@ -170,30 +169,29 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
MenuItem(
|
||||
text=tr('Save configuration'),
|
||||
action=lambda x: self._safe_config(),
|
||||
key=f'{CONFIG_KEY}_save',
|
||||
key=SpecialMenuKey.SAVE.value,
|
||||
),
|
||||
MenuItem(
|
||||
text=tr('Install'),
|
||||
preview_action=self._prev_install_invalid_config,
|
||||
key=f'{CONFIG_KEY}_install',
|
||||
key=SpecialMenuKey.INSTALL.value,
|
||||
),
|
||||
MenuItem(
|
||||
text=tr('Abort'),
|
||||
action=lambda x: sys.exit(1),
|
||||
key=f'{CONFIG_KEY}_abort',
|
||||
key=SpecialMenuKey.ABORT.value,
|
||||
),
|
||||
]
|
||||
|
||||
return menu_options
|
||||
|
||||
def _safe_config(self) -> None:
|
||||
async def _safe_config(self) -> None:
|
||||
# data: dict[str, Any] = {}
|
||||
# for item in self._item_group.items:
|
||||
# if item.key is not None:
|
||||
# data[item.key] = item.value
|
||||
|
||||
self.sync_all_to_config()
|
||||
save_config(self._arch_config)
|
||||
await save_config(self._arch_config)
|
||||
|
||||
def _missing_configs(self) -> list[str]:
|
||||
item: MenuItem = self._item_group.find_by_key('auth_config')
|
||||
|
|
@ -224,7 +222,7 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
return list(missing)
|
||||
|
||||
@override
|
||||
def _is_config_valid(self) -> bool:
|
||||
def is_config_valid(self) -> bool:
|
||||
"""
|
||||
Checks the validity of the current configuration.
|
||||
"""
|
||||
|
|
@ -232,10 +230,10 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
return False
|
||||
return self._validate_bootloader() is None
|
||||
|
||||
def _select_archinstall_language(self, preset: Language) -> Language:
|
||||
async def _select_archinstall_language(self, preset: Language) -> Language:
|
||||
from archinstall.lib.interactions.general_conf import select_archinstall_language
|
||||
|
||||
language = select_archinstall_language(translation_handler.translated_languages, preset)
|
||||
language = await select_archinstall_language(translation_handler.translated_languages, preset)
|
||||
translation_handler.activate(language)
|
||||
|
||||
self._update_lang_text()
|
||||
|
|
@ -249,12 +247,12 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
lang: Language = item.value
|
||||
return f'{tr("Language")}: {lang.display_name}'
|
||||
|
||||
def _select_applications(self, preset: ApplicationConfiguration | None) -> ApplicationConfiguration | None:
|
||||
app_config = ApplicationMenu(preset).run()
|
||||
async def _select_applications(self, preset: ApplicationConfiguration | None) -> ApplicationConfiguration | None:
|
||||
app_config = await ApplicationMenu(preset).show()
|
||||
return app_config
|
||||
|
||||
def _select_authentication(self, preset: AuthenticationConfiguration | None) -> AuthenticationConfiguration | None:
|
||||
auth_config = AuthenticationMenu(preset).run()
|
||||
async def _select_authentication(self, preset: AuthenticationConfiguration | None) -> AuthenticationConfiguration | None:
|
||||
auth_config = await AuthenticationMenu(preset).show()
|
||||
return auth_config
|
||||
|
||||
def _update_lang_text(self) -> None:
|
||||
|
|
@ -268,8 +266,8 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
if o.key is not None:
|
||||
self._item_group.find_by_key(o.key).text = o.text
|
||||
|
||||
def _locale_selection(self, preset: LocaleConfiguration) -> LocaleConfiguration:
|
||||
locale_config = LocaleMenu(preset).run()
|
||||
async def _locale_selection(self, preset: LocaleConfiguration) -> LocaleConfiguration | None:
|
||||
locale_config = await LocaleMenu(preset).show()
|
||||
return locale_config
|
||||
|
||||
def _prev_locale(self, item: MenuItem) -> str | None:
|
||||
|
|
@ -427,9 +425,6 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
|
||||
Returns [`None`] if the bootloader is valid, otherwise returns a
|
||||
string with the error message.
|
||||
|
||||
XXX: The caller is responsible for wrapping the string with the translation
|
||||
shim if necessary.
|
||||
"""
|
||||
bootloader_config: BootloaderConfiguration | None = None
|
||||
root_partition: PartitionModification | None = None
|
||||
|
|
@ -512,50 +507,49 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
|
||||
return None
|
||||
|
||||
def _select_disk_config(
|
||||
async def _select_disk_config(
|
||||
self,
|
||||
preset: DiskLayoutConfiguration | None = None,
|
||||
) -> DiskLayoutConfiguration | None:
|
||||
disk_config = DiskLayoutConfigurationMenu(preset).run()
|
||||
|
||||
disk_config = await DiskLayoutConfigurationMenu(preset).show()
|
||||
return disk_config
|
||||
|
||||
def _select_bootloader_config(
|
||||
async def _select_bootloader_config(
|
||||
self,
|
||||
preset: BootloaderConfiguration | None = None,
|
||||
) -> BootloaderConfiguration | None:
|
||||
if preset is None:
|
||||
preset = BootloaderConfiguration.get_default(self._uefi, self._skip_boot)
|
||||
|
||||
bootloader_config = BootloaderMenu(preset, self._uefi, self._skip_boot).run()
|
||||
bootloader_config = await BootloaderMenu(preset, self._uefi, self._skip_boot).show()
|
||||
|
||||
return bootloader_config
|
||||
|
||||
def _select_profile(self, current_profile: ProfileConfiguration | None) -> ProfileConfiguration | None:
|
||||
async def _select_profile(self, current_profile: ProfileConfiguration | None) -> ProfileConfiguration | None:
|
||||
from archinstall.lib.profile.profile_menu import ProfileMenu
|
||||
|
||||
profile_config = ProfileMenu(preset=current_profile).run()
|
||||
profile_config = await ProfileMenu(preset=current_profile).show()
|
||||
return profile_config
|
||||
|
||||
def _select_additional_packages(self, preset: list[str]) -> list[str]:
|
||||
async def _select_additional_packages(self, preset: list[str]) -> list[str]:
|
||||
config: MirrorConfiguration | None = self._item_group.find_by_key('mirror_config').value
|
||||
|
||||
repositories: set[Repository] = set()
|
||||
if config:
|
||||
repositories = set(config.optional_repositories)
|
||||
|
||||
packages = select_additional_packages(
|
||||
packages = await select_additional_packages(
|
||||
preset,
|
||||
repositories=repositories,
|
||||
)
|
||||
|
||||
return packages
|
||||
|
||||
def _mirror_configuration(self, preset: MirrorConfiguration | None = None) -> MirrorConfiguration | None:
|
||||
async def _mirror_configuration(self, preset: MirrorConfiguration | None = None) -> MirrorConfiguration | None:
|
||||
if self._mirror_list_handler is None:
|
||||
self._mirror_list_handler = MirrorListHandler()
|
||||
|
||||
mirror_configuration = MirrorMenu(self._mirror_list_handler, preset=preset).run()
|
||||
mirror_configuration = await MirrorMenu(self._mirror_list_handler, preset=preset).run()
|
||||
|
||||
if mirror_configuration and mirror_configuration.optional_repositories:
|
||||
# reset the package list cache in case the repository selection has changed
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
|
|||
from archinstall.tui.ui.result import ResultType
|
||||
|
||||
|
||||
def select_devices(preset: list[BDevice] | None = []) -> list[BDevice] | None:
|
||||
async def select_devices(preset: list[BDevice] | None = []) -> list[BDevice] | None:
|
||||
def _preview_device_selection(item: MenuItem) -> str | None:
|
||||
device: _DeviceInfo = item.value # type: ignore[assignment]
|
||||
dev = device_handler.get_device(device.path)
|
||||
|
|
@ -60,7 +60,7 @@ def select_devices(preset: list[BDevice] | None = []) -> list[BDevice] | None:
|
|||
group = MenuItemGroup(items)
|
||||
group.set_selected_by_value(presets)
|
||||
|
||||
result = Table[_DeviceInfo](
|
||||
result = await Table[_DeviceInfo](
|
||||
header=tr('Select disks for the installation'),
|
||||
group=group,
|
||||
presets=presets,
|
||||
|
|
@ -86,24 +86,24 @@ def select_devices(preset: list[BDevice] | None = []) -> list[BDevice] | None:
|
|||
return selected_devices
|
||||
|
||||
|
||||
def get_default_partition_layout(
|
||||
async def get_default_partition_layout(
|
||||
devices: list[BDevice],
|
||||
filesystem_type: FilesystemType | None = None,
|
||||
) -> list[DeviceModification]:
|
||||
if len(devices) == 1:
|
||||
device_modification = suggest_single_disk_layout(
|
||||
device_modification = await suggest_single_disk_layout(
|
||||
devices[0],
|
||||
filesystem_type=filesystem_type,
|
||||
)
|
||||
return [device_modification]
|
||||
else:
|
||||
return suggest_multi_disk_layout(
|
||||
return await suggest_multi_disk_layout(
|
||||
devices,
|
||||
filesystem_type=filesystem_type,
|
||||
)
|
||||
|
||||
|
||||
def _manual_partitioning(
|
||||
async def _manual_partitioning(
|
||||
preset: list[DeviceModification],
|
||||
devices: list[BDevice],
|
||||
) -> list[DeviceModification] | None:
|
||||
|
|
@ -114,7 +114,7 @@ def _manual_partitioning(
|
|||
if not mod:
|
||||
mod = DeviceModification(device, wipe=False)
|
||||
|
||||
device_mod = manual_partitioning(mod, device_handler.partition_table)
|
||||
device_mod = await manual_partitioning(mod, device_handler.partition_table)
|
||||
|
||||
if not device_mod:
|
||||
return None
|
||||
|
|
@ -124,7 +124,7 @@ def _manual_partitioning(
|
|||
return modifications
|
||||
|
||||
|
||||
def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLayoutConfiguration | None:
|
||||
async def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLayoutConfiguration | None:
|
||||
default_layout = DiskLayoutType.Default.display_msg()
|
||||
manual_mode = DiskLayoutType.Manual.display_msg()
|
||||
pre_mount_mode = DiskLayoutType.Pre_mount.display_msg()
|
||||
|
|
@ -139,7 +139,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay
|
|||
if preset:
|
||||
group.set_selected_by_value(preset.config_type.display_msg())
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=tr('Select a disk configuration'),
|
||||
allow_skip=True,
|
||||
|
|
@ -159,7 +159,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay
|
|||
output += tr('You will use whatever drive-setup is mounted at the specified directory') + '\n'
|
||||
output += tr("WARNING: Archinstall won't check the suitability of this setup")
|
||||
|
||||
path = prompt_dir(output, allow_skip=True)
|
||||
path = await prompt_dir(output, allow_skip=True)
|
||||
|
||||
if path is None:
|
||||
return None
|
||||
|
|
@ -173,13 +173,13 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay
|
|||
)
|
||||
|
||||
preset_devices = [mod.device for mod in preset.device_modifications] if preset else []
|
||||
devices = select_devices(preset_devices)
|
||||
devices = await select_devices(preset_devices)
|
||||
|
||||
if devices is None:
|
||||
return preset
|
||||
|
||||
if result.get_value() == default_layout:
|
||||
modifications = get_default_partition_layout(devices)
|
||||
modifications = await get_default_partition_layout(devices)
|
||||
if modifications:
|
||||
return DiskLayoutConfiguration(
|
||||
config_type=DiskLayoutType.Default,
|
||||
|
|
@ -187,7 +187,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay
|
|||
)
|
||||
elif result.get_value() == manual_mode:
|
||||
preset_mods = preset.device_modifications if preset else []
|
||||
partitions = _manual_partitioning(preset_mods, devices)
|
||||
partitions = await _manual_partitioning(preset_mods, devices)
|
||||
|
||||
if not partitions:
|
||||
return preset
|
||||
|
|
@ -200,7 +200,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay
|
|||
return None
|
||||
|
||||
|
||||
def select_lvm_config(
|
||||
async def select_lvm_config(
|
||||
disk_config: DiskLayoutConfiguration,
|
||||
preset: LvmConfiguration | None = None,
|
||||
) -> LvmConfiguration | None:
|
||||
|
|
@ -211,7 +211,7 @@ def select_lvm_config(
|
|||
group = MenuItemGroup(items)
|
||||
group.set_focus_by_value(preset_value)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
allow_reset=True,
|
||||
allow_skip=True,
|
||||
|
|
@ -224,7 +224,7 @@ def select_lvm_config(
|
|||
return None
|
||||
case ResultType.Selection:
|
||||
if result.get_value() == default_mode:
|
||||
return suggest_lvm_layout(disk_config)
|
||||
return await suggest_lvm_layout(disk_config)
|
||||
|
||||
return None
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ def _boot_partition(sector_size: SectorSize, using_gpt: bool) -> PartitionModifi
|
|||
)
|
||||
|
||||
|
||||
def select_main_filesystem_format() -> FilesystemType:
|
||||
async def select_main_filesystem_format() -> FilesystemType:
|
||||
items = [
|
||||
MenuItem('btrfs', value=FilesystemType.Btrfs),
|
||||
MenuItem('ext4', value=FilesystemType.Ext4),
|
||||
|
|
@ -257,7 +257,7 @@ def select_main_filesystem_format() -> FilesystemType:
|
|||
]
|
||||
|
||||
group = MenuItemGroup(items, sort_items=False)
|
||||
result = Selection[FilesystemType](
|
||||
result = await Selection[FilesystemType](
|
||||
group,
|
||||
header=tr('Select main filesystem'),
|
||||
allow_skip=False,
|
||||
|
|
@ -270,7 +270,7 @@ def select_main_filesystem_format() -> FilesystemType:
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_mount_options() -> list[str]:
|
||||
async def select_mount_options() -> list[str]:
|
||||
prompt = tr('Would you like to use compression or disable CoW?') + '\n'
|
||||
compression = tr('Use compression')
|
||||
disable_cow = tr('Disable Copy-on-Write')
|
||||
|
|
@ -281,7 +281,7 @@ def select_mount_options() -> list[str]:
|
|||
]
|
||||
group = MenuItemGroup(items, sort_items=False)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=prompt,
|
||||
allow_skip=True,
|
||||
|
|
@ -323,13 +323,13 @@ def get_default_btrfs_subvols() -> list[SubvolumeModification]:
|
|||
]
|
||||
|
||||
|
||||
def suggest_single_disk_layout(
|
||||
async def suggest_single_disk_layout(
|
||||
device: BDevice,
|
||||
filesystem_type: FilesystemType | None = None,
|
||||
separate_home: bool | None = None,
|
||||
) -> DeviceModification:
|
||||
if not filesystem_type:
|
||||
filesystem_type = select_main_filesystem_format()
|
||||
filesystem_type = await select_main_filesystem_format()
|
||||
|
||||
sector_size = device.device_info.sector_size
|
||||
total_size = device.device_info.total_size
|
||||
|
|
@ -339,14 +339,14 @@ def suggest_single_disk_layout(
|
|||
if filesystem_type == FilesystemType.Btrfs:
|
||||
prompt = tr('Would you like to use BTRFS subvolumes with a default structure?') + '\n'
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
preset=True,
|
||||
).show()
|
||||
|
||||
using_subvolumes = result.item() == MenuItem.yes()
|
||||
mount_options = select_mount_options()
|
||||
mount_options = await select_mount_options()
|
||||
else:
|
||||
using_subvolumes = False
|
||||
mount_options = []
|
||||
|
|
@ -372,7 +372,7 @@ def suggest_single_disk_layout(
|
|||
else:
|
||||
prompt = tr('Would you like to create a separate partition for /home?') + '\n'
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
preset=True,
|
||||
|
|
@ -429,7 +429,7 @@ def suggest_single_disk_layout(
|
|||
return device_modification
|
||||
|
||||
|
||||
def suggest_multi_disk_layout(
|
||||
async def suggest_multi_disk_layout(
|
||||
devices: list[BDevice],
|
||||
filesystem_type: FilesystemType | None = None,
|
||||
) -> list[DeviceModification]:
|
||||
|
|
@ -445,7 +445,7 @@ def suggest_multi_disk_layout(
|
|||
mount_options = []
|
||||
|
||||
if not filesystem_type:
|
||||
filesystem_type = select_main_filesystem_format()
|
||||
filesystem_type = await select_main_filesystem_format()
|
||||
|
||||
# find proper disk for /home
|
||||
possible_devices = [d for d in devices if d.device_info.total_size >= min_home_partition_size]
|
||||
|
|
@ -466,11 +466,11 @@ def suggest_multi_disk_layout(
|
|||
text += tr('Minimum capacity for /home partition: {}GiB\n').format(min_home_partition_size.format_size(Unit.GiB))
|
||||
text += tr('Minimum capacity for Arch Linux partition: {}GiB').format(desired_root_partition_size.format_size(Unit.GiB))
|
||||
|
||||
Notify(text).show()
|
||||
_ = await Notify(text).show()
|
||||
return []
|
||||
|
||||
if filesystem_type == FilesystemType.Btrfs:
|
||||
mount_options = select_mount_options()
|
||||
mount_options = await select_mount_options()
|
||||
|
||||
device_paths = ', '.join(str(d.device_info.path) for d in devices)
|
||||
|
||||
|
|
@ -536,7 +536,7 @@ def suggest_multi_disk_layout(
|
|||
return [root_device_modification, home_device_modification]
|
||||
|
||||
|
||||
def suggest_lvm_layout(
|
||||
async def suggest_lvm_layout(
|
||||
disk_config: DiskLayoutConfiguration,
|
||||
filesystem_type: FilesystemType | None = None,
|
||||
vg_grp_name: str = 'ArchinstallVg',
|
||||
|
|
@ -550,14 +550,14 @@ def suggest_lvm_layout(
|
|||
mount_options = []
|
||||
|
||||
if not filesystem_type:
|
||||
filesystem_type = select_main_filesystem_format()
|
||||
filesystem_type = await select_main_filesystem_format()
|
||||
|
||||
if filesystem_type == FilesystemType.Btrfs:
|
||||
prompt = tr('Would you like to use BTRFS subvolumes with a default structure?') + '\n'
|
||||
result = Confirmation(header=prompt, allow_skip=False, preset=True).show()
|
||||
result = await Confirmation(header=prompt, allow_skip=False, preset=True).show()
|
||||
|
||||
using_subvolumes = MenuItem.yes() == result.item()
|
||||
mount_options = select_mount_options()
|
||||
mount_options = await select_mount_options()
|
||||
|
||||
if using_subvolumes:
|
||||
btrfs_subvols = get_default_btrfs_subvols()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import sys
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -16,7 +15,7 @@ class PostInstallationAction(Enum):
|
|||
CHROOT = tr('chroot into installation for post-installation configurations')
|
||||
|
||||
|
||||
def select_ntp(preset: bool = True) -> bool:
|
||||
async def select_ntp(preset: bool = True) -> bool:
|
||||
header = tr('Would you like to use automatic time synchronization (NTP) with the default time servers?\n') + '\n'
|
||||
header += (
|
||||
tr(
|
||||
|
|
@ -25,7 +24,7 @@ def select_ntp(preset: bool = True) -> bool:
|
|||
+ '\n'
|
||||
)
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
preset=preset,
|
||||
|
|
@ -40,8 +39,8 @@ def select_ntp(preset: bool = True) -> bool:
|
|||
raise ValueError('Unhandled return type')
|
||||
|
||||
|
||||
def select_hostname(preset: str | None = None) -> str | None:
|
||||
result = Input(
|
||||
async def select_hostname(preset: str | None = None) -> str | None:
|
||||
result = await Input(
|
||||
header=tr('Enter a hostname'),
|
||||
allow_skip=True,
|
||||
default_value=preset,
|
||||
|
|
@ -59,7 +58,7 @@ def select_hostname(preset: str | None = None) -> str | None:
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_timezone(preset: str | None = None) -> str | None:
|
||||
async def select_timezone(preset: str | None = None) -> str | None:
|
||||
default = 'UTC'
|
||||
timezones = list_timezones()
|
||||
|
||||
|
|
@ -68,7 +67,7 @@ def select_timezone(preset: str | None = None) -> str | None:
|
|||
group.set_selected_by_value(preset)
|
||||
group.set_default_by_value(default)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=tr('Select timezone'),
|
||||
allow_reset=True,
|
||||
|
|
@ -85,7 +84,7 @@ def select_timezone(preset: str | None = None) -> str | None:
|
|||
return result.get_value()
|
||||
|
||||
|
||||
def select_language(preset: str | None = None) -> str | None:
|
||||
async def select_language(preset: str | None = None) -> str | None:
|
||||
from archinstall.lib.locale.locale_menu import select_kb_layout
|
||||
|
||||
# We'll raise an exception in an upcoming version.
|
||||
|
|
@ -95,10 +94,10 @@ def select_language(preset: str | None = None) -> str | None:
|
|||
# No need to translate this i feel, as it's a short lived message.
|
||||
warn('select_language() is deprecated, use select_kb_layout() instead. select_language() will be removed in a future version')
|
||||
|
||||
return select_kb_layout(preset)
|
||||
return await select_kb_layout(preset)
|
||||
|
||||
|
||||
def select_archinstall_language(languages: list[Language], preset: Language) -> Language:
|
||||
async def select_archinstall_language(languages: list[Language], preset: Language) -> Language:
|
||||
# these are the displayed language names which can either be
|
||||
# the english name of a language or, if present, the
|
||||
# name of the language in its own language
|
||||
|
|
@ -111,7 +110,7 @@ def select_archinstall_language(languages: list[Language], preset: Language) ->
|
|||
title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n'
|
||||
title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n'
|
||||
|
||||
result = Selection[Language](
|
||||
result = await Selection[Language](
|
||||
header=title,
|
||||
group=group,
|
||||
allow_reset=False,
|
||||
|
|
@ -127,7 +126,7 @@ def select_archinstall_language(languages: list[Language], preset: Language) ->
|
|||
raise ValueError('Language selection not handled')
|
||||
|
||||
|
||||
def add_number_of_parallel_downloads(preset: int = 1) -> int | None:
|
||||
async def add_number_of_parallel_downloads(preset: int = 1) -> int | None:
|
||||
max_recommended = 5
|
||||
|
||||
header = tr('This option enables the number of parallel downloads that can occur during package downloads') + '\n'
|
||||
|
|
@ -145,7 +144,7 @@ def add_number_of_parallel_downloads(preset: int = 1) -> int | None:
|
|||
except Exception:
|
||||
return tr('Please enter a valid number')
|
||||
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
allow_reset=True,
|
||||
|
|
@ -164,10 +163,10 @@ def add_number_of_parallel_downloads(preset: int = 1) -> int | None:
|
|||
downloads = int(result.get_value())
|
||||
|
||||
pacman_conf_path = Path('/etc/pacman.conf')
|
||||
with pacman_conf_path.open() as f:
|
||||
with pacman_conf_path.open() as f: # noqa: ASYNC230
|
||||
pacman_conf = f.read().split('\n')
|
||||
|
||||
with pacman_conf_path.open('w') as fwrite:
|
||||
with pacman_conf_path.open('w') as fwrite: # noqa: ASYNC230
|
||||
for line in pacman_conf:
|
||||
if 'ParallelDownloads' in line:
|
||||
fwrite.write(f'ParallelDownloads = {downloads}\n')
|
||||
|
|
@ -177,7 +176,7 @@ def add_number_of_parallel_downloads(preset: int = 1) -> int | None:
|
|||
return downloads
|
||||
|
||||
|
||||
def select_post_installation(elapsed_time: float | None = None) -> PostInstallationAction:
|
||||
async def select_post_installation(elapsed_time: float | None = None) -> PostInstallationAction:
|
||||
header = 'Installation completed'
|
||||
if elapsed_time is not None:
|
||||
minutes = int(elapsed_time // 60)
|
||||
|
|
@ -188,7 +187,7 @@ def select_post_installation(elapsed_time: float | None = None) -> PostInstallat
|
|||
items = [MenuItem(action.value, value=action) for action in PostInstallationAction]
|
||||
group = MenuItemGroup(items)
|
||||
|
||||
result = Selection[PostInstallationAction](
|
||||
result = await Selection[PostInstallationAction](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -199,16 +198,3 @@ def select_post_installation(elapsed_time: float | None = None) -> PostInstallat
|
|||
return result.get_value()
|
||||
case _:
|
||||
raise ValueError('Post installation action not handled')
|
||||
|
||||
|
||||
def confirm_abort() -> None:
|
||||
prompt = tr('Do you really want to abort?') + '\n'
|
||||
|
||||
result = Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
preset=False,
|
||||
).show()
|
||||
|
||||
if result.get_value():
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
|
|||
from archinstall.tui.ui.result import ResultType
|
||||
|
||||
|
||||
def select_kernel(preset: list[str] = []) -> list[str]:
|
||||
async def select_kernel(preset: list[str] = []) -> list[str]:
|
||||
"""
|
||||
Asks the user to select a kernel for system.
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ def select_kernel(preset: list[str] = []) -> list[str]:
|
|||
group.set_focus_by_value(default_kernel)
|
||||
group.set_selected_by_value(preset)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=tr('Select which kernel(s) to install'),
|
||||
allow_skip=True,
|
||||
|
|
@ -42,10 +42,10 @@ def select_kernel(preset: list[str] = []) -> list[str]:
|
|||
return result.get_values()
|
||||
|
||||
|
||||
def select_uki(preset: bool = True) -> bool:
|
||||
async def select_uki(preset: bool = True) -> bool:
|
||||
prompt = tr('Would you like to use unified kernel images?') + '\n'
|
||||
|
||||
result = Confirmation(header=prompt, allow_skip=True, preset=preset).show()
|
||||
result = await Confirmation(header=prompt, allow_skip=True, preset=preset).show()
|
||||
|
||||
match result.type_:
|
||||
case ResultType.Skip:
|
||||
|
|
@ -56,7 +56,7 @@ def select_uki(preset: bool = True) -> bool:
|
|||
raise ValueError('Unhandled result type')
|
||||
|
||||
|
||||
def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None) -> GfxDriver | None:
|
||||
async def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None) -> GfxDriver | None:
|
||||
"""
|
||||
Somewhat convoluted function, whose job is simple.
|
||||
Select a graphics driver from a pre-defined set of popular options.
|
||||
|
|
@ -90,7 +90,7 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None
|
|||
if SysInfo.has_nvidia_graphics():
|
||||
header += tr('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n')
|
||||
|
||||
result = Selection[GfxDriver](
|
||||
result = await Selection[GfxDriver](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=True,
|
||||
|
|
@ -107,14 +107,14 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None
|
|||
return result.get_value()
|
||||
|
||||
|
||||
def select_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> ZramConfiguration:
|
||||
async def select_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) -> ZramConfiguration:
|
||||
prompt = tr('Would you like to use swap on zram?') + '\n'
|
||||
|
||||
group = MenuItemGroup.yes_no()
|
||||
group.set_default_by_value(True)
|
||||
group.set_focus_by_value(preset.enabled)
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=True,
|
||||
preset=preset.enabled,
|
||||
|
|
@ -133,7 +133,7 @@ def select_swap(preset: ZramConfiguration = ZramConfiguration(enabled=True)) ->
|
|||
algo_group.set_default_by_value(ZramAlgorithm.ZSTD)
|
||||
algo_group.set_focus_by_value(preset.algorithm)
|
||||
|
||||
algo_result = Selection[ZramAlgorithm](
|
||||
algo_result = await Selection[ZramAlgorithm](
|
||||
algo_group,
|
||||
header=tr('Select zram compression algorithm:') + '\n',
|
||||
allow_skip=True,
|
||||
|
|
|
|||
|
|
@ -50,22 +50,22 @@ class LocaleMenu(AbstractSubMenu[LocaleConfiguration]):
|
|||
]
|
||||
|
||||
@override
|
||||
def run(self) -> LocaleConfiguration:
|
||||
config = super().run()
|
||||
async def show(self) -> LocaleConfiguration | None:
|
||||
config = await super().show()
|
||||
|
||||
if config is None:
|
||||
config = LocaleConfiguration.default()
|
||||
|
||||
return config
|
||||
|
||||
def _select_kb_layout(self, preset: str | None) -> str | None:
|
||||
kb_lang = select_kb_layout(preset)
|
||||
async def _select_kb_layout(self, preset: str | None) -> str | None:
|
||||
kb_lang = await select_kb_layout(preset)
|
||||
if kb_lang:
|
||||
set_kb_layout(kb_lang)
|
||||
return kb_lang
|
||||
|
||||
|
||||
def select_locale_lang(preset: str | None = None) -> str | None:
|
||||
async def select_locale_lang(preset: str | None = None) -> str | None:
|
||||
locales = list_locales()
|
||||
locale_lang = set([locale.split()[0] for locale in locales])
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ def select_locale_lang(preset: str | None = None) -> str | None:
|
|||
group = MenuItemGroup(items, sort_items=True)
|
||||
group.set_focus_by_value(preset)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
header=tr('Locale language'),
|
||||
group=group,
|
||||
enable_filter=True,
|
||||
|
|
@ -88,7 +88,7 @@ def select_locale_lang(preset: str | None = None) -> str | None:
|
|||
raise ValueError('Unhandled return type')
|
||||
|
||||
|
||||
def select_locale_enc(preset: str | None = None) -> str | None:
|
||||
async def select_locale_enc(preset: str | None = None) -> str | None:
|
||||
locales = list_locales()
|
||||
locale_enc = set([locale.split()[1] for locale in locales])
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ def select_locale_enc(preset: str | None = None) -> str | None:
|
|||
group = MenuItemGroup(items, sort_items=True)
|
||||
group.set_focus_by_value(preset)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
header=tr('Locale encoding'),
|
||||
group=group,
|
||||
enable_filter=True,
|
||||
|
|
@ -111,7 +111,7 @@ def select_locale_enc(preset: str | None = None) -> str | None:
|
|||
raise ValueError('Unhandled return type')
|
||||
|
||||
|
||||
def select_kb_layout(preset: str | None = None) -> str | None:
|
||||
async def select_kb_layout(preset: str | None = None) -> str | None:
|
||||
"""
|
||||
Select keyboard layout
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ def select_kb_layout(preset: str | None = None) -> str | None:
|
|||
group = MenuItemGroup(items, sort_items=False)
|
||||
group.set_focus_by_value(preset)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
header=tr('Keyboard layout'),
|
||||
group=group,
|
||||
enable_filter=True,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,29 @@
|
|||
from enum import Enum
|
||||
from types import TracebackType
|
||||
from typing import Any, Self
|
||||
from typing import Any, Self, override
|
||||
|
||||
from archinstall.lib.menu.helpers import Selection
|
||||
from archinstall.lib.output import error
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.types import Chars
|
||||
from archinstall.tui.ui.components import InstanceRunnable
|
||||
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.ui.result import ResultType
|
||||
|
||||
CONFIG_KEY = '__config__'
|
||||
|
||||
|
||||
class AbstractMenu[ValueT]:
|
||||
class SpecialMenuKey(Enum):
|
||||
SAVE = f'{CONFIG_KEY}_save'
|
||||
INSTALL = f'{CONFIG_KEY}_install'
|
||||
ABORT = f'{CONFIG_KEY}_abort'
|
||||
|
||||
@staticmethod
|
||||
def matches(key: str) -> bool:
|
||||
return any(key == item.value for item in SpecialMenuKey)
|
||||
|
||||
|
||||
class AbstractMenu[ValueT](InstanceRunnable[ValueT]):
|
||||
def __init__(
|
||||
self,
|
||||
item_group: MenuItemGroup,
|
||||
|
|
@ -50,7 +62,7 @@ class AbstractMenu[ValueT]:
|
|||
|
||||
def _sync_from_config(self) -> None:
|
||||
for item in self._menu_item_group._menu_items:
|
||||
if item.key is not None and not item.key.startswith(CONFIG_KEY):
|
||||
if item.key is not None and not SpecialMenuKey.matches(item.key):
|
||||
config_value = getattr(self._config, item.key)
|
||||
if config_value is not None:
|
||||
item.value = config_value
|
||||
|
|
@ -61,7 +73,7 @@ class AbstractMenu[ValueT]:
|
|||
setattr(self._config, item.key, item.value)
|
||||
|
||||
def _sync(self, item: MenuItem) -> None:
|
||||
if not item.key or item.key.startswith(CONFIG_KEY):
|
||||
if not item.key or SpecialMenuKey.matches(item.key):
|
||||
return
|
||||
|
||||
config_value = getattr(self._config, item.key)
|
||||
|
|
@ -79,7 +91,7 @@ class AbstractMenu[ValueT]:
|
|||
|
||||
for item in self._menu_item_group.items:
|
||||
if item.key:
|
||||
if item.key == key or (is_config_key and item.key.startswith(CONFIG_KEY)):
|
||||
if item.key == key or (is_config_key and SpecialMenuKey.matches(item.key)):
|
||||
item.enabled = enabled
|
||||
found = True
|
||||
|
||||
|
|
@ -90,14 +102,18 @@ class AbstractMenu[ValueT]:
|
|||
for item in self._menu_item_group.items:
|
||||
item.enabled = False
|
||||
|
||||
def _is_config_valid(self) -> bool:
|
||||
def is_config_valid(self) -> bool:
|
||||
return True
|
||||
|
||||
def run(self) -> ValueT | None:
|
||||
@override
|
||||
async def run(self) -> ValueT | None:
|
||||
return await self.show()
|
||||
|
||||
async def show(self) -> ValueT | None:
|
||||
self._sync_from_config()
|
||||
|
||||
while True:
|
||||
result = Selection[ValueT](
|
||||
result = await Selection[ValueT](
|
||||
title=self._title,
|
||||
group=self._menu_item_group,
|
||||
allow_skip=False,
|
||||
|
|
@ -111,11 +127,16 @@ class AbstractMenu[ValueT]:
|
|||
self._menu_item_group.focus_item = item
|
||||
|
||||
if item.action is None:
|
||||
if not self._is_config_valid():
|
||||
continue
|
||||
break
|
||||
if item.key == SpecialMenuKey.INSTALL.value:
|
||||
if not self.is_config_valid():
|
||||
continue
|
||||
break
|
||||
elif item.key == SpecialMenuKey.ABORT.value:
|
||||
return None
|
||||
else:
|
||||
break
|
||||
else:
|
||||
item.value = item.action(item.value)
|
||||
item.value = await item.action(item.value)
|
||||
case ResultType.Reset:
|
||||
return None
|
||||
case _:
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any, Literal, TypeVar, override
|
||||
from typing import Any, Literal, override
|
||||
|
||||
from textual.validation import ValidationResult, Validator
|
||||
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import InputScreen, LoadingScreen, NotifyScreen, OptionListScreen, SelectListScreen, TableSelectionScreen, tui
|
||||
from archinstall.tui.ui.components import InputScreen, LoadingScreen, NotifyScreen, OptionListScreen, SelectListScreen, TableSelectionScreen
|
||||
from archinstall.tui.ui.menu_item import MenuItemGroup
|
||||
from archinstall.tui.ui.result import Result, ResultType
|
||||
|
||||
ValueT = TypeVar('ValueT')
|
||||
|
||||
|
||||
class Selection[ValueT]:
|
||||
def __init__(
|
||||
|
|
@ -32,11 +30,7 @@ class Selection[ValueT]:
|
|||
self._multi = multi
|
||||
self._enable_filter = enable_filter
|
||||
|
||||
def show(self) -> Result[ValueT]:
|
||||
result: Result[ValueT] = tui.run(self)
|
||||
return result
|
||||
|
||||
async def _run(self) -> None:
|
||||
async def show(self) -> Result[ValueT]:
|
||||
if self._multi:
|
||||
result = await SelectListScreen[ValueT](
|
||||
self._group,
|
||||
|
|
@ -61,9 +55,9 @@ class Selection[ValueT]:
|
|||
confirmed = await _confirm_reset()
|
||||
|
||||
if confirmed.get_value() is False:
|
||||
return await self._run()
|
||||
return await self.show()
|
||||
|
||||
tui.exit(result)
|
||||
return result
|
||||
|
||||
|
||||
class Confirmation:
|
||||
|
|
@ -90,11 +84,7 @@ class Confirmation:
|
|||
else:
|
||||
self._group = group
|
||||
|
||||
def show(self) -> Result[bool]:
|
||||
result: Result[bool] = tui.run(self)
|
||||
return result
|
||||
|
||||
async def _run(self) -> None:
|
||||
async def show(self) -> Result[bool]:
|
||||
result = await OptionListScreen[bool](
|
||||
self._group,
|
||||
header=self._header,
|
||||
|
|
@ -108,22 +98,18 @@ class Confirmation:
|
|||
confirmed = await _confirm_reset()
|
||||
|
||||
if confirmed.get_value() is False:
|
||||
return await self._run()
|
||||
return await self.show()
|
||||
|
||||
tui.exit(result)
|
||||
return result
|
||||
|
||||
|
||||
class Notify:
|
||||
def __init__(self, header: str):
|
||||
self._header = header
|
||||
|
||||
def show(self) -> Result[bool]:
|
||||
result: Result[bool] = tui.run(self)
|
||||
return result
|
||||
|
||||
async def _run(self) -> None:
|
||||
await NotifyScreen(header=self._header).run()
|
||||
tui.exit(Result.true())
|
||||
async def show(self) -> Result[bool]:
|
||||
_ = await NotifyScreen(header=self._header).run()
|
||||
return Result.true()
|
||||
|
||||
|
||||
class GenericValidator(Validator):
|
||||
|
|
@ -161,11 +147,7 @@ class Input:
|
|||
self._allow_reset = allow_reset
|
||||
self._validator_callback = validator_callback
|
||||
|
||||
def show(self) -> Result[str]:
|
||||
result: Result[str] = tui.run(self)
|
||||
return result
|
||||
|
||||
async def _run(self) -> None:
|
||||
async def show(self) -> Result[str]:
|
||||
validator = GenericValidator(self._validator_callback) if self._validator_callback else None
|
||||
|
||||
result = await InputScreen(
|
||||
|
|
@ -182,9 +164,9 @@ class Input:
|
|||
confirmed = await _confirm_reset()
|
||||
|
||||
if confirmed.get_value() is False:
|
||||
return await self._run()
|
||||
return await self.show()
|
||||
|
||||
tui.exit(result)
|
||||
return result
|
||||
|
||||
|
||||
class Loading[ValueT]:
|
||||
|
|
@ -198,23 +180,19 @@ class Loading[ValueT]:
|
|||
self._timer = timer
|
||||
self._data_callback = data_callback
|
||||
|
||||
def show(self) -> Result[ValueT]:
|
||||
result: Result[ValueT] = tui.run(self)
|
||||
return result
|
||||
|
||||
async def _run(self) -> None:
|
||||
async def show(self) -> Result[ValueT]:
|
||||
if self._data_callback:
|
||||
result = await LoadingScreen(
|
||||
result = await LoadingScreen[ValueT](
|
||||
header=self._header,
|
||||
data_callback=self._data_callback,
|
||||
).run()
|
||||
tui.exit(result)
|
||||
return result
|
||||
else:
|
||||
await LoadingScreen(
|
||||
_ = await LoadingScreen(
|
||||
timer=self._timer,
|
||||
header=self._header,
|
||||
).run()
|
||||
tui.exit(Result.true())
|
||||
return Result.true()
|
||||
|
||||
|
||||
class Table[ValueT]:
|
||||
|
|
@ -245,11 +223,7 @@ class Table[ValueT]:
|
|||
if self._group is None and self._data_callback is None:
|
||||
raise ValueError('Either data or data_callback must be provided')
|
||||
|
||||
def show(self) -> Result[ValueT]:
|
||||
result: Result[ValueT] = tui.run(self)
|
||||
return result
|
||||
|
||||
async def _run(self) -> None:
|
||||
async def show(self) -> Result[ValueT]:
|
||||
result = await TableSelectionScreen[ValueT](
|
||||
header=self._header,
|
||||
group=self._group,
|
||||
|
|
@ -266,9 +240,9 @@ class Table[ValueT]:
|
|||
confirmed = await _confirm_reset()
|
||||
|
||||
if confirmed.get_value() is False:
|
||||
return await self._run()
|
||||
return await self.show()
|
||||
|
||||
tui.exit(result)
|
||||
return result
|
||||
|
||||
|
||||
async def _confirm_reset() -> Result[bool]:
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class ListManager[ValueT]:
|
|||
return self._last_choice == self._cancel_action
|
||||
return False
|
||||
|
||||
def _run(self) -> list[ValueT] | None:
|
||||
async def _run(self) -> list[ValueT] | None:
|
||||
additional_options = self._base_actions + self._terminate_actions
|
||||
|
||||
while True:
|
||||
|
|
@ -66,7 +66,7 @@ class ListManager[ValueT]:
|
|||
if self._prompt is not None:
|
||||
prompt = f'{self._prompt}\n\n'
|
||||
|
||||
result = Selection[ValueT | str](
|
||||
result = await Selection[ValueT | str](
|
||||
group,
|
||||
header=prompt,
|
||||
enable_filter=False,
|
||||
|
|
@ -81,14 +81,14 @@ class ListManager[ValueT]:
|
|||
|
||||
if value in self._base_actions:
|
||||
value = cast(str, value)
|
||||
self._data = self.handle_action(value, None, self._data)
|
||||
self._data = await self.handle_action(value, None, self._data)
|
||||
elif value in self._terminate_actions:
|
||||
break
|
||||
else: # an entry of the existing selection was chosen
|
||||
selected_entry = result.get_value()
|
||||
selected_entry = cast(ValueT, selected_entry)
|
||||
|
||||
self._run_actions_on_entry(selected_entry)
|
||||
await self._run_actions_on_entry(selected_entry)
|
||||
|
||||
self._last_choice = value
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ class ListManager[ValueT]:
|
|||
else:
|
||||
return self._data
|
||||
|
||||
def _run_actions_on_entry(self, entry: ValueT) -> None:
|
||||
async def _run_actions_on_entry(self, entry: ValueT) -> None:
|
||||
options = self.filter_options(entry, self._sub_menu_actions) + [self._cancel_action]
|
||||
|
||||
items = [MenuItem(o, value=o) for o in options]
|
||||
|
|
@ -105,7 +105,7 @@ class ListManager[ValueT]:
|
|||
|
||||
header = f'{self.selected_action_display(entry)}'
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=header,
|
||||
enable_filter=False,
|
||||
|
|
@ -119,7 +119,7 @@ class ListManager[ValueT]:
|
|||
raise ValueError('Unhandled return type')
|
||||
|
||||
if value != self._cancel_action:
|
||||
self._data = self.handle_action(value, entry, self._data)
|
||||
self._data = await self.handle_action(value, entry, self._data)
|
||||
|
||||
def selected_action_display(self, selection: ValueT) -> str:
|
||||
"""
|
||||
|
|
@ -128,7 +128,7 @@ class ListManager[ValueT]:
|
|||
"""
|
||||
raise NotImplementedError('Please implement me in the child class')
|
||||
|
||||
def handle_action(self, action: str, entry: ValueT | None, data: list[ValueT]) -> list[ValueT]:
|
||||
async def handle_action(self, action: str, entry: ValueT | None, data: list[ValueT]) -> list[ValueT]:
|
||||
"""
|
||||
this function is called when a base action or
|
||||
a specific action for an entry is triggered
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from archinstall.lib.menu.helpers import Input
|
||||
from archinstall.lib.menu.helpers import Confirmation, Input
|
||||
from archinstall.lib.models.users import Password
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import tui
|
||||
from archinstall.tui.ui.result import ResultType
|
||||
|
||||
|
||||
def get_password(
|
||||
async def get_password(
|
||||
header: str | None = None,
|
||||
allow_skip: bool = False,
|
||||
preset: str | None = None,
|
||||
skip_confirmation: bool = False,
|
||||
) -> Password | None:
|
||||
while True:
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=header,
|
||||
allow_skip=allow_skip,
|
||||
default_value=preset,
|
||||
|
|
@ -46,7 +49,7 @@ def get_password(
|
|||
return tr('The password did not match, please try again')
|
||||
return None
|
||||
|
||||
_ = Input(
|
||||
_ = await Input(
|
||||
header=confirmation_header,
|
||||
allow_skip=False,
|
||||
password=True,
|
||||
|
|
@ -56,7 +59,7 @@ def get_password(
|
|||
return password
|
||||
|
||||
|
||||
def prompt_dir(
|
||||
async def prompt_dir(
|
||||
header: str | None = None,
|
||||
validate: bool = True,
|
||||
must_exist: bool = True,
|
||||
|
|
@ -80,7 +83,7 @@ def prompt_dir(
|
|||
else:
|
||||
validate_func = None
|
||||
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=header,
|
||||
allow_skip=allow_skip,
|
||||
validator_callback=validate_func,
|
||||
|
|
@ -96,3 +99,33 @@ def prompt_dir(
|
|||
return Path(result.get_value())
|
||||
case _:
|
||||
return None
|
||||
|
||||
|
||||
async def confirm_abort() -> bool:
|
||||
prompt = tr('Do you really want to abort?') + '\n'
|
||||
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
preset=False,
|
||||
).show()
|
||||
|
||||
return result.get_value()
|
||||
|
||||
|
||||
def delayed_warning(message: str) -> bool:
|
||||
# Issue a final warning before we continue with something un-revertable.
|
||||
# We count down from 5 to 0.
|
||||
print(message, end='', flush=True)
|
||||
|
||||
try:
|
||||
countdown = '\n5...4...3...2...1\n'
|
||||
for c in countdown:
|
||||
print(c, end='', flush=True)
|
||||
time.sleep(0.25)
|
||||
except KeyboardInterrupt:
|
||||
ret: bool = tui.run(confirm_abort)
|
||||
if ret:
|
||||
sys.exit(1)
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -39,27 +39,27 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]):
|
|||
'',
|
||||
)
|
||||
|
||||
def show(self) -> list[CustomRepository] | None:
|
||||
return super()._run()
|
||||
async def show(self) -> list[CustomRepository] | None:
|
||||
return await super()._run()
|
||||
|
||||
@override
|
||||
def selected_action_display(self, selection: CustomRepository) -> str:
|
||||
return selection.name
|
||||
|
||||
@override
|
||||
def handle_action(
|
||||
async def handle_action(
|
||||
self,
|
||||
action: str,
|
||||
entry: CustomRepository | None,
|
||||
data: list[CustomRepository],
|
||||
) -> list[CustomRepository]:
|
||||
if action == self._actions[0]: # add
|
||||
new_repo = self._add_custom_repository()
|
||||
new_repo = await self._add_custom_repository()
|
||||
if new_repo is not None:
|
||||
data = [d for d in data if d.name != new_repo.name]
|
||||
data += [new_repo]
|
||||
elif action == self._actions[1] and entry: # modify repo
|
||||
new_repo = self._add_custom_repository(entry)
|
||||
new_repo = await self._add_custom_repository(entry)
|
||||
if new_repo is not None:
|
||||
data = [d for d in data if d.name != entry.name]
|
||||
data += [new_repo]
|
||||
|
|
@ -68,8 +68,8 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]):
|
|||
|
||||
return data
|
||||
|
||||
def _add_custom_repository(self, preset: CustomRepository | None = None) -> CustomRepository | None:
|
||||
edit_result = Input(
|
||||
async def _add_custom_repository(self, preset: CustomRepository | None = None) -> CustomRepository | None:
|
||||
edit_result = await Input(
|
||||
header=tr('Enter a respository name'),
|
||||
allow_skip=True,
|
||||
default_value=preset.name if preset else None,
|
||||
|
|
@ -86,7 +86,7 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]):
|
|||
header = f'{tr("Name")}: {name}\n'
|
||||
prompt = f'{header}\n' + tr('Enter the repository url')
|
||||
|
||||
edit_result = Input(
|
||||
edit_result = await Input(
|
||||
header=prompt,
|
||||
allow_skip=True,
|
||||
default_value=preset.url if preset else None,
|
||||
|
|
@ -109,7 +109,7 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]):
|
|||
if preset is not None:
|
||||
group.set_selected_by_value(preset.sign_check.value)
|
||||
|
||||
result = Selection[SignCheck](
|
||||
result = await Selection[SignCheck](
|
||||
group,
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
|
|
@ -130,7 +130,7 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]):
|
|||
if preset is not None:
|
||||
group.set_selected_by_value(preset.sign_option.value)
|
||||
|
||||
result = Selection(
|
||||
result = await Selection(
|
||||
group,
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
|
|
@ -160,27 +160,27 @@ class CustomMirrorServersList(ListManager[CustomServer]):
|
|||
'',
|
||||
)
|
||||
|
||||
def show(self) -> list[CustomServer] | None:
|
||||
return super()._run()
|
||||
async def show(self) -> list[CustomServer] | None:
|
||||
return await super()._run()
|
||||
|
||||
@override
|
||||
def selected_action_display(self, selection: CustomServer) -> str:
|
||||
return selection.url
|
||||
|
||||
@override
|
||||
def handle_action(
|
||||
async def handle_action(
|
||||
self,
|
||||
action: str,
|
||||
entry: CustomServer | None,
|
||||
data: list[CustomServer],
|
||||
) -> list[CustomServer]:
|
||||
if action == self._actions[0]: # add
|
||||
new_server = self._add_custom_server()
|
||||
new_server = await self._add_custom_server()
|
||||
if new_server is not None:
|
||||
data = [d for d in data if d.url != new_server.url]
|
||||
data += [new_server]
|
||||
elif action == self._actions[1] and entry: # modify repo
|
||||
new_server = self._add_custom_server(entry)
|
||||
new_server = await self._add_custom_server(entry)
|
||||
if new_server is not None:
|
||||
data = [d for d in data if d.url != entry.url]
|
||||
data += [new_server]
|
||||
|
|
@ -189,8 +189,8 @@ class CustomMirrorServersList(ListManager[CustomServer]):
|
|||
|
||||
return data
|
||||
|
||||
def _add_custom_server(self, preset: CustomServer | None = None) -> CustomServer | None:
|
||||
edit_result = Input(
|
||||
async def _add_custom_server(self, preset: CustomServer | None = None) -> CustomServer | None:
|
||||
edit_result = await Input(
|
||||
header=tr('Enter server url'),
|
||||
allow_skip=True,
|
||||
default_value=preset.url if preset else None,
|
||||
|
|
@ -453,15 +453,15 @@ class MirrorMenu(AbstractSubMenu[MirrorConfiguration]):
|
|||
return output.strip()
|
||||
|
||||
@override
|
||||
def run(self) -> MirrorConfiguration | None:
|
||||
return super().run()
|
||||
async def show(self) -> MirrorConfiguration | None:
|
||||
return await super().show()
|
||||
|
||||
|
||||
def select_mirror_regions(
|
||||
async def select_mirror_regions(
|
||||
mirror_list_handler: MirrorListHandler,
|
||||
preset: list[MirrorRegion],
|
||||
) -> list[MirrorRegion]:
|
||||
Loading[None](
|
||||
await Loading[None](
|
||||
header=tr('Loading mirror regions...'),
|
||||
data_callback=mirror_list_handler.load_mirrors,
|
||||
).show()
|
||||
|
|
@ -478,7 +478,7 @@ def select_mirror_regions(
|
|||
|
||||
group.set_selected_by_value(preset_regions)
|
||||
|
||||
result = Selection[MirrorRegion](
|
||||
result = await Selection[MirrorRegion](
|
||||
group,
|
||||
header=tr('Select mirror regions to be enabled'),
|
||||
allow_reset=True,
|
||||
|
|
@ -497,8 +497,8 @@ def select_mirror_regions(
|
|||
return selected_mirrors
|
||||
|
||||
|
||||
def add_custom_mirror_servers(preset: list[CustomServer] = []) -> list[CustomServer]:
|
||||
custom_mirrors = CustomMirrorServersList(preset).show()
|
||||
async def add_custom_mirror_servers(preset: list[CustomServer] = []) -> list[CustomServer]:
|
||||
custom_mirrors = await CustomMirrorServersList(preset).show()
|
||||
|
||||
if not custom_mirrors:
|
||||
return preset
|
||||
|
|
@ -506,8 +506,8 @@ def add_custom_mirror_servers(preset: list[CustomServer] = []) -> list[CustomSer
|
|||
return custom_mirrors
|
||||
|
||||
|
||||
def select_custom_mirror(preset: list[CustomRepository] = []) -> list[CustomRepository]:
|
||||
custom_mirrors = CustomMirrorRepositoriesList(preset).show()
|
||||
async def select_custom_mirror(preset: list[CustomRepository] = []) -> list[CustomRepository]:
|
||||
custom_mirrors = await CustomMirrorRepositoriesList(preset).show()
|
||||
|
||||
if not custom_mirrors:
|
||||
return preset
|
||||
|
|
@ -515,7 +515,7 @@ def select_custom_mirror(preset: list[CustomRepository] = []) -> list[CustomRepo
|
|||
return custom_mirrors
|
||||
|
||||
|
||||
def select_optional_repositories(preset: list[Repository]) -> list[Repository]:
|
||||
async def select_optional_repositories(preset: list[Repository]) -> list[Repository]:
|
||||
"""
|
||||
Allows the user to select additional repositories (multilib, and testing) if desired.
|
||||
|
||||
|
|
@ -533,7 +533,7 @@ def select_optional_repositories(preset: list[Repository]) -> list[Repository]:
|
|||
group = MenuItemGroup(items, sort_items=False)
|
||||
group.set_selected_by_value(preset)
|
||||
|
||||
result = Selection[Repository](
|
||||
result = await Selection[Repository](
|
||||
group,
|
||||
header=tr('Select optional repositories to be enabled'),
|
||||
allow_reset=True,
|
||||
|
|
|
|||
|
|
@ -25,31 +25,32 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
prompt,
|
||||
)
|
||||
|
||||
def show(self) -> list[Nic] | None:
|
||||
return super()._run()
|
||||
async def show(self) -> list[Nic] | None:
|
||||
return await super()._run()
|
||||
|
||||
@override
|
||||
def selected_action_display(self, selection: Nic) -> str:
|
||||
return selection.iface if selection.iface else ''
|
||||
|
||||
@override
|
||||
def handle_action(self, action: str, entry: Nic | None, data: list[Nic]) -> list[Nic]:
|
||||
async def handle_action(self, action: str, entry: Nic | None, data: list[Nic]) -> list[Nic]:
|
||||
if action == self._actions[0]: # add
|
||||
iface = self._select_iface(data)
|
||||
iface = await self._select_iface(data)
|
||||
if iface:
|
||||
nic = Nic(iface=iface)
|
||||
nic = self._edit_iface(nic)
|
||||
nic = await self._edit_iface(nic)
|
||||
data += [nic]
|
||||
elif entry:
|
||||
if action == self._actions[1]: # edit interface
|
||||
data = [d for d in data if d.iface != entry.iface]
|
||||
data.append(self._edit_iface(entry))
|
||||
nic = await self._edit_iface(entry)
|
||||
data.append(nic)
|
||||
elif action == self._actions[2]: # delete
|
||||
data = [d for d in data if d != entry]
|
||||
|
||||
return data
|
||||
|
||||
def _select_iface(self, data: list[Nic]) -> str | None:
|
||||
async def _select_iface(self, data: list[Nic]) -> str | None:
|
||||
all_ifaces = list_interfaces().values()
|
||||
existing_ifaces = [d.iface for d in data]
|
||||
available = set(all_ifaces) - set(existing_ifaces)
|
||||
|
|
@ -63,7 +64,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
items = [MenuItem(i, value=i) for i in available]
|
||||
group = MenuItemGroup(items, sort_items=True)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=tr('Select an interface'),
|
||||
allow_skip=True,
|
||||
|
|
@ -77,7 +78,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
case ResultType.Reset:
|
||||
raise ValueError('Unhandled result type')
|
||||
|
||||
def _get_ip_address(self, header: str, allow_skip: bool, multi: bool, preset: str | None = None, allow_empty: bool = False) -> str | None:
|
||||
async def _get_ip_address(self, header: str, allow_skip: bool, multi: bool, preset: str | None = None, allow_empty: bool = False) -> str | None:
|
||||
def validator(ip: str | None) -> str | None:
|
||||
failure = tr('You need to enter a valid IP in IP-config mode')
|
||||
|
||||
|
|
@ -98,7 +99,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
except ValueError:
|
||||
return failure
|
||||
|
||||
result = Input(
|
||||
result = await Input(
|
||||
header=header,
|
||||
validator_callback=validator,
|
||||
allow_skip=allow_skip,
|
||||
|
|
@ -113,7 +114,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
case ResultType.Reset:
|
||||
raise ValueError('Unhandled result type')
|
||||
|
||||
def _edit_iface(self, edit_nic: Nic) -> Nic:
|
||||
async def _edit_iface(self, edit_nic: Nic) -> Nic:
|
||||
iface_name = edit_nic.iface
|
||||
modes = ['DHCP (auto detect)', 'IP (static)']
|
||||
default_mode = 'DHCP (auto detect)'
|
||||
|
|
@ -124,7 +125,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
group = MenuItemGroup(items, sort_items=True)
|
||||
group.set_default_by_value(default_mode)
|
||||
|
||||
result = Selection[str](
|
||||
result = await Selection[str](
|
||||
group,
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
|
|
@ -142,10 +143,10 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
|
||||
if mode == 'IP (static)':
|
||||
header = tr('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name) + '\n'
|
||||
ip = self._get_ip_address(header, False, False)
|
||||
ip = await self._get_ip_address(header, False, False)
|
||||
|
||||
header = tr('Enter your gateway (router) IP address (leave blank for none)') + '\n'
|
||||
gateway = self._get_ip_address(header, True, False, allow_empty=True)
|
||||
gateway = await self._get_ip_address(header, True, False, allow_empty=True)
|
||||
|
||||
if edit_nic.dns:
|
||||
display_dns = ' '.join(edit_nic.dns)
|
||||
|
|
@ -153,7 +154,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
display_dns = None
|
||||
|
||||
header = tr('Enter your DNS servers with space separated (leave blank for none)') + '\n'
|
||||
dns_servers = self._get_ip_address(header, True, True, display_dns, allow_empty=True)
|
||||
dns_servers = await self._get_ip_address(header, True, True, display_dns, allow_empty=True)
|
||||
|
||||
dns = []
|
||||
if dns_servers is not None:
|
||||
|
|
@ -165,7 +166,7 @@ class ManualNetworkConfig(ListManager[Nic]):
|
|||
return Nic(iface=iface_name)
|
||||
|
||||
|
||||
def select_network(preset: NetworkConfiguration | None) -> NetworkConfiguration | None:
|
||||
async def select_network(preset: NetworkConfiguration | None) -> NetworkConfiguration | None:
|
||||
"""
|
||||
Configure the network on the newly installed system
|
||||
"""
|
||||
|
|
@ -176,7 +177,7 @@ def select_network(preset: NetworkConfiguration | None) -> NetworkConfiguration
|
|||
if preset:
|
||||
group.set_selected_by_value(preset.type)
|
||||
|
||||
result = Selection[NicType](
|
||||
result = await Selection[NicType](
|
||||
group,
|
||||
header=tr('Choose network configuration'),
|
||||
allow_reset=True,
|
||||
|
|
@ -200,7 +201,7 @@ def select_network(preset: NetworkConfiguration | None) -> NetworkConfiguration
|
|||
return NetworkConfiguration(NicType.NM_IWD)
|
||||
case NicType.MANUAL:
|
||||
preset_nics = preset.nics if preset else []
|
||||
nics = ManualNetworkConfig(tr('Configure interfaces'), preset_nics).show()
|
||||
nics = await ManualNetworkConfig(tr('Configure interfaces'), preset_nics).show()
|
||||
|
||||
if nics:
|
||||
return NetworkConfiguration(NicType.MANUAL, nics)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from asyncio import sleep
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import assert_never
|
||||
from typing import assert_never, override
|
||||
|
||||
from archinstall.lib.command import SysCommand
|
||||
from archinstall.lib.exceptions import SysCallError
|
||||
|
|
@ -9,7 +9,7 @@ from archinstall.lib.models.network import WifiConfiguredNetwork, WifiNetwork
|
|||
from archinstall.lib.network.wpa_supplicant import WpaSupplicantConfig
|
||||
from archinstall.lib.output import debug
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import ConfirmationScreen, InputScreen, LoadingScreen, NotifyScreen, TableSelectionScreen, tui
|
||||
from archinstall.tui.ui.components import ConfirmationScreen, InputScreen, InstanceRunnable, LoadingScreen, NotifyScreen, TableSelectionScreen, tui
|
||||
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.ui.result import Result, ResultType
|
||||
|
||||
|
|
@ -21,15 +21,12 @@ class WpaCliResult:
|
|||
error: str | None = None
|
||||
|
||||
|
||||
class WifiHandler:
|
||||
class WifiHandler(InstanceRunnable[bool]):
|
||||
def __init__(self) -> None:
|
||||
self._wpa_config = WpaSupplicantConfig()
|
||||
self._wpa_config: WpaSupplicantConfig = WpaSupplicantConfig()
|
||||
|
||||
def setup(self) -> bool:
|
||||
result: Result[bool] = tui.run(self)
|
||||
return result.get_value()
|
||||
|
||||
async def _run(self) -> None:
|
||||
@override
|
||||
async def run(self) -> bool | None:
|
||||
"""
|
||||
This is the entry point that is called by components.TApp
|
||||
"""
|
||||
|
|
@ -37,8 +34,7 @@ class WifiHandler:
|
|||
|
||||
if not wifi_iface:
|
||||
debug('No wifi interface found')
|
||||
tui.exit(Result.false())
|
||||
return None
|
||||
return False
|
||||
|
||||
prompt = tr('No network connection found') + '\n\n'
|
||||
prompt += tr('Would you like to connect to a Wifi?') + '\n'
|
||||
|
|
@ -53,14 +49,12 @@ class WifiHandler:
|
|||
match result.type_:
|
||||
case ResultType.Selection:
|
||||
if result.get_value() is False:
|
||||
tui.exit(Result.false())
|
||||
return None
|
||||
return False
|
||||
case ResultType.Skip | ResultType.Reset:
|
||||
tui.exit(Result.false())
|
||||
return None
|
||||
return False
|
||||
|
||||
setup_result = await self._setup_wifi(wifi_iface)
|
||||
tui.exit(Result(ResultType.Selection, _data=setup_result))
|
||||
return setup_result
|
||||
|
||||
async def _enable_supplicant(self, wifi_iface: str) -> bool:
|
||||
self._wpa_config.load_config()
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ def _parse_package_output[PackageType: (AvailablePackage, LocalPackage)](
|
|||
return cls.model_validate(package)
|
||||
|
||||
|
||||
def select_additional_packages(
|
||||
async def select_additional_packages(
|
||||
preset: list[str] = [],
|
||||
repositories: set[Repository] = set(),
|
||||
) -> list[str]:
|
||||
|
|
@ -94,7 +94,7 @@ def select_additional_packages(
|
|||
output = tr('Repositories: {}').format(respos_text) + '\n'
|
||||
output += tr('Loading packages...')
|
||||
|
||||
result = Loading[dict[str, AvailablePackage]](
|
||||
result = await Loading[dict[str, AvailablePackage]](
|
||||
header=output,
|
||||
data_callback=lambda: list_available_packages(tuple(repositories)),
|
||||
).show()
|
||||
|
|
@ -106,7 +106,7 @@ def select_additional_packages(
|
|||
packages = result.get_value()
|
||||
|
||||
if not packages:
|
||||
Notify(tr('No packages found')).show()
|
||||
await Notify(tr('No packages found')).show()
|
||||
return []
|
||||
|
||||
package_groups = PackageGroup.from_available_packages(packages)
|
||||
|
|
@ -145,7 +145,7 @@ def select_additional_packages(
|
|||
menu_group = MenuItemGroup(items, sort_items=True)
|
||||
menu_group.set_selected_by_value(preset_packages)
|
||||
|
||||
pck_result = Selection[AvailablePackage | PackageGroup](
|
||||
pck_result = await Selection[AvailablePackage | PackageGroup](
|
||||
menu_group,
|
||||
header=header,
|
||||
allow_reset=True,
|
||||
|
|
|
|||
|
|
@ -60,11 +60,11 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]):
|
|||
]
|
||||
|
||||
@override
|
||||
def run(self) -> ProfileConfiguration | None:
|
||||
return super().run()
|
||||
async def show(self) -> ProfileConfiguration | None:
|
||||
return await super().show()
|
||||
|
||||
def _select_profile(self, preset: Profile | None) -> Profile | None:
|
||||
profile = select_profile(preset)
|
||||
async def _select_profile(self, preset: Profile | None) -> Profile | None:
|
||||
profile = await select_profile(preset)
|
||||
|
||||
if profile is not None:
|
||||
if not profile.is_graphic_driver_supported():
|
||||
|
|
@ -86,20 +86,20 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]):
|
|||
|
||||
return profile
|
||||
|
||||
def _select_gfx_driver(self, preset: GfxDriver | None = None) -> GfxDriver | None:
|
||||
async def _select_gfx_driver(self, preset: GfxDriver | None = None) -> GfxDriver | None:
|
||||
driver = preset
|
||||
profile: Profile | None = self._item_group.find_by_key('profile').value
|
||||
|
||||
if profile:
|
||||
if profile.is_graphic_driver_supported():
|
||||
driver = select_driver(preset=preset)
|
||||
driver = await select_driver(preset=preset)
|
||||
|
||||
if driver and 'Sway' in profile.current_selection_names():
|
||||
if driver.is_nvidia():
|
||||
header = tr('The proprietary Nvidia driver is not supported by Sway.') + '\n'
|
||||
header += tr('It is likely that you will run into issues, are you okay with that?') + '\n'
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=header,
|
||||
allow_skip=False,
|
||||
preset=False,
|
||||
|
|
@ -140,7 +140,7 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]):
|
|||
return None
|
||||
|
||||
|
||||
def select_greeter(
|
||||
async def select_greeter(
|
||||
profile: Profile | None = None,
|
||||
preset: GreeterType | None = None,
|
||||
) -> GreeterType | None:
|
||||
|
|
@ -157,7 +157,7 @@ def select_greeter(
|
|||
|
||||
group.set_default_by_value(default)
|
||||
|
||||
result = Selection[GreeterType](
|
||||
result = await Selection[GreeterType](
|
||||
group,
|
||||
header=tr('Select which greeter to install'),
|
||||
allow_skip=True,
|
||||
|
|
@ -174,7 +174,7 @@ def select_greeter(
|
|||
return None
|
||||
|
||||
|
||||
def select_profile(
|
||||
async def select_profile(
|
||||
current_profile: Profile | None = None,
|
||||
header: str | None = None,
|
||||
allow_reset: bool = True,
|
||||
|
|
@ -190,7 +190,7 @@ def select_profile(
|
|||
group = MenuItemGroup(items, sort_items=True)
|
||||
group.set_selected_by_value(current_profile)
|
||||
|
||||
result = Selection[Profile](
|
||||
result = await Selection[Profile](
|
||||
group,
|
||||
header=header,
|
||||
allow_reset=allow_reset,
|
||||
|
|
@ -204,7 +204,7 @@ def select_profile(
|
|||
return current_profile
|
||||
case ResultType.Selection:
|
||||
profile_selection = result.get_value()
|
||||
select_result = profile_selection.do_on_select()
|
||||
select_result = await profile_selection.do_on_select()
|
||||
|
||||
if not select_result:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -26,17 +26,17 @@ class UserList(ListManager[User]):
|
|||
prompt,
|
||||
)
|
||||
|
||||
def show(self) -> list[User] | None:
|
||||
return super()._run()
|
||||
async def show(self) -> list[User] | None:
|
||||
return await super()._run()
|
||||
|
||||
@override
|
||||
def selected_action_display(self, selection: User) -> str:
|
||||
return selection.username
|
||||
|
||||
@override
|
||||
def handle_action(self, action: str, entry: User | None, data: list[User]) -> list[User]:
|
||||
async def handle_action(self, action: str, entry: User | None, data: list[User]) -> list[User]:
|
||||
if action == self._actions[0]: # add
|
||||
new_user = self._add_user()
|
||||
new_user = await 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
|
||||
|
|
@ -45,7 +45,7 @@ class UserList(ListManager[User]):
|
|||
elif action == self._actions[1] and entry: # change password
|
||||
header = f'{tr("User")}: {entry.username}\n'
|
||||
header += tr('Enter new password')
|
||||
new_password = get_password(header=header)
|
||||
new_password = await get_password(header=header)
|
||||
|
||||
if new_password:
|
||||
user = next(filter(lambda x: x == entry, data))
|
||||
|
|
@ -64,8 +64,8 @@ class UserList(ListManager[User]):
|
|||
return None
|
||||
return tr('The username you entered is invalid')
|
||||
|
||||
def _add_user(self) -> User | None:
|
||||
editResult = Input(
|
||||
async def _add_user(self) -> User | None:
|
||||
editResult = await Input(
|
||||
tr('Enter a username'),
|
||||
allow_skip=True,
|
||||
validator_callback=self._check_for_correct_username,
|
||||
|
|
@ -85,7 +85,7 @@ class UserList(ListManager[User]):
|
|||
header = f'{tr("Username")}: {username}\n'
|
||||
prompt = f'{header}\n' + tr('Enter a password')
|
||||
|
||||
password = get_password(header=prompt, allow_skip=True)
|
||||
password = await get_password(header=prompt, allow_skip=True)
|
||||
|
||||
if not password:
|
||||
return None
|
||||
|
|
@ -93,7 +93,7 @@ class UserList(ListManager[User]):
|
|||
header += f'{tr("Password")}: {password.hidden()}\n'
|
||||
prompt = f'{header}\n' + tr('Should "{}" be a superuser (sudo)?\n').format(username)
|
||||
|
||||
result = Confirmation(
|
||||
result = await Confirmation(
|
||||
header=prompt,
|
||||
allow_skip=False,
|
||||
preset=True,
|
||||
|
|
@ -108,8 +108,8 @@ class UserList(ListManager[User]):
|
|||
return User(username, password, sudo)
|
||||
|
||||
|
||||
def select_users(prompt: str = '', preset: list[User] = []) -> list[User]:
|
||||
users = UserList(prompt, preset).show()
|
||||
async def select_users(prompt: str = '', preset: list[User] = []) -> list[User]:
|
||||
users = await UserList(prompt, preset).show()
|
||||
|
||||
if users is None:
|
||||
return preset
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from archinstall.lib.packages.util import check_version_upgrade
|
|||
from archinstall.lib.pacman.pacman import Pacman
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.lib.utils.util import running_from_iso
|
||||
from archinstall.tui.ui.components import tui
|
||||
|
||||
|
||||
def _log_sys_info() -> None:
|
||||
|
|
@ -38,8 +39,8 @@ def _check_online(wifi_handler: WifiHandler | None = None) -> bool:
|
|||
except OSError as ex:
|
||||
if 'Network is unreachable' in str(ex):
|
||||
if wifi_handler is not None:
|
||||
success = not wifi_handler.setup()
|
||||
if not success:
|
||||
result: bool = tui.run(wifi_handler)
|
||||
if not result:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
from archinstall.lib.applications.application_handler import ApplicationHandler
|
||||
from archinstall.lib.args import ArchConfigHandler
|
||||
from archinstall.lib.args import ArchConfig, ArchConfigHandler
|
||||
from archinstall.lib.authentication.authentication_handler import AuthenticationHandler
|
||||
from archinstall.lib.configuration import ConfigurationOutput
|
||||
from archinstall.lib.disk.filesystem import FilesystemHandler
|
||||
|
|
@ -10,6 +11,7 @@ from archinstall.lib.disk.utils import disk_layouts
|
|||
from archinstall.lib.global_menu import GlobalMenu
|
||||
from archinstall.lib.installer import Installer, accessibility_tools_in_use, run_custom_user_commands
|
||||
from archinstall.lib.interactions.general_conf import PostInstallationAction, select_post_installation
|
||||
from archinstall.lib.menu.util import delayed_warning
|
||||
from archinstall.lib.mirrors import MirrorListHandler
|
||||
from archinstall.lib.models import Bootloader
|
||||
from archinstall.lib.models.device import DiskLayoutType, EncryptionType
|
||||
|
|
@ -19,6 +21,7 @@ from archinstall.lib.output import debug, error, info
|
|||
from archinstall.lib.packages.util import check_version_upgrade
|
||||
from archinstall.lib.profile.profiles_handler import profile_handler
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import tui
|
||||
|
||||
|
||||
def show_menu(
|
||||
|
|
@ -42,7 +45,9 @@ def show_menu(
|
|||
if not arch_config_handler.args.advanced:
|
||||
global_menu.set_enabled('parallel_downloads', False)
|
||||
|
||||
global_menu.run()
|
||||
result: ArchConfig | None = tui.run(global_menu)
|
||||
if result is None:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def perform_installation(
|
||||
|
|
@ -177,13 +182,13 @@ def perform_installation(
|
|||
|
||||
if not arch_config_handler.args.silent:
|
||||
elapsed_time = time.monotonic() - start_time
|
||||
action = select_post_installation(elapsed_time)
|
||||
action: PostInstallationAction = tui.run(lambda: select_post_installation(elapsed_time))
|
||||
|
||||
match action:
|
||||
case PostInstallationAction.EXIT:
|
||||
pass
|
||||
case PostInstallationAction.REBOOT:
|
||||
os.system('reboot')
|
||||
_ = os.system('reboot')
|
||||
case PostInstallationAction.CHROOT:
|
||||
try:
|
||||
installation.drop_to_shell()
|
||||
|
|
@ -212,7 +217,9 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
|||
|
||||
if not arch_config_handler.args.silent:
|
||||
aborted = False
|
||||
if not config.confirm_config():
|
||||
res: bool = tui.run(config.confirm_config)
|
||||
|
||||
if not res:
|
||||
debug('Installation aborted')
|
||||
aborted = True
|
||||
|
||||
|
|
@ -221,6 +228,10 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
|||
|
||||
if arch_config_handler.config.disk_config:
|
||||
fs_handler = FilesystemHandler(arch_config_handler.config.disk_config)
|
||||
|
||||
if not delayed_warning(tr('Starting device modifications in ')):
|
||||
return main()
|
||||
|
||||
fs_handler.perform_filesystem_operations()
|
||||
|
||||
perform_installation(
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@ from archinstall.lib.configuration import ConfigurationOutput
|
|||
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
|
||||
from archinstall.lib.disk.filesystem import FilesystemHandler
|
||||
from archinstall.lib.installer import Installer
|
||||
from archinstall.lib.menu.util import delayed_warning
|
||||
from archinstall.lib.models import Bootloader
|
||||
from archinstall.lib.models.profile import ProfileConfiguration
|
||||
from archinstall.lib.models.users import Password, User
|
||||
from archinstall.lib.network.network_handler import NetworkHandler
|
||||
from archinstall.lib.output import debug, error, info
|
||||
from archinstall.lib.profile.profiles_handler import profile_handler
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import tui
|
||||
|
||||
|
||||
def perform_installation(arch_config_handler: ArchConfigHandler) -> None:
|
||||
|
|
@ -58,11 +61,11 @@ def perform_installation(arch_config_handler: ArchConfigHandler) -> None:
|
|||
info(' * devel (password: devel)')
|
||||
|
||||
|
||||
def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
||||
async def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
||||
if arch_config_handler is None:
|
||||
arch_config_handler = ArchConfigHandler()
|
||||
|
||||
disk_config = DiskLayoutConfigurationMenu(disk_layout_config=None).run()
|
||||
disk_config = await DiskLayoutConfigurationMenu(disk_layout_config=None).show()
|
||||
arch_config_handler.config.disk_config = disk_config
|
||||
|
||||
config = ConfigurationOutput(arch_config_handler.config)
|
||||
|
|
@ -74,19 +77,25 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
|||
|
||||
if not arch_config_handler.args.silent:
|
||||
aborted = False
|
||||
if not config.confirm_config():
|
||||
res: bool = tui.run(config.confirm_config)
|
||||
|
||||
if not res:
|
||||
debug('Installation aborted')
|
||||
aborted = True
|
||||
|
||||
if aborted:
|
||||
return main(arch_config_handler)
|
||||
return await main(arch_config_handler)
|
||||
|
||||
if arch_config_handler.config.disk_config:
|
||||
fs_handler = FilesystemHandler(arch_config_handler.config.disk_config)
|
||||
|
||||
if not delayed_warning(tr('Starting device modifications in ')):
|
||||
return await main()
|
||||
|
||||
fs_handler.perform_filesystem_operations()
|
||||
|
||||
perform_installation(arch_config_handler)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
tui.run(main)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from archinstall.lib.args import ArchConfigHandler
|
||||
from archinstall.lib.args import ArchConfig, ArchConfigHandler
|
||||
from archinstall.lib.configuration import ConfigurationOutput
|
||||
from archinstall.lib.disk.filesystem import FilesystemHandler
|
||||
from archinstall.lib.disk.utils import disk_layouts
|
||||
from archinstall.lib.global_menu import GlobalMenu
|
||||
from archinstall.lib.installer import Installer
|
||||
from archinstall.lib.menu.util import delayed_warning
|
||||
from archinstall.lib.output import debug, error
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import tui
|
||||
|
||||
|
||||
def show_menu(arch_config_handler: ArchConfigHandler) -> None:
|
||||
|
|
@ -18,7 +22,9 @@ def show_menu(arch_config_handler: ArchConfigHandler) -> None:
|
|||
global_menu.set_enabled('swap', True)
|
||||
global_menu.set_enabled('__config__', True)
|
||||
|
||||
global_menu.run()
|
||||
result: ArchConfig | None = tui.run(global_menu)
|
||||
if result is None:
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def perform_installation(arch_config_handler: ArchConfigHandler) -> None:
|
||||
|
|
@ -72,7 +78,9 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
|||
|
||||
if not arch_config_handler.args.silent:
|
||||
aborted = False
|
||||
if not config.confirm_config():
|
||||
res: bool = tui.run(config.confirm_config)
|
||||
|
||||
if not res:
|
||||
debug('Installation aborted')
|
||||
aborted = True
|
||||
|
||||
|
|
@ -81,6 +89,10 @@ def main(arch_config_handler: ArchConfigHandler | None = None) -> None:
|
|||
|
||||
if arch_config_handler.config.disk_config:
|
||||
fs_handler = FilesystemHandler(arch_config_handler.config.disk_config)
|
||||
|
||||
if not delayed_warning(tr('Starting device modifications in ')):
|
||||
return main()
|
||||
|
||||
fs_handler.perform_filesystem_operations()
|
||||
|
||||
perform_installation(arch_config_handler)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import sys
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any, ClassVar, Literal, TypeVar, override
|
||||
from typing import Any, ClassVar, Literal, TypeVar, cast, override
|
||||
|
||||
from textual import work
|
||||
from textual.app import App, ComposeResult
|
||||
|
|
@ -44,7 +45,7 @@ class BaseScreen(Screen[Result[ValueT]]):
|
|||
_ = self.dismiss(Result(ResultType.Reset))
|
||||
|
||||
|
||||
class LoadingScreen(BaseScreen[None]):
|
||||
class LoadingScreen(BaseScreen[ValueT]):
|
||||
CSS = """
|
||||
LoadingScreen {
|
||||
align: center middle;
|
||||
|
|
@ -78,7 +79,7 @@ class LoadingScreen(BaseScreen[None]):
|
|||
self._header = header
|
||||
self._data_callback = data_callback
|
||||
|
||||
async def run(self) -> Result[None]:
|
||||
async def run(self) -> Result[ValueT]:
|
||||
assert TApp.app
|
||||
return await TApp.app.show(self)
|
||||
|
||||
|
|
@ -1051,6 +1052,12 @@ class TableSelectionScreen(BaseScreen[ValueT]):
|
|||
)
|
||||
|
||||
|
||||
class InstanceRunnable[ValueT](ABC):
|
||||
@abstractmethod
|
||||
async def run(self) -> ValueT | None:
|
||||
pass
|
||||
|
||||
|
||||
class _AppInstance(App[ValueT]):
|
||||
ENABLE_COMMAND_PALETTE = False
|
||||
|
||||
|
|
@ -1148,7 +1155,7 @@ class _AppInstance(App[ValueT]):
|
|||
}
|
||||
"""
|
||||
|
||||
def __init__(self, main: Any) -> None:
|
||||
def __init__(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) -> None:
|
||||
super().__init__(ansi_color=True)
|
||||
self._main = main
|
||||
|
||||
|
|
@ -1166,13 +1173,18 @@ class _AppInstance(App[ValueT]):
|
|||
@work
|
||||
async def _run_worker(self) -> None:
|
||||
try:
|
||||
await self._main._run()
|
||||
if isinstance(self._main, InstanceRunnable):
|
||||
result: ValueT | None = await self._main.run()
|
||||
else:
|
||||
result = await self._main()
|
||||
|
||||
tui.exit(result)
|
||||
except WorkerCancelled:
|
||||
debug('Worker was cancelled')
|
||||
except Exception as err:
|
||||
debug(f'Error while running main app: {err}')
|
||||
# this will terminate the textual app and return the exception
|
||||
self.exit(err) # type: ignore[arg-type]
|
||||
self.exit(cast(ValueT, err))
|
||||
|
||||
@work
|
||||
async def _show_async(self, screen: Screen[Result[ValueT]]) -> Result[ValueT]:
|
||||
|
|
@ -1185,13 +1197,9 @@ class _AppInstance(App[ValueT]):
|
|||
class TApp:
|
||||
app: _AppInstance[Any] | None = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._main = None
|
||||
self._global_header: str | None = None
|
||||
|
||||
def run(self, main: Any) -> Result[ValueT]:
|
||||
def run(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) -> ValueT:
|
||||
TApp.app = _AppInstance(main)
|
||||
result: Result[ValueT] | Exception | None = TApp.app.run()
|
||||
result: ValueT | Exception | None = TApp.app.run()
|
||||
|
||||
if isinstance(result, Exception):
|
||||
raise result
|
||||
|
|
@ -1202,7 +1210,7 @@ class TApp:
|
|||
|
||||
return result
|
||||
|
||||
def exit(self, result: Result[ValueT]) -> None:
|
||||
def exit(self, result: Any) -> None:
|
||||
assert TApp.app
|
||||
TApp.app.exit(result)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from functools import cached_property
|
||||
|
|
@ -13,7 +13,7 @@ from archinstall.lib.translationhandler import tr
|
|||
class MenuItem:
|
||||
text: str
|
||||
value: Any | None = None
|
||||
action: Callable[[Any], Any] | None = None
|
||||
action: Callable[[Any], Awaitable[Any]] | None = None
|
||||
enabled: bool = True
|
||||
read_only: bool = False
|
||||
mandatory: bool = False
|
||||
|
|
|
|||
|
|
@ -25,6 +25,18 @@ class Result[ValueT]:
|
|||
def false(cls) -> Self:
|
||||
return cls(ResultType.Selection, _data=False) # type: ignore[arg-type]
|
||||
|
||||
@classmethod
|
||||
def reset(cls) -> Self:
|
||||
return cls(ResultType.Reset)
|
||||
|
||||
@classmethod
|
||||
def selection(cls, value: ValueT | list[ValueT] | None) -> Self:
|
||||
return cls(ResultType.Selection, _data=value)
|
||||
|
||||
@classmethod
|
||||
def skip(cls) -> Self:
|
||||
return cls(ResultType.Skip)
|
||||
|
||||
def has_data(self) -> bool:
|
||||
return self._data is not None
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ fs_handler = FilesystemHandler(disk_config)
|
|||
|
||||
# perform all file operations
|
||||
# WARNING: this will potentially format the filesystem and delete all data
|
||||
fs_handler.perform_filesystem_operations(show_countdown=False)
|
||||
fs_handler.perform_filesystem_operations()
|
||||
|
||||
mountpoint = Path('/tmp')
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue