From 0de90bd55beaa87a9b7ecc591cd2b172ae7d5835 Mon Sep 17 00:00:00 2001 From: correctmost <134317971+correctmost@users.noreply.github.com> Date: Sun, 27 Apr 2025 03:01:17 +0000 Subject: [PATCH] Specify menu return types using generics instead of Any (#3400) --- archinstall/default_profiles/desktop.py | 2 +- .../default_profiles/desktops/hyprland.py | 2 +- .../default_profiles/desktops/labwc.py | 2 +- archinstall/default_profiles/desktops/niri.py | 2 +- archinstall/default_profiles/desktops/sway.py | 2 +- archinstall/default_profiles/server.py | 2 +- archinstall/lib/configuration.py | 4 +-- archinstall/lib/disk/disk_menu.py | 4 +-- archinstall/lib/disk/encryption_menu.py | 10 +++---- archinstall/lib/disk/partitioning_menu.py | 8 ++--- archinstall/lib/disk/subvolume_menu.py | 4 +-- archinstall/lib/global_menu.py | 2 +- archinstall/lib/interactions/disk_conf.py | 18 ++++++------ archinstall/lib/interactions/general_conf.py | 20 ++++++------- .../lib/interactions/manage_users_conf.py | 6 ++-- archinstall/lib/interactions/network_menu.py | 10 +++---- archinstall/lib/interactions/system_conf.py | 10 +++---- archinstall/lib/locale/locale_menu.py | 8 ++--- archinstall/lib/menu/abstract_menu.py | 8 ++--- archinstall/lib/menu/list_manager.py | 29 ++++++++++--------- archinstall/lib/mirrors.py | 22 +++++++------- archinstall/lib/profile/profile_menu.py | 10 +++---- archinstall/lib/utils/util.py | 6 ++-- archinstall/tui/curses_menu.py | 26 ++++++++--------- archinstall/tui/result.py | 9 +++--- 25 files changed, 114 insertions(+), 112 deletions(-) diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py index 19149f72..64555342 100644 --- a/archinstall/default_profiles/desktop.py +++ b/archinstall/default_profiles/desktop.py @@ -68,7 +68,7 @@ class DesktopProfile(Profile): group = MenuItemGroup(items, sort_items=True, sort_case_sensitive=False) group.set_selected_by_value(self.current_selection) - result = SelectMenu( + result = SelectMenu[Profile]( group, multi=True, allow_reset=True, diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py index 5904e4ab..ad7ae900 100644 --- a/archinstall/default_profiles/desktops/hyprland.py +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -62,7 +62,7 @@ class HyprlandProfile(XorgProfile): default = self.custom_settings.get('seat_access', None) group.set_default_by_value(default) - result = SelectMenu( + result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, diff --git a/archinstall/default_profiles/desktops/labwc.py b/archinstall/default_profiles/desktops/labwc.py index 8fc30d5a..cbfd3eea 100644 --- a/archinstall/default_profiles/desktops/labwc.py +++ b/archinstall/default_profiles/desktops/labwc.py @@ -60,7 +60,7 @@ class LabwcProfile(XorgProfile): default = self.custom_settings.get('seat_access', None) group.set_default_by_value(default) - result = SelectMenu( + result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, diff --git a/archinstall/default_profiles/desktops/niri.py b/archinstall/default_profiles/desktops/niri.py index 8472f5de..ef1a8e08 100644 --- a/archinstall/default_profiles/desktops/niri.py +++ b/archinstall/default_profiles/desktops/niri.py @@ -68,7 +68,7 @@ class NiriProfile(XorgProfile): default = self.custom_settings.get('seat_access', None) group.set_default_by_value(default) - result = SelectMenu( + result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index c228a0bb..79c4886e 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -70,7 +70,7 @@ class SwayProfile(XorgProfile): default = self.custom_settings.get('seat_access', None) group.set_default_by_value(default) - result = SelectMenu( + result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, diff --git a/archinstall/default_profiles/server.py b/archinstall/default_profiles/server.py index 38ef26a5..60271020 100644 --- a/archinstall/default_profiles/server.py +++ b/archinstall/default_profiles/server.py @@ -33,7 +33,7 @@ class ServerProfile(Profile): group = MenuItemGroup(items, sort_items=True) group.set_selected_by_value(self.current_selection) - result = SelectMenu( + result = SelectMenu[Profile]( group, allow_reset=True, allow_skip=True, diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 5f790b17..d3b7ba4a 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -69,7 +69,7 @@ class ConfigurationOutput: group.focus_item = MenuItem.yes() group.set_preview_for_all(lambda x: self.user_config_to_json()) - result = SelectMenu( + result = SelectMenu[bool]( group, header=header, alignment=Alignment.CENTER, @@ -168,7 +168,7 @@ def save_config(config: ArchConfig) -> None: ] group = MenuItemGroup(items) - result = SelectMenu( + result = SelectMenu[str]( group, allow_skip=True, preview_frame=FrameProperties.max(str(_('Configuration'))), diff --git a/archinstall/lib/disk/disk_menu.py b/archinstall/lib/disk/disk_menu.py index 597e702a..d9db86ea 100644 --- a/archinstall/lib/disk/disk_menu.py +++ b/archinstall/lib/disk/disk_menu.py @@ -22,7 +22,7 @@ class DiskMenuConfig: lvm_config: LvmConfiguration | None -class DiskLayoutConfigurationMenu(AbstractSubMenu): +class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): def __init__(self, disk_layout_config: DiskLayoutConfiguration | None): if not disk_layout_config: self._disk_menu_config = DiskMenuConfig(disk_config=None, lvm_config=None) @@ -101,7 +101,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu): if not item.value: return None - disk_layout_conf: DiskLayoutConfiguration = item.get_value() + disk_layout_conf = item.get_value() if disk_layout_conf.config_type == DiskLayoutType.Pre_mount: msg = str(_('Configuration type: {}')).format(disk_layout_conf.config_type.display_msg()) + '\n' diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py index f7f81952..ec14d242 100644 --- a/archinstall/lib/disk/encryption_menu.py +++ b/archinstall/lib/disk/encryption_menu.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class DiskEncryptionMenu(AbstractSubMenu): +class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): def __init__( self, disk_config: DiskLayoutConfiguration, @@ -233,7 +233,7 @@ def select_encryption_type(disk_config: DiskLayoutConfiguration, preset: Encrypt group = MenuItemGroup(items) group.set_focus_by_value(preset_value) - result = SelectMenu( + result = SelectMenu[EncryptionType]( group, allow_skip=True, allow_reset=True, @@ -273,7 +273,7 @@ def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None: group, table_header = MenuHelper.create_table(data=fido_devices) header = f'{header}\n\n{table_header}' - result = SelectMenu( + result = SelectMenu[Fido2Device]( group, header=header, alignment=Alignment.CENTER, @@ -309,7 +309,7 @@ def select_partitions_to_encrypt( if avail_partitions: group, header = MenuHelper.create_table(data=avail_partitions) - result = SelectMenu( + result = SelectMenu[PartitionModification]( group, header=header, alignment=Alignment.CENTER, @@ -337,7 +337,7 @@ def select_lvm_vols_to_encrypt( if volumes: group, header = MenuHelper.create_table(data=volumes) - result = SelectMenu( + result = SelectMenu[LvmVolume]( group, header=header, alignment=Alignment.CENTER, diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 2a52e76d..094c7806 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -77,7 +77,7 @@ class DiskSegment: return data -class PartitioningList(ListManager): +class PartitioningList(ListManager[DiskSegment]): def __init__( self, device_mod: DeviceModification, @@ -438,7 +438,7 @@ class PartitioningList(ListManager): items = [MenuItem(fs.value, value=fs) for fs in fs_types] group = MenuItemGroup(items, sort_items=False) - result = SelectMenu( + result = SelectMenu[FilesystemType]( group, header=prompt, alignment=Alignment.CENTER, @@ -509,7 +509,7 @@ class PartitioningList(ListManager): title = str(_('Size (default: {}): ')).format(max_size.format_highest()) - result = EditMenu( + result = EditMenu[str]( title, header=f'{prompt}\b', allow_skip=True, @@ -564,7 +564,7 @@ class PartitioningList(ListManager): def _reset_confirmation(self) -> bool: prompt = str(_('This will remove all newly added partitions, continue?')) + '\n' - result = SelectMenu( + result = SelectMenu[bool]( MenuItemGroup.yes_no(), header=prompt, alignment=Alignment.CENTER, diff --git a/archinstall/lib/disk/subvolume_menu.py b/archinstall/lib/disk/subvolume_menu.py index 0d5c1e49..1dfb8928 100644 --- a/archinstall/lib/disk/subvolume_menu.py +++ b/archinstall/lib/disk/subvolume_menu.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class SubvolumeMenu(ListManager): +class SubvolumeMenu(ListManager[SubvolumeModification]): def __init__( self, btrfs_subvols: list[SubvolumeModification], @@ -41,7 +41,7 @@ class SubvolumeMenu(ListManager): return str(selection.name) def _add_subvolume(self, preset: SubvolumeModification | None = None) -> SubvolumeModification | None: - result = EditMenu( + result = EditMenu[str]( str(_('Subvolume name')), alignment=Alignment.CENTER, allow_skip=True, diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 2680e3d5..612ff1dc 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -43,7 +43,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class GlobalMenu(AbstractMenu): +class GlobalMenu(AbstractMenu[None]): def __init__(self, arch_config: ArchConfig) -> None: self._arch_config = arch_config menu_optioons = self._get_menu_options() diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index 4d3d9d68..02f66684 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -46,7 +46,7 @@ if TYPE_CHECKING: def select_devices(preset: list[BDevice] | None = []) -> list[BDevice]: def _preview_device_selection(item: MenuItem) -> str | None: - device: _DeviceInfo = item.get_value() + device = item.get_value() dev = device_handler.get_device(device.path) if dev and dev.partition_infos: @@ -64,7 +64,7 @@ def select_devices(preset: list[BDevice] | None = []) -> list[BDevice]: group.set_selected_by_value(presets) group.set_preview_for_all(_preview_device_selection) - result = SelectMenu( + result = SelectMenu[_DeviceInfo]( group, header=header, alignment=Alignment.CENTER, @@ -82,7 +82,7 @@ def select_devices(preset: list[BDevice] | None = []) -> list[BDevice]: case ResultType.Skip: return preset case ResultType.Selection: - selected_device_info: list[_DeviceInfo] = result.get_values() + selected_device_info = result.get_values() selected_devices = [] for device in devices: @@ -140,7 +140,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay if preset: group.set_selected_by_value(preset.config_type.display_msg()) - result = SelectMenu( + result = SelectMenu[str]( group, allow_skip=True, alignment=Alignment.CENTER, @@ -210,7 +210,7 @@ def select_lvm_config( group = MenuItemGroup(items) group.set_focus_by_value(preset_value) - result = SelectMenu( + result = SelectMenu[str]( group, allow_reset=True, allow_skip=True, @@ -261,7 +261,7 @@ def select_main_filesystem_format() -> FilesystemType: items.append(MenuItem('ntfs', value=FilesystemType.Ntfs)) group = MenuItemGroup(items, sort_items=False) - result = SelectMenu( + result = SelectMenu[FilesystemType]( group, alignment=Alignment.CENTER, frame=FrameProperties.min('Filesystem'), @@ -285,7 +285,7 @@ def select_mount_options() -> list[str]: MenuItem(disable_cow, value=BtrfsMountOption.nodatacow.value), ] group = MenuItemGroup(items, sort_items=False) - result = SelectMenu( + result = SelectMenu[str]( group, header=prompt, alignment=Alignment.CENTER, @@ -336,7 +336,7 @@ def suggest_single_disk_layout( prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?')) + '\n' group = MenuItemGroup.yes_no() group.set_focus_by_value(MenuItem.yes().value) - result = SelectMenu( + result = SelectMenu[bool]( group, header=prompt, alignment=Alignment.CENTER, @@ -577,7 +577,7 @@ def suggest_lvm_layout( group = MenuItemGroup.yes_no() group.set_focus_by_value(MenuItem.yes().value) - result = SelectMenu( + result = SelectMenu[bool]( group, header=prompt, search_enabled=False, diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 9dcffa80..6a5d470a 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -42,7 +42,7 @@ def ask_ntp(preset: bool = True) -> bool: group = MenuItemGroup.yes_no() group.focus_item = preset_val - result = SelectMenu( + result = SelectMenu[bool]( group, header=header, allow_skip=True, @@ -61,7 +61,7 @@ def ask_ntp(preset: bool = True) -> bool: def ask_hostname(preset: str | None = None) -> str | None: - result = EditMenu( + result = EditMenu[str]( str(_('Hostname')), alignment=Alignment.CENTER, allow_skip=True, @@ -89,7 +89,7 @@ def ask_for_a_timezone(preset: str | None = None) -> str | None: group.set_selected_by_value(preset) group.set_default_by_value(default) - result = SelectMenu( + result = SelectMenu[str]( group, allow_reset=True, allow_skip=True, @@ -113,7 +113,7 @@ def ask_for_audio_selection(preset: AudioConfiguration | None = None) -> AudioCo if preset: group.set_focus_by_value(preset.audio) - result = SelectMenu( + result = SelectMenu[Audio]( group, allow_skip=True, alignment=Alignment.CENTER, @@ -156,7 +156,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 = SelectMenu( + result = SelectMenu[Language]( group, header=title, allow_skip=True, @@ -215,7 +215,7 @@ def ask_additional_packages_to_install( menu_group = MenuItemGroup(items, sort_items=True) menu_group.set_selected_by_value(preset_packages) - result = SelectMenu( + result = SelectMenu[AvailablePackage | PackageGroup]( menu_group, header=header, alignment=Alignment.LEFT, @@ -233,7 +233,7 @@ def ask_additional_packages_to_install( case ResultType.Reset: return [] case ResultType.Selection: - selected_pacakges: list[AvailablePackage | PackageGroup] = result.get_values() + selected_pacakges = result.get_values() return [pkg.name for pkg in selected_pacakges] @@ -255,7 +255,7 @@ def add_number_of_parallel_downloads(preset: int | None = None) -> int | None: return str(_('Invalid download number')) - result = EditMenu( + result = EditMenu[str]( str(_('Number downloads')), header=header, allow_skip=True, @@ -295,7 +295,7 @@ def ask_post_installation() -> PostInstallationAction: items = [MenuItem(action.value, value=action) for action in PostInstallationAction] group = MenuItemGroup(items) - result = SelectMenu( + result = SelectMenu[PostInstallationAction]( group, header=header, allow_skip=False, @@ -313,7 +313,7 @@ def ask_abort() -> None: prompt = str(_('Do you really want to abort?')) + '\n' group = MenuItemGroup.yes_no() - result = SelectMenu( + result = SelectMenu[bool]( group, header=prompt, allow_skip=False, diff --git a/archinstall/lib/interactions/manage_users_conf.py b/archinstall/lib/interactions/manage_users_conf.py index 1b827510..8d9073d2 100644 --- a/archinstall/lib/interactions/manage_users_conf.py +++ b/archinstall/lib/interactions/manage_users_conf.py @@ -20,7 +20,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class UserList(ListManager): +class UserList(ListManager[User]): def __init__(self, prompt: str, lusers: list[User]): self._actions = [ str(_('Add a user')), @@ -70,7 +70,7 @@ class UserList(ListManager): return str(_("The username you entered is invalid")) def _add_user(self) -> User | None: - editResult = EditMenu( + editResult = EditMenu[str]( str(_('Username')), allow_skip=True, validator=self._check_for_correct_username @@ -97,7 +97,7 @@ class UserList(ListManager): group = MenuItemGroup.yes_no() group.focus_item = MenuItem.yes() - result = SelectMenu( + result = SelectMenu[bool]( group, header=header, alignment=Alignment.CENTER, diff --git a/archinstall/lib/interactions/network_menu.py b/archinstall/lib/interactions/network_menu.py index dd2243eb..10385e7f 100644 --- a/archinstall/lib/interactions/network_menu.py +++ b/archinstall/lib/interactions/network_menu.py @@ -20,7 +20,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class ManualNetworkConfig(ListManager): +class ManualNetworkConfig(ListManager[Nic]): def __init__(self, prompt: str, preset: list[Nic]): self._actions = [ str(_('Add interface')), @@ -70,7 +70,7 @@ class ManualNetworkConfig(ListManager): items = [MenuItem(i, value=i) for i in available] group = MenuItemGroup(items, sort_items=True) - result = SelectMenu( + result = SelectMenu[str]( group, alignment=Alignment.CENTER, frame=FrameProperties.min(str(_('Interfaces'))), @@ -106,7 +106,7 @@ class ManualNetworkConfig(ListManager): except ValueError: return str(_('You need to enter a valid IP in IP-config mode')) - result = EditMenu( + result = EditMenu[str]( title, header=header, validator=validator, @@ -132,7 +132,7 @@ class ManualNetworkConfig(ListManager): group = MenuItemGroup(items, sort_items=True) group.set_default_by_value(default_mode) - result = SelectMenu( + result = SelectMenu[str]( group, header=header, allow_skip=False, @@ -192,7 +192,7 @@ def ask_to_configure_network(preset: NetworkConfiguration | None) -> NetworkConf if preset: group.set_selected_by_value(preset.type) - result = SelectMenu( + result = SelectMenu[NetworkConfiguration]( group, alignment=Alignment.CENTER, frame=FrameProperties.min(str(_('Network configuration'))), diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index e42b3419..65afa279 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -35,7 +35,7 @@ def select_kernel(preset: list[str] = []) -> list[str]: group.set_focus_by_value(default_kernel) group.set_selected_by_value(preset) - result = SelectMenu( + result = SelectMenu[str]( group, allow_skip=True, allow_reset=True, @@ -69,7 +69,7 @@ def ask_for_bootloader(preset: Bootloader | None) -> Bootloader | None: group.set_default_by_value(default) group.set_focus_by_value(preset) - result = SelectMenu( + result = SelectMenu[Bootloader]( group, header=header, alignment=Alignment.CENTER, @@ -92,7 +92,7 @@ def ask_for_uki(preset: bool = True) -> bool: group = MenuItemGroup.yes_no() group.set_focus_by_value(preset) - result = SelectMenu( + result = SelectMenu[bool]( group, header=prompt, columns=2, @@ -136,7 +136,7 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None if SysInfo.has_nvidia_graphics(): header += str(_('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n')) - result = SelectMenu( + result = SelectMenu[GfxDriver]( group, header=header, allow_skip=True, @@ -166,7 +166,7 @@ def ask_for_swap(preset: bool = True) -> bool: group = MenuItemGroup.yes_no() group.set_focus_by_value(default_item) - result = SelectMenu( + result = SelectMenu[bool]( group, header=prompt, columns=2, diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py index 267f61f9..dc3d739f 100644 --- a/archinstall/lib/locale/locale_menu.py +++ b/archinstall/lib/locale/locale_menu.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class LocaleMenu(AbstractSubMenu): +class LocaleMenu(AbstractSubMenu[LocaleConfiguration]): def __init__( self, locale_conf: LocaleConfiguration @@ -85,7 +85,7 @@ def select_locale_lang(preset: str | None = None) -> str | None: group = MenuItemGroup(items, sort_items=True) group.set_focus_by_value(preset) - result = SelectMenu( + result = SelectMenu[str]( group, alignment=Alignment.CENTER, frame=FrameProperties.min(str(_('Locale language'))), @@ -109,7 +109,7 @@ def select_locale_enc(preset: str | None = None) -> str | None: group = MenuItemGroup(items, sort_items=True) group.set_focus_by_value(preset) - result = SelectMenu( + result = SelectMenu[str]( group, alignment=Alignment.CENTER, frame=FrameProperties.min(str(_('Locale encoding'))), @@ -141,7 +141,7 @@ def select_kb_layout(preset: str | None = None) -> str | None: group = MenuItemGroup(items, sort_items=False) group.set_focus_by_value(preset) - result = SelectMenu( + result = SelectMenu[str]( group, alignment=Alignment.CENTER, frame=FrameProperties.min(str(_('Keyboard layout'))), diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index 35699ea1..5433a603 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: CONFIG_KEY = '__config__' -class AbstractMenu: +class AbstractMenu[ValueT]: def __init__( self, item_group: MenuItemGroup, @@ -97,11 +97,11 @@ class AbstractMenu: def _is_config_valid(self) -> bool: return True - def run(self) -> Any | None: + def run(self) -> ValueT | None: self._sync_from_config() while True: - result = SelectMenu( + result = SelectMenu[ValueT]( self._menu_item_group, allow_skip=False, allow_reset=self._allow_reset, @@ -126,7 +126,7 @@ class AbstractMenu: return None -class AbstractSubMenu(AbstractMenu): +class AbstractSubMenu[ValueT](AbstractMenu[ValueT]): def __init__( self, item_group: MenuItemGroup, diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index f6fd172f..96bd4a21 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -1,5 +1,5 @@ import copy -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, cast from archinstall.tui.curses_menu import SelectMenu from archinstall.tui.menu_item import MenuItem, MenuItemGroup @@ -16,10 +16,10 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class ListManager: +class ListManager[ValueT]: def __init__( self, - entries: list[Any], + entries: list[ValueT], base_actions: list[str], sub_menu_actions: list[str], prompt: str | None = None @@ -51,10 +51,10 @@ class ListManager: self._base_actions = base_actions self._sub_menu_actions = sub_menu_actions - self._last_choice: str | None = None + self._last_choice: ValueT | str | None = None @property - def last_choice(self) -> str | None: + def last_choice(self) -> ValueT | str | None: return self._last_choice def is_last_choice_cancel(self) -> bool: @@ -62,7 +62,7 @@ class ListManager: return self._last_choice == self._cancel_action return False - def run(self) -> list[Any]: + def run(self) -> list[ValueT]: while True: # this will return a dictionary with the key as the menu entry to be displayed # and the value is the original value from the self._data container @@ -77,7 +77,7 @@ class ListManager: items = [MenuItem(o[0], value=o[1]) for o in options] group = MenuItemGroup(items, sort_items=False) - result = SelectMenu( + result = SelectMenu[ValueT | str]( group, header=header, search_enabled=False, @@ -92,11 +92,14 @@ class ListManager: raise ValueError('Unhandled return type') if value in self._base_actions: + value = cast(str, value) self._data = 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) self._last_choice = value @@ -111,7 +114,7 @@ class ListManager: header = '\n'.join(table_header) return header - def _prepare_selection(self, data_formatted: dict[str, Any]) -> list[tuple[str, Any]]: + def _prepare_selection(self, data_formatted: dict[str, Any]) -> list[tuple[str, str | ValueT]]: # header rows are mapped to None so make sure # to exclude those from the selectable data options = [(key, val) for key, val in data_formatted.items() if val is not None] @@ -125,7 +128,7 @@ class ListManager: return options - def _run_actions_on_entry(self, entry: Any) -> None: + 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] @@ -133,7 +136,7 @@ class ListManager: header = f'{self.selected_action_display(entry)}\n' - result = SelectMenu( + result = SelectMenu[str]( group, header=header, search_enabled=False, @@ -171,21 +174,21 @@ class ListManager: return display_data - def selected_action_display(self, selection: Any) -> str: + def selected_action_display(self, selection: ValueT) -> str: """ this will return the value to be displayed in the "Select an action for '{}'" string """ raise NotImplementedError('Please implement me in the child class') - def handle_action(self, action: Any, entry: Any | None, data: list[Any]) -> list[Any]: + 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 """ raise NotImplementedError('Please implement me in the child class') - def filter_options(self, selection: Any, options: list[str]) -> list[str]: + def filter_options(self, selection: ValueT, options: list[str]) -> list[str]: """ filter which actions to show for an specific selection """ diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 35185681..3fd2f106 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -32,7 +32,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class CustomMirrorRepositoriesList(ListManager): +class CustomMirrorRepositoriesList(ListManager[CustomRepository]): def __init__(self, custom_repositories: list[CustomRepository]): self._actions = [ str(_('Add a custom repository')), @@ -74,7 +74,7 @@ class CustomMirrorRepositoriesList(ListManager): return data def _add_custom_repository(self, preset: CustomRepository | None = None) -> CustomRepository | None: - edit_result = EditMenu( + edit_result = EditMenu[str]( str(_('Repository name')), alignment=Alignment.CENTER, allow_skip=True, @@ -91,7 +91,7 @@ class CustomMirrorRepositoriesList(ListManager): header = f'{_("Name")}: {name}' - edit_result = EditMenu( + edit_result = EditMenu[str]( str(_('Url')), header=header, alignment=Alignment.CENTER, @@ -116,7 +116,7 @@ class CustomMirrorRepositoriesList(ListManager): if preset is not None: group.set_selected_by_value(preset.sign_check.value) - result = SelectMenu( + result = SelectMenu[SignCheck]( group, header=prompt, alignment=Alignment.CENTER, @@ -154,7 +154,7 @@ class CustomMirrorRepositoriesList(ListManager): return CustomRepository(name, url, sign_check, sign_opt) -class CustomMirrorServersList(ListManager): +class CustomMirrorServersList(ListManager[CustomServer]): def __init__(self, custom_servers: list[CustomServer]): self._actions = [ str(_('Add a custom server')), @@ -196,7 +196,7 @@ class CustomMirrorServersList(ListManager): return data def _add_custom_server(self, preset: CustomServer | None = None) -> CustomServer | None: - edit_result = EditMenu( + edit_result = EditMenu[str]( str(_('Server url')), alignment=Alignment.CENTER, allow_skip=True, @@ -213,7 +213,7 @@ class CustomMirrorServersList(ListManager): return None -class MirrorMenu(AbstractSubMenu): +class MirrorMenu(AbstractSubMenu[MirrorConfiguration]): def __init__( self, preset: MirrorConfiguration | None = None @@ -265,7 +265,7 @@ class MirrorMenu(AbstractSubMenu): ] def _prev_regions(self, item: MenuItem) -> str | None: - regions: list[MirrorRegion] = item.get_value() + regions = item.get_value() output = '' for region in regions: @@ -323,7 +323,7 @@ def select_mirror_regions(preset: list[MirrorRegion]) -> list[MirrorRegion]: group.set_selected_by_value(preset_regions) - result = SelectMenu( + result = SelectMenu[MirrorRegion]( group, alignment=Alignment.CENTER, frame=FrameProperties.min(str(_('Mirror regions'))), @@ -338,7 +338,7 @@ def select_mirror_regions(preset: list[MirrorRegion]) -> list[MirrorRegion]: case ResultType.Reset: return [] case ResultType.Selection: - selected_mirrors: list[MirrorRegion] = result.get_values() + selected_mirrors = result.get_values() return selected_mirrors @@ -365,7 +365,7 @@ def select_optional_repositories(preset: list[Repository]) -> list[Repository]: group = MenuItemGroup(items, sort_items=True) group.set_selected_by_value(preset) - result = SelectMenu( + result = SelectMenu[Repository]( group, alignment=Alignment.CENTER, frame=FrameProperties.min('Additional repositories'), diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py index 8533c751..b5e09ef7 100644 --- a/archinstall/lib/profile/profile_menu.py +++ b/archinstall/lib/profile/profile_menu.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class ProfileMenu(AbstractSubMenu): +class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): def __init__( self, preset: ProfileConfiguration | None = None @@ -114,7 +114,7 @@ class ProfileMenu(AbstractSubMenu): group.focus_item = MenuItem.no() group.default_item = MenuItem.no() - result = SelectMenu( + result = SelectMenu[bool]( group, header=header, allow_skip=False, @@ -175,7 +175,7 @@ def select_greeter( group.set_default_by_value(default) - result = SelectMenu( + result = SelectMenu[GreeterType]( group, allow_skip=True, frame=FrameProperties.min(str(_('Greeter'))), @@ -208,7 +208,7 @@ def select_profile( group = MenuItemGroup(items, sort_items=True) group.set_selected_by_value(current_profile) - result = SelectMenu( + result = SelectMenu[Profile]( group, header=header, allow_reset=allow_reset, @@ -223,7 +223,7 @@ def select_profile( case ResultType.Skip: return current_profile case ResultType.Selection: - profile_selection: Profile = result.get_value() + profile_selection = result.get_value() select_result = profile_selection.do_on_select() if not select_result: diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py index 9a6f5a86..54825476 100644 --- a/archinstall/lib/utils/util.py +++ b/archinstall/lib/utils/util.py @@ -31,7 +31,7 @@ def get_password( elif header is not None: user_hdr = header - result = EditMenu( + result = EditMenu[str]( text, header=user_hdr, alignment=Alignment.CENTER, @@ -53,7 +53,7 @@ def get_password( else: confirmation_header = f'{_("Password")}: {password.hidden()}\n' - result = EditMenu( + result = EditMenu[str]( str(_('Confirm password')), header=confirmation_header, alignment=Alignment.CENTER, @@ -87,7 +87,7 @@ def prompt_dir( else: validate_func = None - result = EditMenu( + result = EditMenu[str]( text, header=header, alignment=Alignment.CENTER, diff --git a/archinstall/tui/curses_menu.py b/archinstall/tui/curses_menu.py index 207ab44d..e8c255b3 100644 --- a/archinstall/tui/curses_menu.py +++ b/archinstall/tui/curses_menu.py @@ -35,7 +35,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -class AbstractCurses(metaclass=ABCMeta): +class AbstractCurses[ValueT](metaclass=ABCMeta): def __init__(self) -> None: self._help_window = self._set_help_viewport() @@ -44,7 +44,7 @@ class AbstractCurses(metaclass=ABCMeta): pass @abstractmethod - def kickoff(self, win: curses.window) -> Result: + def kickoff(self, win: curses.window) -> Result[ValueT]: pass def clear_all(self) -> None: @@ -71,7 +71,7 @@ class AbstractCurses(metaclass=ABCMeta): def _confirm_interrupt(self, warning: str) -> bool: while True: - result = SelectMenu( + result = SelectMenu[bool]( MenuItemGroup.yes_no(), header=warning, alignment=Alignment.CENTER, @@ -461,7 +461,7 @@ class Viewport(AbstractViewport): self._main_win.refresh() -class EditMenu(AbstractCurses): +class EditMenu[ValueT](AbstractCurses[ValueT]): def __init__( self, title: str, @@ -506,7 +506,7 @@ class EditMenu(AbstractCurses): self._init_viewports() - self._last_state: Result | None = None + self._last_state: Result[ValueT] | None = None self._help_active = False self._real_input = default_text or "" @@ -536,7 +536,7 @@ class EditMenu(AbstractCurses): y_offset += 3 self._info_vp = Viewport(self._max_width, 1, 0, y_offset, alignment=self._alignment) - def input(self) -> Result: + def input(self) -> Result[ValueT]: result = Tui.run(self) assert not result.has_item() or isinstance(result.text(), str) @@ -593,7 +593,7 @@ class EditMenu(AbstractCurses): self._input_vp.edit(default_text=self._default_text) @override - def kickoff(self, win: curses.window) -> Result: + def kickoff(self, win: curses.window) -> Result[ValueT]: try: self._draw() except KeyboardInterrupt: @@ -680,7 +680,7 @@ class EditMenu(AbstractCurses): return True -class SelectMenu(AbstractCurses): +class SelectMenu[ValueT](AbstractCurses[ValueT]): def __init__( self, group: MenuItemGroup, @@ -769,13 +769,13 @@ class SelectMenu(AbstractCurses): return offset - def run(self) -> Result: + def run(self) -> Result[ValueT]: result = Tui.run(self) self._clear_all() return result @override - def kickoff(self, win: curses.window) -> Result: + def kickoff(self, win: curses.window) -> Result[ValueT]: self._draw() while True: @@ -1137,7 +1137,7 @@ class SelectMenu(AbstractCurses): else: return False - def _process_input_key(self, key: int) -> Result | None: + def _process_input_key(self, key: int) -> Result[ValueT] | None: key_handles = MenuKeys.from_ord(key) if self._help_active: @@ -1339,7 +1339,7 @@ class Tui: return self._screen.getmaxyx() @staticmethod - def run(component: AbstractCurses) -> Result: + def run[ValueT](component: AbstractCurses[ValueT]) -> Result[ValueT]: if Tui._t is None: tui = Tui().init() tui.screen.clear() @@ -1355,7 +1355,7 @@ class Tui: if hasattr(self, '_component') and self._component is not None: # pylint: disable=no-member self._component.resize_win() # pylint: disable=no-member - def _main_loop(self, component: AbstractCurses) -> Result: + def _main_loop[ValueT](self, component: AbstractCurses[ValueT]) -> Result[ValueT]: self._screen.refresh() return component.kickoff(self._screen) diff --git a/archinstall/tui/result.py b/archinstall/tui/result.py index 4307efb6..8ccf09d8 100644 --- a/archinstall/tui/result.py +++ b/archinstall/tui/result.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from enum import Enum, auto -from typing import Any from .menu_item import MenuItem @@ -12,17 +11,17 @@ class ResultType(Enum): @dataclass -class Result: +class Result[ValueT]: type_: ResultType _item: MenuItem | list[MenuItem] | str | None def has_item(self) -> bool: return self._item is not None - def get_value(self) -> Any: - return self.item().get_value() + def get_value(self) -> ValueT: + return self.item().get_value() # type: ignore[no-any-return] - def get_values(self) -> list[Any]: + def get_values(self) -> list[ValueT]: return [i.get_value() for i in self.items()] def item(self) -> MenuItem: