config: add listings of devices and flavours to config profile init aka prompt_profile() and --no-parse flag

This is slow-ish without SRCINFO caching
This commit is contained in:
InsanePrawn 2022-09-09 01:35:18 +02:00
parent 7b7caf3f37
commit f77aa4f2a2
3 changed files with 72 additions and 11 deletions

View file

@ -1,7 +1,7 @@
import click import click
import logging import logging
from copy import deepcopy from copy import deepcopy
from typing import Any, Optional, Union from typing import Any, Iterable, Optional, Union
from .scheme import Profile from .scheme import Profile
from .profile import PROFILE_EMPTY, PROFILE_DEFAULTS from .profile import PROFILE_EMPTY, PROFILE_DEFAULTS
@ -23,9 +23,10 @@ def comma_str_to_list(s: str, default=None) -> list[str]:
def prompt_config( def prompt_config(
text: str, text: str,
default: Any, default: Any,
field_type: type = str, field_type: Union[type, click.Choice] = str,
bold: bool = True, bold: bool = True,
echo_changes: bool = True, echo_changes: bool = True,
show_choices: bool = False,
) -> tuple[Any, bool]: ) -> tuple[Any, bool]:
""" """
prompts for a new value for a config key. returns the result and a boolean that indicates prompts for a new value for a config key. returns the result and a boolean that indicates
@ -54,16 +55,28 @@ def prompt_config(
if bold: if bold:
text = click.style(text, bold=True) text = click.style(text, bold=True)
result = click.prompt(text, type=field_type, default=default, value_proc=value_conv, show_default=True) # type: ignore result = click.prompt(
text,
type=field_type, # type: ignore
default=default,
value_proc=value_conv,
show_default=True,
show_choices=show_choices,
) # type: ignore
changed = result != (original_default if field_type == list else default) and (true_or_zero(default) or true_or_zero(result)) changed = result != (original_default if field_type == list else default) and (true_or_zero(default) or true_or_zero(result))
if changed and echo_changes: if changed and echo_changes:
print(f'value changed: "{text}" = "{result}"') print(f'value changed: "{text}" = "{result}"')
return result, changed return result, changed
def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict] = {}) -> tuple[Profile, bool]: def prompt_profile(
name: str,
create: bool = True,
defaults: Union[Profile, dict] = {},
no_parse: bool = True,
) -> tuple[Profile, bool]:
"""Prompts the user for every field in `defaults`. Set values to None for an empty profile.""" """Prompts the user for every field in `defaults`. Set values to None for an empty profile."""
PARSEABLE_FIELDS = ['device', 'flavour']
profile: Any = PROFILE_EMPTY | defaults profile: Any = PROFILE_EMPTY | defaults
# don't use get_profile() here because we need the sparse profile # don't use get_profile() here because we need the sparse profile
if name in config.file.profiles: if name in config.file.profiles:
@ -77,6 +90,16 @@ def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict
for key, current in profile.items(): for key, current in profile.items():
current = profile[key] current = profile[key]
text = f'{name}.{key}' text = f'{name}.{key}'
if not no_parse and key in PARSEABLE_FIELDS:
parse_prompt = None
if key == 'device':
parse_prompt = prompt_profile_device
elif key == 'flavour':
parse_prompt = prompt_profile_flavour
else:
raise Exception(f'config: Unhanled parseable field {key}, this is a bug in kupferbootstrap.')
result, _changed = parse_prompt(current, name) # type: ignore
else:
result, _changed = prompt_config(text=text, default=current, field_type=type(PROFILE_DEFAULTS[key])) # type: ignore result, _changed = prompt_config(text=text, default=current, field_type=type(PROFILE_DEFAULTS[key])) # type: ignore
if _changed: if _changed:
profile[key] = result profile[key] = result
@ -84,6 +107,32 @@ def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict
return profile, changed return profile, changed
def prompt_choice(current: Optional[Any], key: str, choices: Iterable[Any], allow_none: bool = True, show_choices: bool = False) -> tuple[Any, bool]:
choices = list(choices) + ([''] if allow_none else [])
res, _ = prompt_config(text=key, default=current, field_type=click.Choice(choices), show_choices=show_choices)
if allow_none and res == '':
res = None
return res, res == current
def prompt_profile_device(current: Optional[str], profile_name: str) -> tuple[str, bool]:
from packages.device import get_devices
devices = get_devices()
print(click.style("Pick your device!\nThese are the available devices:", bold=True))
for dev in sorted(devices.keys()):
print(devices[dev])
return prompt_choice(current, f'profiles.{profile_name}.device', devices.keys())
def prompt_profile_flavour(current: Optional[str], profile_name: str) -> tuple[str, bool]:
from packages.flavour import get_flavours
flavours = get_flavours()
print(click.style("Pick your flavour!\nThese are the available flavours:", bold=True))
for f in sorted(flavours.keys()):
print(flavours[f])
return prompt_choice(current, f'profiles.{profile_name}.flavour', flavours.keys())
def config_dot_name_get(name: str, config: dict[str, Any], prefix: str = '') -> Any: def config_dot_name_get(name: str, config: dict[str, Any], prefix: str = '') -> Any:
if not isinstance(config, dict): if not isinstance(config, dict):
raise Exception(f"Couldn't resolve config name: passed config is not a dict: {repr(config)}") raise Exception(f"Couldn't resolve config name: passed config is not a dict: {repr(config)}")
@ -138,11 +187,13 @@ def cmd_config():
noninteractive_flag = click.option('-N', '--non-interactive', is_flag=True) noninteractive_flag = click.option('-N', '--non-interactive', is_flag=True)
noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_flag=True) noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_flag=True)
noparse_flag = click.option('--no-parse', help="Don't search PKGBUILDs for devices and flavours", is_flag=True)
@cmd_config.command(name='init') @cmd_config.command(name='init')
@noninteractive_flag @noninteractive_flag
@noop_flag @noop_flag
@noparse_flag
@click.option( @click.option(
'--sections', '--sections',
'-s', '-s',
@ -152,7 +203,13 @@ noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_
show_choices=True, show_choices=True,
) )
@click.pass_context @click.pass_context
def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive: bool = False, noop: bool = False): def cmd_config_init(
ctx,
sections: list[str] = CONFIG_SECTIONS,
non_interactive: bool = False,
noop: bool = False,
no_parse: bool = False,
):
"""Initialize the config file""" """Initialize the config file"""
if not non_interactive: if not non_interactive:
results: dict[str, dict] = {} results: dict[str, dict] = {}
@ -173,7 +230,7 @@ def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive:
if 'profiles' in sections: if 'profiles' in sections:
current_profile = 'default' if 'current' not in config.file.profiles else config.file.profiles.current current_profile = 'default' if 'current' not in config.file.profiles else config.file.profiles.current
new_current, _ = prompt_config('profiles.current', default=current_profile, field_type=str) new_current, _ = prompt_config('profiles.current', default=current_profile, field_type=str)
profile, changed = prompt_profile(new_current, create=True) profile, changed = prompt_profile(new_current, create=True, no_parse=no_parse)
config.update_profile(new_current, profile) config.update_profile(new_current, profile)
if not noop: if not noop:
if not prompt_for_save(ctx): if not prompt_for_save(ctx):
@ -188,9 +245,10 @@ def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive:
@cmd_config.command(name='set') @cmd_config.command(name='set')
@noninteractive_flag @noninteractive_flag
@noop_flag @noop_flag
@noparse_flag
@click.argument('key_vals', nargs=-1) @click.argument('key_vals', nargs=-1)
@click.pass_context @click.pass_context
def cmd_config_set(ctx, key_vals: list[str], non_interactive: bool = False, noop: bool = False): def cmd_config_set(ctx, key_vals: list[str], non_interactive: bool = False, noop: bool = False, no_parse: bool = False):
""" """
Set config entries. Pass entries as `key=value` pairs, with keys as dot-separated identifiers, Set config entries. Pass entries as `key=value` pairs, with keys as dot-separated identifiers,
like `build.clean_mode=false` or alternatively just keys to get prompted if run interactively. like `build.clean_mode=false` or alternatively just keys to get prompted if run interactively.
@ -245,16 +303,17 @@ def cmd_profile():
@cmd_profile.command(name='init') @cmd_profile.command(name='init')
@noninteractive_flag @noninteractive_flag
@noop_flag @noop_flag
@noparse_flag
@click.argument('name', required=True) @click.argument('name', required=True)
@click.pass_context @click.pass_context
def cmd_profile_init(ctx, name: str, non_interactive: bool = False, noop: bool = False): def cmd_profile_init(ctx, name: str, non_interactive: bool = False, noop: bool = False, no_parse: bool = False):
"""Create or edit a profile""" """Create or edit a profile"""
profile = deepcopy(PROFILE_EMPTY) profile = deepcopy(PROFILE_EMPTY)
if name in config.file.profiles: if name in config.file.profiles:
profile |= config.file.profiles[name] profile |= config.file.profiles[name]
if not non_interactive: if not non_interactive:
profile, _changed = prompt_profile(name, create=True) profile, _changed = prompt_profile(name, create=True, no_parse=no_parse)
config.update_profile(name, profile) config.update_profile(name, profile)
if not noop: if not noop:

