Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib')
-rw-r--r--archinstall/lib/disk/filesystem.py10
-rw-r--r--archinstall/lib/disk/user_guides.py1
-rw-r--r--archinstall/lib/installer.py6
-rw-r--r--archinstall/lib/menu/menu.py2
-rw-r--r--archinstall/lib/menu/selection_menu.py58
-rw-r--r--archinstall/lib/profiles.py4
-rw-r--r--archinstall/lib/translation.py13
-rw-r--r--archinstall/lib/user_interaction.py82
8 files changed, 103 insertions, 73 deletions
diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py
index c8a74e3f..4f1b4217 100644
--- a/archinstall/lib/disk/filesystem.py
+++ b/archinstall/lib/disk/filesystem.py
@@ -83,7 +83,7 @@ class Filesystem:
for partition in layout.get('partitions', []):
# We don't want to re-add an existing partition (those containing a UUID already)
if partition.get('wipe', False) and not partition.get('PARTUUID', None):
- print("Adding partition....")
+ print(_("Adding partition...."))
start = partition.get('start') or (
prev_partition and f'{prev_partition["device_instance"].end_sectors}s' or DEFAULT_PARTITION_START)
partition['device_instance'] = self.add_partition(partition.get('type', 'primary'),
@@ -94,7 +94,7 @@ class Filesystem:
# print('Device instance:', partition['device_instance'])
elif (partition_uuid := partition.get('PARTUUID')) and (partition_instance := self.blockdevice.get_partition(uuid=partition_uuid)):
- print("Re-using partition_instance:", partition_instance)
+ print(_("Re-using partition instance: {}").format(partition_instance))
partition['device_instance'] = partition_instance
else:
raise ValueError(f"{self}.load_layout() doesn't know how to continue without a new partition definition or a UUID ({partition.get('PARTUUID')}) on the device ({self.blockdevice.get_partition(uuid=partition.get('PARTUUID'))}).")
@@ -113,7 +113,9 @@ class Filesystem:
raise ValueError(f"Missing encryption password for {partition['device_instance']}")
from ..user_interaction import get_password
- storage['arguments']['!encryption-password'] = get_password(f"Enter a encryption password for {partition['device_instance']}")
+
+ prompt = str(_('Enter a encryption password for {}').format(partition['device_instance']))
+ storage['arguments']['!encryption-password'] = get_password(prompt)
partition['!password'] = storage['arguments']['!encryption-password']
@@ -136,7 +138,7 @@ class Filesystem:
while True:
partition['filesystem']['format'] = input(f"Enter a valid fs-type for newly encrypted partition {partition['filesystem']['format']}: ").strip()
if not partition['filesystem']['format'] or valid_fs_type(partition['filesystem']['format']) is False:
- print("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's.")
+ print(_("You need to enter a valid fs-type in order to continue. See `man parted` for valid fs-type's."))
continue
break
diff --git a/archinstall/lib/disk/user_guides.py b/archinstall/lib/disk/user_guides.py
index 8acb8cd2..90d323c7 100644
--- a/archinstall/lib/disk/user_guides.py
+++ b/archinstall/lib/disk/user_guides.py
@@ -11,6 +11,7 @@ from ..hardware import has_uefi
from ..output import log
from ..menu import Menu
+
def suggest_single_disk_layout(block_device :BlockDevice,
default_filesystem :Optional[str] = None,
advanced_options :bool = False) -> Dict[str, Any]:
diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py
index cde1ec1d..daac340b 100644
--- a/archinstall/lib/installer.py
+++ b/archinstall/lib/installer.py
@@ -141,8 +141,8 @@ class Installer:
# We avoid printing /mnt/<log path> because that might confuse people if they note it down
# and then reboot, and a identical log file will be found in the ISO medium anyway.
- print(f"[!] A log file has been created here: {os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])}")
- print(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues")
+ print(_("[!] A log file has been created here: {} {}").format(os.path.join(storage['LOG_PATH'], storage['LOG_FILE'])))
+ print(_(" Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"))
raise args[1]
self.genfstab()
@@ -282,7 +282,7 @@ class Installer:
partition.mount(f'{self.target}{mountpoint}')
- def post_install_check(self, *args :str, **kwargs :str) -> List[bool]:
+ def post_install_check(self, *args :str, **kwargs :str) -> List[str]:
return [step for step, flag in self.helper_flags.items() if flag is False]
def pacstrap(self, *packages :str, **kwargs :str) -> bool:
diff --git a/archinstall/lib/menu/menu.py b/archinstall/lib/menu/menu.py
index dfd47a7a..1811482d 100644
--- a/archinstall/lib/menu/menu.py
+++ b/archinstall/lib/menu/menu.py
@@ -65,7 +65,7 @@ class Menu(TerminalMenu):
menu_title = f'\n{title}\n\n'
if skip:
- menu_title += "Use ESC to skip\n\n"
+ menu_title += str(_("Use ESC to skip\n\n"))
if default_option:
# if a default value was specified we move that one
diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py
index f3fd3b4c..f094c6dc 100644
--- a/archinstall/lib/menu/selection_menu.py
+++ b/archinstall/lib/menu/selection_menu.py
@@ -1,4 +1,5 @@
import sys
+from typing import Dict
from .menu import Menu
from ..general import SysCommand
@@ -7,7 +8,7 @@ from ..output import log
from ..profiles import is_desktop_profile
from ..disk import encrypted_partitions
from ..locale_helpers import set_keyboard_language
-from ..user_interaction import get_password
+from ..user_interaction import get_password, ask_for_a_timezone
from ..user_interaction import ask_ntp
from ..user_interaction import ask_for_swap
from ..user_interaction import ask_for_bootloader
@@ -15,7 +16,6 @@ from ..user_interaction import ask_hostname
from ..user_interaction import ask_for_audio_selection
from ..user_interaction import ask_additional_packages_to_install
from ..user_interaction import ask_to_configure_network
-from ..user_interaction import ask_for_a_timezone
from ..user_interaction import ask_for_superuser_account
from ..user_interaction import ask_for_additional_users
from ..user_interaction import select_language
@@ -30,6 +30,7 @@ from ..user_interaction import select_profile
from ..user_interaction import select_archinstall_language
from ..translation import Translation
+
class Selector:
def __init__(
self,
@@ -76,7 +77,6 @@ class Selector:
self._display_func = display_func
self._current_selection = default
self.enabled = enabled
- self.text = self.menu_text()
self._dependencies = dependencies
self._dependencies_not = dependencies_not
@@ -88,12 +88,15 @@ class Selector:
def dependencies_not(self):
return self._dependencies_not
+ @property
+ def current_selection(self):
+ return self._current_selection
+
def set_enabled(self):
self.enabled = True
def update_description(self, description):
self._description = description
- self.text = self.menu_text()
def menu_text(self):
current = ''
@@ -105,14 +108,13 @@ class Selector:
current = str(self._current_selection)
if current:
- padding = 35 - len(self._description)
+ padding = 35 - len(str(self._description))
current = ' ' * padding + f'SET: {current}'
return f'{self._description} {current}'
def set_current_selection(self, current):
self._current_selection = current
- self.text = self.menu_text()
def has_selection(self):
if self._current_selection is None:
@@ -168,7 +170,7 @@ class GlobalMenu:
self._menu_options['!encryption-password'] = \
Selector(
_('Set encryption password'),
- lambda: get_password(prompt='Enter disk encryption password (leave blank for no encryption): '),
+ lambda: get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))),
display_func=lambda x: self._secret(x) if x else 'None',
dependencies=['harddrives'])
self._menu_options['swap'] = \
@@ -181,7 +183,7 @@ class GlobalMenu:
_('Select bootloader'),
lambda: ask_for_bootloader(storage['arguments'].get('advanced', False)),)
self._menu_options['hostname'] = \
- Selector('Specify hostname', lambda: ask_hostname())
+ Selector(_('Specify hostname'), lambda: ask_hostname())
self._menu_options['!root-password'] = \
Selector(
_('Set root password'),
@@ -222,10 +224,10 @@ class GlobalMenu:
Selector(
_('Configure network'),
lambda: ask_to_configure_network(),
- display_func=lambda x: x if x else 'Not configured, unavailable unless setup manually',
+ display_func=lambda x: x if x else _('Not configured, unavailable unless setup manually'),
default={})
self._menu_options['timezone'] = \
- Selector('Select timezone', lambda: ask_for_a_timezone())
+ Selector(_('Select timezone'), lambda: ask_for_a_timezone())
self._menu_options['ntp'] = \
Selector(
_('Set automatic time sync (NTP)'),
@@ -235,7 +237,7 @@ class GlobalMenu:
Selector(
self._install_text(),
enabled=True)
- self._menu_options['abort'] = Selector('Abort', enabled=True)
+ self._menu_options['abort'] = Selector(_('Abort'), enabled=True)
def enable(self, selector_name, omit_if_set=False):
arg = storage['arguments'].get(selector_name, None)
@@ -259,26 +261,29 @@ class GlobalMenu:
self._set_kb_language()
enabled_menus = self._menus_to_enable()
- menu_text = [m.text for m in enabled_menus.values()]
- selection = Menu('Set/Modify the below options', menu_text, sort=False).run()
+ menu_text = [m.menu_text() for m in enabled_menus.values()]
+ selection = Menu(_('Set/Modify the below options'), menu_text, sort=False).run()
+
if selection:
selection = selection.strip()
- if 'Abort' in selection:
+ if str(_('Abort')) in selection:
exit(0)
- elif 'Install' in selection:
+ elif str(_('Install')) in selection:
if self._missing_configs() == 0:
break
else:
self._process_selection(selection)
+
for key in self._menu_options:
sel = self._menu_options[key]
if key not in storage['arguments']:
- storage['arguments'][key] = sel._current_selection
+ storage['arguments'][key] = sel.current_selection
+
self._post_processing()
def _process_selection(self, selection):
# find the selected option in our option list
- option = [[k, v] for k, v in self._menu_options.items() if v.text.strip() == selection]
+ option = [[k, v] for k, v in self._menu_options.items() if v.menu_text().strip() == selection]
if len(option) != 1:
raise ValueError(f'Selection not found: {selection}')
@@ -306,7 +311,7 @@ class GlobalMenu:
def _install_text(self):
missing = self._missing_configs()
if missing > 0:
- return f'Install ({missing} config(s) missing)'
+ return _('Install ({} config(s) missing)').format(missing)
return 'Install'
def _missing_configs(self):
@@ -338,7 +343,7 @@ class GlobalMenu:
return language
def _set_root_password(self):
- prompt = 'Enter root password (leave blank to disable root & create superuser): '
+ prompt = str(_('Enter root password (leave blank to disable root): '))
password = get_password(prompt=prompt)
# TODO: Do we really wanna wipe the !superusers and !users if root password is set?
@@ -368,11 +373,12 @@ class GlobalMenu:
storage['arguments']['disk_layouts'] = {}
if not harddrives:
- prompt = 'You decided to skip harddrive selection\n'
- prompt += f"and will use whatever drive-setup is mounted at {storage['MOUNT_POINT']} (experimental)\n"
- prompt += "WARNING: Archinstall won't check the suitability of this setup\n"
+ prompt = _(
+ "You decided to skip harddrive selection\nand will use whatever drive-setup is mounted at {} (experimental)\n"
+ "WARNING: Archinstall won't check the suitability of this setup\n"
+ "Do you wish to continue?"
+ ).format(storage['MOUNT_POINT'])
- prompt += 'Do you wish to continue?'
choice = Menu(prompt, ['yes', 'no'], default_option='yes').run()
if choice == 'no':
@@ -397,11 +403,11 @@ class GlobalMenu:
return profile
def _create_superuser_account(self):
- superuser = ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True)
+ superuser = ask_for_superuser_account(str(_('Create a required super-user with sudo privileges: ')), forced=True)
return superuser
def _create_user_account(self):
- users, superusers = ask_for_additional_users('Enter a username to create an additional user: ')
+ users, superusers = ask_for_additional_users(str(_('Enter a username to create an additional user (leave blank to skip): ')))
storage['arguments']['!superusers'] = {**storage['arguments'].get('!superusers', {}), **superusers}
return users
@@ -431,7 +437,7 @@ class GlobalMenu:
raise ValueError(f'No selection found: {selection_name}')
- def _menus_to_enable(self):
+ def _menus_to_enable(self) -> Dict[str, Selector]:
enabled_menus = {}
for name, selection in self._menu_options.items():
diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py
index 9befd3d5..4295fa8f 100644
--- a/archinstall/lib/profiles.py
+++ b/archinstall/lib/profiles.py
@@ -86,10 +86,10 @@ def list_profiles(
try:
profile_list = json.loads(grab_url_data(profiles_url))
except urllib.error.HTTPError as err:
- print(f'Error: Listing profiles on URL "{profiles_url}" resulted in:', err)
+ print(_('Error: Listing profiles on URL "{}" resulted in:').format(profiles_url), err)
return cache
except json.decoder.JSONDecodeError as err:
- print(f'Error: Could not decode "{profiles_url}" result as JSON:', err)
+ print(_('Error: Could not decode "{}" result as JSON:').format(profiles_url), err)
return cache
for profile in profile_list:
diff --git a/archinstall/lib/translation.py b/archinstall/lib/translation.py
index 1ae6989d..16556fbe 100644
--- a/archinstall/lib/translation.py
+++ b/archinstall/lib/translation.py
@@ -28,7 +28,7 @@ class Languages:
class DeferredTranslation:
- def __init__(self, message):
+ def __init__(self, message: str):
self.message = message
def __len__(self) -> int:
@@ -40,6 +40,15 @@ class DeferredTranslation:
return self.message
return translate(self.message)
+ def __lt__(self, other) -> bool:
+ return self.message < other
+
+ def __gt__(self, other) -> bool:
+ return self.message > other
+
+ def format(self, *args) -> str:
+ return self.message.format(*args)
+
@classmethod
def install(cls):
import builtins
@@ -48,8 +57,6 @@ class DeferredTranslation:
class Translation:
def __init__(self, locales_dir):
- DeferredTranslation.install()
-
self._languages = {}
for name in self.get_all_names():
diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py
index 3db74e9b..badf4cb4 100644
--- a/archinstall/lib/user_interaction.py
+++ b/archinstall/lib/user_interaction.py
@@ -96,7 +96,10 @@ def do_countdown() -> bool:
return True
-def get_password(prompt :str = "Enter a password: ") -> Optional[str]:
+def get_password(prompt :str = '') -> Optional[str]:
+ if not prompt:
+ prompt = _("Enter a password: ")
+
while passwd := getpass.getpass(prompt):
passwd_verification = getpass.getpass(prompt=_('And one more time for verification: '))
if passwd != passwd_verification:
@@ -275,8 +278,8 @@ def ask_for_swap():
def ask_ntp() -> bool:
- prompt = _('Would you like to use automatic time synchronization (NTP) with the default time servers?')
- prompt += _('Hardware time and other post-configuration steps might be required in order for NTP to work. For more information, please check the Arch wiki')
+ prompt = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n'))
+ prompt += str(_('Hardware time and other post-configuration steps might be required in order for NTP to work.\nFor more information, please check the Arch wiki'))
choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='yes').run()
return False if choice == 'no' else True
@@ -286,7 +289,7 @@ def ask_hostname():
return hostname
-def ask_for_superuser_account(prompt :str = '', forced :bool = False) -> Dict[str, Dict[str, str]]:
+def ask_for_superuser_account(prompt: str = '', forced :bool = False) -> Dict[str, Dict[str, str]]:
prompt = prompt if prompt else _('Username for required superuser with sudo privileges: ')
while 1:
new_user = input(prompt).strip(' ')
@@ -301,7 +304,7 @@ def ask_for_superuser_account(prompt :str = '', forced :bool = False) -> Dict[st
elif not check_for_correct_username(new_user):
continue
- prompt = _('Password for user "{}"').format(new_user)
+ prompt = str(_('Password for user "{}": ').format(new_user))
password = get_password(prompt=prompt)
return {new_user: {"!password": password}}
@@ -318,11 +321,14 @@ def ask_for_additional_users(prompt :str = '') -> tuple[dict[str, dict[str, str
if not check_for_correct_username(new_user):
continue
- prompt = _('Password for user "{}"').format(new_user)
- password = get_password(prompt=prompt)
+ password = get_password(prompt=str(_('Password for user "{}": ').format(new_user)))
- prompt = _('Should this user be a superuser (sudoer)?')
- choice = Menu(prompt, ['yes', 'no'], skip=False, default_option='no').run()
+ choice = Menu(
+ str(_('Should this user be a superuser (sudoer)?')),
+ ['yes', 'no'],
+ skip=False,
+ default_option='no'
+ ).run()
if choice == 'yes':
superusers[new_user] = {"!password": password}
@@ -399,7 +405,7 @@ def ask_additional_packages_to_install(packages :List[str] = None) -> List[str]:
if len(packages):
# Verify packages that were given
try:
- log("Verifying that additional packages exist (this might take a few seconds)")
+ print(_("Verifying that additional packages exist (this might take a few seconds)"))
validate_package_list(packages)
break
except RequirementError as e:
@@ -587,21 +593,28 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
# Test code: [part.__dump__() for part in block_device.partitions.values()]
# TODO: Squeeze in BTRFS subvolumes here
+ new_partition = _('Create a new partition')
+ suggest_partition_layout = _('Suggest partition layout')
+ delete_partition = _('Delete a partition')
+ delete_all_partitions = _('Clear/Delete all partitions')
+ assign_mount_point = _('Assign mount-point for a partition')
+ mark_formatted = _('Mark/Unmark a partition to be formatted (wipes data)')
+ mark_encrypted = _('Mark/Unmark a partition as encrypted')
+ mark_bootable = _('Mark/Unmark a partition as bootable (automatic for /boot)')
+ set_filesystem_partition = _('Set desired filesystem for a partition')
+
while True:
- modes = [
- "Create a new partition",
- f"Suggest partition layout for {block_device}"
- ]
+ modes = [new_partition, suggest_partition_layout]
if len(block_device_struct['partitions']):
modes += [
- "Delete a partition",
- "Clear/Delete all partitions",
- "Assign mount-point for a partition",
- "Mark/Unmark a partition to be formatted (wipes data)",
- "Mark/Unmark a partition as encrypted",
- "Mark/Unmark a partition as bootable (automatic for /boot)",
- "Set desired filesystem for a partition",
+ delete_partition,
+ delete_all_partitions,
+ assign_mount_point,
+ mark_formatted,
+ mark_encrypted,
+ mark_bootable,
+ set_filesystem_partition,
]
title = _('Select what to do with\n{}').format(block_device)
@@ -615,7 +628,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
if not task:
break
- if task == 'Create a new partition':
+ if task == new_partition:
# if partition_type == 'gpt':
# # https://www.gnu.org/software/parted/manual/html_node/mkpart.html
# # https://www.gnu.org/software/parted/manual/html_node/mklabel.html
@@ -632,7 +645,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
else:
end_suggested = '100%'
- prompt = _('Enter the end sector of the partition (percentage or block number, ex: {}): "').format(end_suggested)
+ prompt = _('Enter the end sector of the partition (percentage or block number, ex: {}): ').format(end_suggested)
end = input(prompt).strip()
if not end.strip():
@@ -656,7 +669,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
else:
log(f"Invalid start ({valid_parted_position(start)}) or end ({valid_parted_position(end)}) for this partition. Ignoring this partition creation.", fg="red")
continue
- elif task[:len("Suggest partition layout")] == "Suggest partition layout":
+ elif task == suggest_partition_layout:
if len(block_device_struct["partitions"]):
prompt = _('{} contains queued partitions, this will remove those, are you sure?').format(block_device)
choice = Menu(prompt, ['yes', 'no'], default_option='no').run()
@@ -670,15 +683,15 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
else:
current_layout = current_partition_layout(block_device_struct['partitions'], with_idx=True)
- if task == "Delete a partition":
+ if task == delete_partition:
title = _('{}\n\nSelect by index which partitions to delete').format(current_layout)
to_delete = select_partition(title, block_device_struct["partitions"], multiple=True)
if to_delete:
block_device_struct['partitions'] = [p for idx, p in enumerate(block_device_struct['partitions']) if idx not in to_delete]
- elif task == "Clear/Delete all partitions":
+ elif task == delete_all_partitions:
block_device_struct["partitions"] = []
- elif task == "Assign mount-point for a partition":
+ elif task == assign_mount_point:
title = _('{}\n\nSelect by index which partition to mount where').format(current_layout)
partition = select_partition(title, block_device_struct["partitions"])
@@ -694,7 +707,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
else:
del(block_device_struct["partitions"][partition]['mountpoint'])
- elif task == "Mark/Unmark a partition to be formatted (wipes data)":
+ elif task == mark_formatted:
title = _('{}\n\nSelect which partition to mask for formatting').format(current_layout)
partition = select_partition(title, block_device_struct["partitions"])
@@ -713,7 +726,7 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
# Negate the current wipe marking
block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False)
- elif task == "Mark/Unmark a partition as encrypted":
+ elif task == mark_encrypted:
title = _('{}\n\nSelect which partition to mark as encrypted').format(current_layout)
partition = select_partition(title, block_device_struct["partitions"])
@@ -721,14 +734,14 @@ def manage_new_and_existing_partitions(block_device :BlockDevice) -> Dict[str, A
# Negate the current encryption marking
block_device_struct["partitions"][partition]['encrypted'] = not block_device_struct["partitions"][partition].get('encrypted', False)
- elif task == "Mark/Unmark a partition as bootable (automatic for /boot)":
+ elif task == mark_bootable:
title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout)
partition = select_partition(title, block_device_struct["partitions"])
if partition is not None:
block_device_struct["partitions"][partition]['boot'] = not block_device_struct["partitions"][partition].get('boot', False)
- elif task == "Set desired filesystem for a partition":
+ elif task == set_filesystem_partition:
title = _('{}\n\nSelect which partition to set a filesystem on').format(current_layout)
partition = select_partition(title, block_device_struct["partitions"])
@@ -762,10 +775,11 @@ def select_archinstall_language(default='English'):
def select_disk_layout(block_devices :list, advanced_options=False) -> Dict[str, Any]:
- wipe_mode = _('Wipe all selected drives and use a best-effort default partition layout')
- custome_mode = _('Select what to do with each individual drive (followed by partition usage)')
+ wipe_mode = str(_('Wipe all selected drives and use a best-effort default partition layout'))
+ custome_mode = str(_('Select what to do with each individual drive (followed by partition usage)'))
modes = [wipe_mode, custome_mode]
+ print(modes)
mode = Menu(_('Select what you wish to do with the selected block devices'), modes, skip=False).run()
if mode == wipe_mode:
@@ -802,7 +816,7 @@ def select_disk(dict_o_disks :Dict[str, BlockDevice]) -> BlockDevice:
raise DiskError('select_disk() requires a non-empty dictionary of disks to select from.')
-def select_profile() -> Optional[str]:
+def select_profile() -> Optional[Profile]:
"""
# Asks the user to select a profile from the available profiles.
#