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 logging
from copy import deepcopy
from typing import Any, Optional, Union
from typing import Any, Iterable, Optional, Union
from .scheme import Profile
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(
text: str,
default: Any,
field_type: type = str,
field_type: Union[type, click.Choice] = str,
bold: bool = True,
echo_changes: bool = True,
show_choices: bool = False,
) -> tuple[Any, bool]:
"""
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:
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))
if changed and echo_changes:
print(f'value changed: "{text}" = "{result}"')
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."""
PARSEABLE_FIELDS = ['device', 'flavour']
profile: Any = PROFILE_EMPTY | defaults
# don't use get_profile() here because we need the sparse profile
if name in config.file.profiles:
@ -77,13 +90,49 @@ def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict
for key, current in profile.items():
current = profile[key]
text = f'{name}.{key}'
result, _changed = prompt_config(text=text, default=current, field_type=type(PROFILE_DEFAULTS[key])) # type: ignore
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
if _changed:
profile[key] = result
changed = True
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:
if not isinstance(config, dict):
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)
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')
@noninteractive_flag
@noop_flag
@noparse_flag
@click.option(
'--sections',
'-s',
@ -152,7 +203,13 @@ noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_
show_choices=True,
)
@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"""
if not non_interactive:
results: dict[str, dict] = {}
@ -173,7 +230,7 @@ def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive:
if 'profiles' in sections:
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)
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)
if not noop:
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')
@noninteractive_flag
@noop_flag
@noparse_flag
@click.argument('key_vals', nargs=-1)
@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,
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')
@noninteractive_flag
@noop_flag
@noparse_flag
@click.argument('name', required=True)
@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"""
profile = deepcopy(PROFILE_EMPTY)
if name in config.file.profiles:
profile |= config.file.profiles[name]
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)
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
use_cache = _device_cache_populated and lazy
if not use_cache:
logging.info("Searching PKGBUILDs for device packages")
if not pkgbuilds:
pkgbuilds = discover_pkgbuilds(lazy=lazy, repositories=['device'])
_device_cache.clear()

View file

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