View file

@ -99,6 +99,7 @@ def get_devices(pkgbuilds: Optional[dict[str, Pkgbuild]] = None, lazy: bool = Tr
global _device_cache, _device_cache_populated global _device_cache, _device_cache_populated
use_cache = _device_cache_populated and lazy use_cache = _device_cache_populated and lazy
if not use_cache: if not use_cache:
logging.info("Searching PKGBUILDs for device packages")
if not pkgbuilds: if not pkgbuilds:
pkgbuilds = discover_pkgbuilds(lazy=lazy, repositories=['device']) pkgbuilds = discover_pkgbuilds(lazy=lazy, repositories=['device'])
_device_cache.clear() _device_cache.clear()

View file

@ -39,6 +39,7 @@ def get_flavours(lazy: bool = True):
global _flavours_cache, _flavours_discovered global _flavours_cache, _flavours_discovered
if lazy and _flavours_discovered: if lazy and _flavours_discovered:
return _flavours_cache return _flavours_cache
logging.info("Searching PKGBUILDs for flavour packages")
flavours: dict[str, Flavour] = {} flavours: dict[str, Flavour] = {}
pkgbuilds: dict[str, Pkgbuild] = discover_pkgbuilds(lazy=(lazy or not _flavours_discovered)) pkgbuilds: dict[str, Pkgbuild] = discover_pkgbuilds(lazy=(lazy or not _flavours_discovered))
for pkg in pkgbuilds.values(): for pkg in pkgbuilds.values():