Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Girtler <blackrabbit256@gmail.com>2022-05-18 21:59:49 +1000
committerGitHub <noreply@github.com>2022-05-18 13:59:49 +0200
commit65a5a335aa21ea44fd99fb200e238df54b3c2e47 (patch)
tree5b7a1d3937fc283c91734b7ab4f06e376bb59fdb
parent089c46db4a3c89dd8ba670419369c405bec3a270 (diff)
Enhance view (#1210)
* Add preview for menu entries * Fix mypy * Update * Update * Fix mypy Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com>
-rw-r--r--.github/workflows/mypy.yaml2
-rw-r--r--archinstall/lib/disk/blockdevice.py42
-rw-r--r--archinstall/lib/hsm/fido.py2
-rw-r--r--archinstall/lib/menu/global_menu.py66
-rw-r--r--archinstall/lib/menu/list_manager.py4
-rw-r--r--archinstall/lib/menu/selection_menu.py8
-rw-r--r--archinstall/lib/user_interaction/general_conf.py1
-rw-r--r--archinstall/lib/user_interaction/network_conf.py12
-rw-r--r--archinstall/lib/user_interaction/partitioning_conf.py19
-rw-r--r--archinstall/lib/user_interaction/utils.py1
10 files changed, 110 insertions, 47 deletions
diff --git a/.github/workflows/mypy.yaml b/.github/workflows/mypy.yaml
index 6fd0876f..18c33e67 100644
--- a/.github/workflows/mypy.yaml
+++ b/.github/workflows/mypy.yaml
@@ -15,4 +15,4 @@ jobs:
# one day this will be enabled
# run: mypy --strict --module archinstall || exit 0
- name: run mypy
- run: mypy --follow-imports=skip archinstall/lib/menu/selection_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py
+ run: mypy --follow-imports=silent archinstall/lib/menu/selection_menu.py archinstall/lib/menu/global_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py
diff --git a/archinstall/lib/disk/blockdevice.py b/archinstall/lib/disk/blockdevice.py
index 995ca355..15f03789 100644
--- a/archinstall/lib/disk/blockdevice.py
+++ b/archinstall/lib/disk/blockdevice.py
@@ -34,11 +34,29 @@ class BlockDevice:
def __repr__(self, *args :str, **kwargs :str) -> str:
return self._str_repr
-
+
@cached_property
def _str_repr(self) -> str:
return f"BlockDevice({self.device_or_backfile}, size={self._safe_size}GB, free_space={self._safe_free_space}, bus_type={self.bus_type})"
+ @cached_property
+ def display_info(self) -> str:
+ columns = {
+ str(_('Device')): self.device_or_backfile,
+ str(_('Size')): f'{self._safe_size}GB',
+ str(_('Free space')): f'{self._safe_free_space}',
+ str(_('Bus-type')): f'{self.bus_type}'
+ }
+
+ padding = max([len(k) for k in columns.keys()])
+
+ pretty = ''
+ for k, v in columns.items():
+ k = k.ljust(padding, ' ')
+ pretty += f'{k} = {v}\n'
+
+ return pretty.rstrip()
+
def __iter__(self) -> Iterator[Partition]:
for partition in self.partitions:
yield self.partitions[partition]
@@ -79,7 +97,7 @@ class BlockDevice:
for device in output['blockdevices']:
return device['pttype']
- @property
+ @cached_property
def device_or_backfile(self) -> str:
"""
Returns the actual device-endpoint of the BlockDevice.
@@ -162,7 +180,7 @@ class BlockDevice:
from .filesystem import GPT
return GPT
- @property
+ @cached_property
def uuid(self) -> str:
log('BlockDevice().uuid is untested!', level=logging.WARNING, fg='yellow')
"""
@@ -172,7 +190,7 @@ class BlockDevice:
"""
return SysCommand(f'blkid -s PTUUID -o value {self.path}').decode('UTF-8')
- @property
+ @cached_property
def _safe_size(self) -> float:
from .helpers import convert_size_to_gb
@@ -184,7 +202,7 @@ class BlockDevice:
for device in output['blockdevices']:
return convert_size_to_gb(device['size'])
- @property
+ @cached_property
def size(self) -> float:
from .helpers import convert_size_to_gb
@@ -193,28 +211,28 @@ class BlockDevice:
for device in output['blockdevices']:
return convert_size_to_gb(device['size'])
- @property
+ @cached_property
def bus_type(self) -> str:
output = json.loads(SysCommand(f"lsblk --json -o+ROTA,TRAN {self.path}").decode('UTF-8'))
for device in output['blockdevices']:
return device['tran']
- @property
+ @cached_property
def spinning(self) -> bool:
output = json.loads(SysCommand(f"lsblk --json -o+ROTA,TRAN {self.path}").decode('UTF-8'))
for device in output['blockdevices']:
return device['rota'] is True
- @property
+ @cached_property
def _safe_free_space(self) -> Tuple[str, ...]:
try:
return '+'.join(part[2] for part in self.free_space)
except SysCallError:
return '?'
- @property
+ @cached_property
def free_space(self) -> Tuple[str, ...]:
# NOTE: parted -s will default to `cancel` on prompt, skipping any partition
# that is "outside" the disk. in /dev/sr0 this is usually the case with Archiso,
@@ -228,7 +246,7 @@ class BlockDevice:
except SysCallError as error:
log(f"Could not get free space on {self.path}: {error}", level=logging.DEBUG)
- @property
+ @cached_property
def largest_free_space(self) -> List[str]:
info = []
for space_info in self.free_space:
@@ -240,7 +258,7 @@ class BlockDevice:
info = space_info
return info
- @property
+ @cached_property
def first_free_sector(self) -> str:
if info := self.largest_free_space:
start = info[0]
@@ -248,7 +266,7 @@ class BlockDevice:
start = '512MB'
return start
- @property
+ @cached_property
def first_end_sector(self) -> str:
if info := self.largest_free_space:
end = info[1]
diff --git a/archinstall/lib/hsm/fido.py b/archinstall/lib/hsm/fido.py
index 8707ac52..49f36957 100644
--- a/archinstall/lib/hsm/fido.py
+++ b/archinstall/lib/hsm/fido.py
@@ -40,7 +40,7 @@ def get_fido2_devices() -> typing.Dict[str, typing.Dict[str, str]]:
}
return devices
-
+
def fido2_enroll(hsm_device_path :pathlib.Path, partition :Partition, password :str) -> bool:
worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device_path} {partition.real_device}", peak_output=True)
pw_inputted = False
diff --git a/archinstall/lib/menu/global_menu.py b/archinstall/lib/menu/global_menu.py
index d807433c..53e0941d 100644
--- a/archinstall/lib/menu/global_menu.py
+++ b/archinstall/lib/menu/global_menu.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import Any, List, Optional, Union
+from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING
import archinstall
@@ -33,10 +33,15 @@ from ..user_interaction import select_encrypted_partitions
from ..user_interaction import select_harddrives
from ..user_interaction import select_profile
from ..user_interaction import select_additional_repositories
+from ..user_interaction.partitioning_conf import current_partition_layout
+
+if TYPE_CHECKING:
+ _: Any
+
class GlobalMenu(GeneralMenu):
def __init__(self,data_store):
- super().__init__(data_store=data_store, auto_cursor=True)
+ super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3)
def _setup_selection_menu_options(self):
# archinstall.Language will not use preset values
@@ -69,7 +74,10 @@ class GlobalMenu(GeneralMenu):
self._menu_options['harddrives'] = \
Selector(
_('Drive(s)'),
- lambda preset: self._select_harddrives(preset))
+ lambda preset: self._select_harddrives(preset),
+ display_func=lambda x: f'{len(x)} ' + str(_('Drive(s)')) if x is not None and len(x) > 0 else '',
+ preview_func=self._prev_harddrives,
+ )
self._menu_options['disk_layouts'] = \
Selector(
_('Disk layout'),
@@ -78,6 +86,8 @@ class GlobalMenu(GeneralMenu):
storage['arguments'].get('harddrives', []),
storage['arguments'].get('advanced', False)
),
+ preview_func=self._prev_disk_layouts,
+ display_func=lambda x: self._display_disk_layout(x),
dependencies=['harddrives'])
self._menu_options['!encryption-password'] = \
Selector(
@@ -131,7 +141,8 @@ class GlobalMenu(GeneralMenu):
Selector(
_('Profile'),
lambda preset: self._select_profile(preset),
- display_func=lambda x: x if x else 'None')
+ display_func=lambda x: x if x else 'None'
+ )
self._menu_options['audio'] = \
Selector(
_('Audio'),
@@ -189,7 +200,7 @@ class GlobalMenu(GeneralMenu):
def _update_install_text(self, name :str = None, result :Any = None):
text = self._install_text()
- self._menu_options.get('install').update_description(text)
+ self._menu_options['install'].update_description(text)
def post_callback(self,name :str = None ,result :Any = None):
self._update_install_text(name, result)
@@ -225,6 +236,35 @@ class GlobalMenu(GeneralMenu):
else:
return str(cur_value)
+ def _prev_harddrives(self) -> Optional[str]:
+ selector = self._menu_options['harddrives']
+ if selector.has_selection():
+ drives = selector.current_selection
+ return '\n\n'.join([d.display_info for d in drives])
+ return None
+
+ def _prev_disk_layouts(self) -> Optional[str]:
+ selector = self._menu_options['disk_layouts']
+ if selector.has_selection():
+ layouts: Dict[str, Dict[str, Any]] = selector.current_selection
+
+ output = ''
+ for device, layout in layouts.items():
+ output += f'{_("Device")}: {device}\n\n'
+ output += current_partition_layout(layout['partitions'], with_title=False)
+ output += '\n\n'
+
+ return output.rstrip()
+
+ return None
+
+ def _display_disk_layout(self, current_value: Optional[Dict[str, Any]]) -> str:
+ if current_value:
+ total_partitions = [entry['partitions'] for entry in current_value.values()]
+ total_nr = sum([len(p) for p in total_partitions])
+ return f'{total_nr} {_("Partitions")}'
+ return ''
+
def _prev_install_missing_config(self) -> Optional[str]:
if missing := self._missing_configs():
text = str(_('Missing configurations:\n'))
@@ -247,17 +287,17 @@ class GlobalMenu(GeneralMenu):
if not check('harddrives'):
missing += ['Hard drives']
if check('harddrives'):
- if not self._menu_options.get('harddrives').is_empty() and not check('disk_layouts'):
+ if not self._menu_options['harddrives'].is_empty() and not check('disk_layouts'):
missing += ['Disk layout']
return missing
- def _set_root_password(self):
+ def _set_root_password(self) -> Optional[str]:
prompt = str(_('Enter root password (leave blank to disable root): '))
password = get_password(prompt=prompt)
return password
- def _select_encrypted_password(self):
+ def _select_encrypted_password(self) -> Optional[str]:
if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
return passwd
else:
@@ -271,7 +311,7 @@ class GlobalMenu(GeneralMenu):
return ntp
- def _select_harddrives(self, old_harddrives : list) -> list:
+ def _select_harddrives(self, old_harddrives : list) -> List:
harddrives = select_harddrives(old_harddrives)
if len(harddrives) == 0:
@@ -288,7 +328,7 @@ class GlobalMenu(GeneralMenu):
# in case the harddrives got changed we have to reset the disk layout as well
if old_harddrives != harddrives:
- self._menu_options.get('disk_layouts').set_current_selection(None)
+ self._menu_options['disk_layouts'].set_current_selection(None)
storage['arguments']['disk_layouts'] = {}
return harddrives
@@ -340,11 +380,11 @@ class GlobalMenu(GeneralMenu):
return ret
- def _create_superuser_account(self):
+ def _create_superuser_account(self) -> Optional[Dict[str, Dict[str, str]]]:
superusers = ask_for_superuser_account(str(_('Manage superuser accounts: ')))
return superusers if superusers else None
- def _create_user_account(self):
+ def _create_user_account(self) -> Dict[str, Dict[str, str | None]]:
users = ask_for_additional_users(str(_('Manage ordinary user accounts: ')))
return users
@@ -356,7 +396,7 @@ class GlobalMenu(GeneralMenu):
else:
return list(superusers.keys()) if superusers else ''
- def _users_resynch(self):
+ def _users_resynch(self) -> bool:
self.synch('!superusers')
self.synch('!users')
return False
diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py
index 9faa1c77..7db3b3a9 100644
--- a/archinstall/lib/menu/list_manager.py
+++ b/archinstall/lib/menu/list_manager.py
@@ -89,7 +89,7 @@ from .text_input import TextInput
from .menu import Menu, MenuSelectionType
from os import system
from copy import copy
-from typing import Union, Any, TYPE_CHECKING, Dict
+from typing import Union, Any, TYPE_CHECKING, Dict, Optional
if TYPE_CHECKING:
_: Any
@@ -147,7 +147,7 @@ class ListManager:
self.base_data = base_list
self._data = copy(base_list) # as refs, changes are immediate
# default values for the null case
- self.target = None
+ self.target: Optional[Any] = None
self.action = self._null_action
if len(self._data) == 0 and self._null_action:
diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py
index 26be4cc7..57e290f1 100644
--- a/archinstall/lib/menu/selection_menu.py
+++ b/archinstall/lib/menu/selection_menu.py
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
_: Any
-def select_archinstall_language(preset_value: str) -> Optional[str]:
+def select_archinstall_language(preset_value: str) -> Optional[Any]:
"""
copied from user_interaction/general_conf.py as a temporary measure
"""
@@ -487,6 +487,8 @@ class GeneralMenu:
match choice.type_:
case MenuSelectionType.Esc: return preset
case MenuSelectionType.Selection:
- return pathlib.Path(list(fido_devices.keys())[int(choice.value.split('|',1)[0])])
+ selection: Any = choice.value
+ index = int(selection.split('|',1)[0])
+ return pathlib.Path(list(fido_devices.keys())[index])
- return None \ No newline at end of file
+ return None
diff --git a/archinstall/lib/user_interaction/general_conf.py b/archinstall/lib/user_interaction/general_conf.py
index c3a2a7a7..d4dc60db 100644
--- a/archinstall/lib/user_interaction/general_conf.py
+++ b/archinstall/lib/user_interaction/general_conf.py
@@ -142,7 +142,6 @@ def select_profile(preset) -> Optional[Profile]:
options[option] = profile
title = _('This is a list of pre-programmed profiles, they might make it easier to install things like desktop environments')
-
warning = str(_('Are you sure you want to reset this setting?'))
selection = Menu(
diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py
index e4e681ce..25e9d4c6 100644
--- a/archinstall/lib/user_interaction/network_conf.py
+++ b/archinstall/lib/user_interaction/network_conf.py
@@ -64,7 +64,7 @@ class ManualNetworkConfig(ListManager):
elif self.action == self._action_delete:
del data[iface_name]
- def _select_iface(self, existing_ifaces: List[str]) -> Optional[str]:
+ def _select_iface(self, existing_ifaces: List[str]) -> Optional[Any]:
all_ifaces = list_interfaces().values()
available = set(all_ifaces) - set(existing_ifaces)
choice = Menu(str(_('Select interface to add')), list(available), skip=True).run()
@@ -94,14 +94,14 @@ class ManualNetworkConfig(ListManager):
log("You need to enter a valid IP in IP-config mode.", level=logging.WARNING, fg='red')
# Implemented new check for correct gateway IP address
+ gateway = None
+
while 1:
- gateway = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),
+ gateway_input = TextInput(_('Enter your gateway (router) IP address or leave blank for none: '),
edit_iface.gateway).run().strip()
try:
- if len(gateway) == 0:
- gateway = None
- else:
- ipaddress.ip_address(gateway)
+ if len(gateway_input) > 0:
+ ipaddress.ip_address(gateway_input)
break
except ValueError:
log("You need to enter a valid gateway (router) IP address.", level=logging.WARNING, fg='red')
diff --git a/archinstall/lib/user_interaction/partitioning_conf.py b/archinstall/lib/user_interaction/partitioning_conf.py
index 741decc1..bfff5705 100644
--- a/archinstall/lib/user_interaction/partitioning_conf.py
+++ b/archinstall/lib/user_interaction/partitioning_conf.py
@@ -20,9 +20,9 @@ def partition_overlap(partitions: list, start: str, end: str) -> bool:
return False
-def _current_partition_layout(partitions: List[Partition], with_idx: bool = False) -> str:
+def current_partition_layout(partitions: List[Dict[str, Any]], with_idx: bool = False, with_title: bool = True) -> str:
- def do_padding(name, max_len):
+ def do_padding(name: str, max_len: int):
spaces = abs(len(str(name)) - max_len) + 2
pad_left = int(spaces / 2)
pad_right = spaces - pad_left
@@ -62,8 +62,11 @@ def _current_partition_layout(partitions: List[Partition], with_idx: bool = Fals
current_layout += f'{row[:-1]}\n'
- title = str(_('Current partition layout'))
- return f'\n\n{title}:\n\n{current_layout}'
+ if with_title:
+ title = str(_('Current partition layout'))
+ return f'\n\n{title}:\n\n{current_layout}'
+
+ return current_layout
def _get_partitions(partitions :List[Partition], filter_ :Callable = None) -> List[str]:
@@ -173,7 +176,7 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
# show current partition layout:
if len(block_device_struct["partitions"]):
- title += _current_partition_layout(block_device_struct['partitions']) + '\n'
+ title += current_partition_layout(block_device_struct['partitions']) + '\n'
modes += [save_and_exit, cancel]
@@ -246,7 +249,7 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
block_device_struct.update(suggest_single_disk_layout(block_device)[block_device.path])
else:
- current_layout = _current_partition_layout(block_device_struct['partitions'], with_idx=True)
+ current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True)
if task == delete_partition:
title = _('{}\n\nSelect by index which partitions to delete').format(current_layout)
@@ -375,7 +378,7 @@ def select_encrypted_partitions(
# show current partition layout:
if len(partitions):
- title += _current_partition_layout(partitions) + '\n'
+ title += current_partition_layout(partitions) + '\n'
choice = Menu(title, partition_indexes, multi=multiple).run()
@@ -386,4 +389,4 @@ def select_encrypted_partitions(
for partition_index in choice.value:
yield int(partition_index)
else:
- yield (partition_index) \ No newline at end of file
+ yield (partition_index)
diff --git a/archinstall/lib/user_interaction/utils.py b/archinstall/lib/user_interaction/utils.py
index ce48607d..fa079bc2 100644
--- a/archinstall/lib/user_interaction/utils.py
+++ b/archinstall/lib/user_interaction/utils.py
@@ -52,6 +52,7 @@ def get_password(prompt: str = '') -> Optional[str]:
continue
return passwd
+
return None