Refactor logging and formatting
This commit is contained in:
parent
6ea2de87d3
commit
5fbdbffcc4
|
|
@ -6,9 +6,9 @@ from archinstall.lib.menu.helpers import Confirmation, Selection
|
|||
from archinstall.lib.menu.util import get_password
|
||||
from archinstall.lib.models.authentication import AuthenticationConfiguration, U2FLoginConfiguration, U2FLoginMethod
|
||||
from archinstall.lib.models.users import Password, User
|
||||
from archinstall.lib.output import FormattedOutput
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.lib.user.user_menu import select_users
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.result import ResultType
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]):
|
|||
users: list[User] | None = item.value
|
||||
|
||||
if users:
|
||||
return FormattedOutput.as_table(users)
|
||||
return as_table(users)
|
||||
return None
|
||||
|
||||
def _prev_root_pwd(self, item: MenuItem) -> str | None:
|
||||
|
|
|
|||
|
|
@ -35,8 +35,9 @@ from archinstall.lib.models.device import (
|
|||
Unit,
|
||||
_DeviceInfo,
|
||||
)
|
||||
from archinstall.lib.output import FormattedOutput, debug
|
||||
from archinstall.lib.output import debug
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.result import ResultType
|
||||
|
||||
|
|
@ -221,7 +222,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
|
||||
for mod in device_mods:
|
||||
# create partition table
|
||||
partition_table = FormattedOutput.as_table(mod.partitions)
|
||||
partition_table = as_table(mod.partitions)
|
||||
|
||||
output_partition += f'{mod.device_path}: {mod.device.device_info.model}\n'
|
||||
output_partition += '{}: {}\n'.format(tr('Wipe'), mod.wipe)
|
||||
|
|
@ -230,7 +231,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
# create btrfs table
|
||||
btrfs_partitions = [p for p in mod.partitions if p.btrfs_subvols]
|
||||
for partition in btrfs_partitions:
|
||||
output_btrfs += FormattedOutput.as_table(partition.btrfs_subvols) + '\n'
|
||||
output_btrfs += as_table(partition.btrfs_subvols) + '\n'
|
||||
|
||||
output = output_partition + output_btrfs
|
||||
return output.rstrip()
|
||||
|
|
@ -246,12 +247,12 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskMenuConfig]):
|
|||
output = '{}: {}\n'.format(tr('Configuration'), lvm_config.config_type.display_msg())
|
||||
|
||||
for vol_gp in lvm_config.vol_groups:
|
||||
pv_table = FormattedOutput.as_table(vol_gp.pvs)
|
||||
pv_table = as_table(vol_gp.pvs)
|
||||
output += '{}:\n{}'.format(tr('Physical volumes'), pv_table)
|
||||
|
||||
output += f'\nVolume Group: {vol_gp.name}'
|
||||
|
||||
lvm_volumes = FormattedOutput.as_table(vol_gp.volumes)
|
||||
lvm_volumes = as_table(vol_gp.volumes)
|
||||
output += '\n\n{}:\n{}'.format(tr('Volumes'), lvm_volumes)
|
||||
|
||||
return output
|
||||
|
|
@ -302,7 +303,7 @@ async def select_devices(preset: list[BDevice] | None = []) -> list[BDevice] | N
|
|||
dev = device_handler.get_device(device.path)
|
||||
|
||||
if dev and dev.partition_infos:
|
||||
return FormattedOutput.as_table(dev.partition_infos)
|
||||
return as_table(dev.partition_infos)
|
||||
return None
|
||||
|
||||
if preset is None:
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ from archinstall.lib.models.device import (
|
|||
PartitionModification,
|
||||
)
|
||||
from archinstall.lib.models.users import Password
|
||||
from archinstall.lib.output import FormattedOutput
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.result import ResultType
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
|||
def _prev_partitions(self, item: MenuItem) -> str | None:
|
||||
if item.value:
|
||||
output = tr('Partitions to be encrypted') + '\n'
|
||||
output += FormattedOutput.as_table(item.value)
|
||||
output += as_table(item.value)
|
||||
return output.rstrip()
|
||||
|
||||
return None
|
||||
|
|
@ -207,7 +207,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]):
|
|||
def _prev_lvm_vols(self, item: MenuItem) -> str | None:
|
||||
if item.value:
|
||||
output = tr('LVM volumes to be encrypted') + '\n'
|
||||
output += FormattedOutput.as_table(item.value)
|
||||
output += as_table(item.value)
|
||||
return output.rstrip()
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ from archinstall.lib.models.device import (
|
|||
Size,
|
||||
Unit,
|
||||
)
|
||||
from archinstall.lib.output import FormattedOutput
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.result import ResultType
|
||||
|
||||
|
|
@ -479,7 +479,7 @@ class PartitioningList(ListManager[DiskSegment]):
|
|||
sector_size = device_info.sector_size
|
||||
|
||||
text = tr('Selected free space segment on device {}:').format(device_info.path) + '\n\n'
|
||||
free_space_table = FormattedOutput.as_table([free_space])
|
||||
free_space_table = as_table([free_space])
|
||||
prompt = text + free_space_table + '\n'
|
||||
|
||||
max_sectors = free_space.length.format_size(Unit.sectors, sector_size)
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ from archinstall.lib.models.packages import Repository
|
|||
from archinstall.lib.models.pacman import PacmanConfiguration
|
||||
from archinstall.lib.models.profile import ProfileConfiguration
|
||||
from archinstall.lib.network.network_menu import select_network
|
||||
from archinstall.lib.output import FormattedOutput
|
||||
from archinstall.lib.packages.packages import list_available_packages, select_additional_packages
|
||||
from archinstall.lib.pacman.config import PacmanConfig
|
||||
from archinstall.lib.pacman.pacman_menu import PacmanMenu
|
||||
from archinstall.lib.translationhandler import Language, tr, translation_handler
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.components import tui
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
|
||||
|
|
@ -299,7 +299,7 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
if item.value:
|
||||
network_config: NetworkConfiguration = item.value
|
||||
if network_config.type == NicType.MANUAL:
|
||||
output = FormattedOutput.as_table(network_config.nics)
|
||||
output = as_table(network_config.nics)
|
||||
else:
|
||||
output = f'{tr("Network configuration")}:\n{network_config.type.display_msg()}'
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
output += f'{tr("Root password")}: {auth_config.root_enc_password.hidden()}\n'
|
||||
|
||||
if auth_config.users:
|
||||
output += FormattedOutput.as_table(auth_config.users) + '\n'
|
||||
output += as_table(auth_config.users) + '\n'
|
||||
|
||||
if auth_config.u2f_config:
|
||||
u2f_config = auth_config.u2f_config
|
||||
|
|
@ -612,7 +612,7 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
|
||||
if mirror_config.custom_repositories:
|
||||
title = tr('Custom repositories')
|
||||
table = FormattedOutput.as_table(mirror_config.custom_repositories)
|
||||
table = as_table(mirror_config.custom_repositories)
|
||||
output += f'{title}:\n\n{table}'
|
||||
|
||||
return output.strip()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from archinstall.lib.output import FormattedOutput
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ class MenuHelper[ValueT]:
|
|||
display_data: dict[str, ValueT | str | None] = {}
|
||||
|
||||
if data:
|
||||
table = FormattedOutput.as_table(data)
|
||||
table = as_table(data)
|
||||
rows = table.split('\n')
|
||||
|
||||
# these are the header rows of the table
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ from archinstall.lib.models.mirrors import (
|
|||
SignOption,
|
||||
)
|
||||
from archinstall.lib.models.packages import Repository
|
||||
from archinstall.lib.output import FormattedOutput
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.lib.utils.format import as_table
|
||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.result import ResultType
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ class MirrorMenu(AbstractSubMenu[MirrorConfiguration]):
|
|||
return None
|
||||
|
||||
custom_mirrors: list[CustomRepository] = item.value
|
||||
output = FormattedOutput.as_table(custom_mirrors)
|
||||
output = as_table(custom_mirrors)
|
||||
return output.strip()
|
||||
|
||||
def _prev_custom_servers(self, item: MenuItem) -> str | None:
|
||||
|
|
|
|||
|
|
@ -3,114 +3,11 @@ import os
|
|||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from collections.abc import Callable
|
||||
from dataclasses import asdict, is_dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from archinstall.lib.utils.encoding import unicode_ljust, unicode_rjust
|
||||
from archinstall.lib.utils.util import timestamp
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import DataclassInstance
|
||||
|
||||
|
||||
class FormattedOutput:
|
||||
@staticmethod
|
||||
def _get_values(
|
||||
o: DataclassInstance,
|
||||
class_formatter: str | Callable | None = None, # type: ignore[type-arg] # pyright: ignore[reportMissingTypeArgument]
|
||||
filter_list: list[str] = [],
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
the original values returned a dataclass as dict thru the call to some specific methods
|
||||
this version allows thru the parameter class_formatter to call a dynamically selected formatting method.
|
||||
Can transmit a filter list to the class_formatter,
|
||||
"""
|
||||
if class_formatter:
|
||||
# if invoked per reference it has to be a standard function or a classmethod.
|
||||
# A method of an instance does not make sense
|
||||
if callable(class_formatter):
|
||||
return class_formatter(o, filter_list)
|
||||
# if is invoked by name we restrict it to a method of the class. No need to mess more
|
||||
elif hasattr(o, class_formatter) and callable(getattr(o, class_formatter)):
|
||||
func = getattr(o, class_formatter)
|
||||
return func(filter_list)
|
||||
|
||||
raise ValueError('Unsupported formatting call')
|
||||
elif hasattr(o, 'table_data'):
|
||||
return o.table_data()
|
||||
elif hasattr(o, 'json'):
|
||||
return o.json()
|
||||
elif is_dataclass(o):
|
||||
return asdict(o)
|
||||
else:
|
||||
return o.__dict__ # type: ignore[unreachable]
|
||||
|
||||
@classmethod
|
||||
def as_table(
|
||||
cls,
|
||||
obj: list[Any],
|
||||
class_formatter: str | Callable | None = None, # type: ignore[type-arg]
|
||||
filter_list: list[str] = [],
|
||||
capitalize: bool = False,
|
||||
) -> str:
|
||||
"""variant of as_table (subtly different code) which has two additional parameters
|
||||
filter which is a list of fields which will be shown
|
||||
class_formatter a special method to format the outgoing data
|
||||
|
||||
A general comment, the format selected for the output (a string where every data record is separated by newline)
|
||||
is for compatibility with a print statement
|
||||
As_table_filter can be a drop in replacement for as_table
|
||||
"""
|
||||
raw_data = [cls._get_values(o, class_formatter, filter_list) for o in obj]
|
||||
|
||||
# determine the maximum column size
|
||||
column_width: dict[str, int] = {}
|
||||
for o in raw_data:
|
||||
for k, v in o.items():
|
||||
if not filter_list or k in filter_list:
|
||||
column_width.setdefault(k, 0)
|
||||
column_width[k] = max([column_width[k], len(str(v)), len(k)])
|
||||
|
||||
if not filter_list:
|
||||
filter_list = list(column_width.keys())
|
||||
|
||||
# create the header lines
|
||||
output = ''
|
||||
key_list = []
|
||||
for key in filter_list:
|
||||
width = column_width[key]
|
||||
key = key.replace('!', '').replace('_', ' ')
|
||||
|
||||
if capitalize:
|
||||
key = key.capitalize()
|
||||
|
||||
key_list.append(unicode_ljust(key, width))
|
||||
|
||||
output += ' | '.join(key_list) + '\n'
|
||||
output += '-' * len(output) + '\n'
|
||||
|
||||
# create the data lines
|
||||
for record in raw_data:
|
||||
obj_data = []
|
||||
for key in filter_list:
|
||||
width = column_width.get(key, len(key))
|
||||
value = record.get(key, '')
|
||||
|
||||
if '!' in key:
|
||||
value = '*' * len(value)
|
||||
|
||||
if isinstance(value, (int, float)) or (isinstance(value, str) and value.isnumeric()):
|
||||
obj_data.append(unicode_rjust(str(value), width))
|
||||
else:
|
||||
obj_data.append(unicode_ljust(str(value), width))
|
||||
|
||||
output += ' | '.join(obj_data) + '\n'
|
||||
|
||||
return output
|
||||
|
||||
|
||||
class Logger:
|
||||
def __init__(self, path: Path | None = None) -> None:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
from collections.abc import Callable
|
||||
from dataclasses import asdict, is_dataclass
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from archinstall.lib.utils.encoding import unicode_ljust, unicode_rjust
|
||||
from archinstall.tui.rich import BaseRichTable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import DataclassInstance
|
||||
|
||||
|
||||
def as_key_value_pair(
|
||||
entries: dict[str, str | list[str] | bool],
|
||||
|
|
@ -45,3 +53,96 @@ def as_columns(entries: list[str], cols: int) -> str:
|
|||
output += out_fmt.format(*row) + '\n'
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def _get_values(
|
||||
o: DataclassInstance,
|
||||
class_formatter: str | Callable | None = None, # type: ignore[type-arg] # pyright: ignore[reportMissingTypeArgument]
|
||||
filter_list: list[str] = [],
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
the original values returned a dataclass as dict thru the call to some specific methods
|
||||
this version allows thru the parameter class_formatter to call a dynamically selected formatting method.
|
||||
Can transmit a filter list to the class_formatter,
|
||||
"""
|
||||
if class_formatter:
|
||||
# if invoked per reference it has to be a standard function or a classmethod.
|
||||
# A method of an instance does not make sense
|
||||
if callable(class_formatter):
|
||||
return class_formatter(o, filter_list)
|
||||
# if is invoked by name we restrict it to a method of the class. No need to mess more
|
||||
elif hasattr(o, class_formatter) and callable(getattr(o, class_formatter)):
|
||||
func = getattr(o, class_formatter)
|
||||
return func(filter_list)
|
||||
|
||||
raise ValueError('Unsupported formatting call')
|
||||
elif hasattr(o, 'table_data'):
|
||||
return o.table_data()
|
||||
elif hasattr(o, 'json'):
|
||||
return o.json()
|
||||
elif is_dataclass(o):
|
||||
return asdict(o)
|
||||
else:
|
||||
return o.__dict__ # type: ignore[unreachable]
|
||||
|
||||
|
||||
def as_table(
|
||||
obj: list[Any],
|
||||
class_formatter: str | Callable | None = None, # type: ignore[type-arg]
|
||||
filter_list: list[str] = [],
|
||||
capitalize: bool = False,
|
||||
) -> str:
|
||||
"""variant of as_table (subtly different code) which has two additional parameters
|
||||
filter which is a list of fields which will be shown
|
||||
class_formatter a special method to format the outgoing data
|
||||
|
||||
A general comment, the format selected for the output (a string where every data record is separated by newline)
|
||||
is for compatibility with a print statement
|
||||
As_table_filter can be a drop in replacement for as_table
|
||||
"""
|
||||
raw_data = [_get_values(o, class_formatter, filter_list) for o in obj]
|
||||
|
||||
# determine the maximum column size
|
||||
column_width: dict[str, int] = {}
|
||||
for o in raw_data:
|
||||
for k, v in o.items():
|
||||
if not filter_list or k in filter_list:
|
||||
column_width.setdefault(k, 0)
|
||||
column_width[k] = max([column_width[k], len(str(v)), len(k)])
|
||||
|
||||
if not filter_list:
|
||||
filter_list = list(column_width.keys())
|
||||
|
||||
# create the header lines
|
||||
output = ''
|
||||
key_list = []
|
||||
for key in filter_list:
|
||||
width = column_width[key]
|
||||
key = key.replace('!', '').replace('_', ' ')
|
||||
|
||||
if capitalize:
|
||||
key = key.capitalize()
|
||||
|
||||
key_list.append(unicode_ljust(key, width))
|
||||
|
||||
output += ' | '.join(key_list) + '\n'
|
||||
output += '-' * len(output) + '\n'
|
||||
|
||||
# create the data lines
|
||||
for record in raw_data:
|
||||
obj_data = []
|
||||
for key in filter_list:
|
||||
width = column_width.get(key, len(key))
|
||||
value = record.get(key, '')
|
||||
|
||||
if '!' in key:
|
||||
value = '*' * len(value)
|
||||
|
||||
if isinstance(value, (int, float)) or (isinstance(value, str) and value.isnumeric()):
|
||||
obj_data.append(unicode_rjust(str(value), width))
|
||||
else:
|
||||
obj_data.append(unicode_ljust(str(value), width))
|
||||
|
||||
output += ' | '.join(obj_data) + '\n'
|
||||
|
||||
return output
|
||||
|
|
|
|||
Loading…
Reference in New Issue