248 lines
7.8 KiB
Python
248 lines
7.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Translate an options data structure into command line args
|
|
"""
|
|
|
|
# Import python libs
|
|
import sys
|
|
import inspect
|
|
import argparse
|
|
import dict_tools.update
|
|
from typing import Any, Dict, List, Tuple
|
|
|
|
|
|
def __init__(hub):
|
|
hub.config.args.DEFAULT = object()
|
|
|
|
|
|
def _keys(opts):
|
|
"""
|
|
Return the keys in the right order
|
|
"""
|
|
return sorted(opts, key=lambda k: (opts[k].get("display_priority", sys.maxsize), k))
|
|
|
|
|
|
def gather(
|
|
hub, raw: Dict[str, Any], cli: str, parse_cli: bool
|
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
|
"""
|
|
Return the cli arguments as they are parsed
|
|
"""
|
|
if not parse_cli:
|
|
return {}, {}
|
|
raw_cli = hub.config.args.get_cli(raw, cli)
|
|
hub.config.args.init_parser()
|
|
hub.config.args.subparsers(raw, cli)
|
|
hub.config.args.setup(raw_cli)
|
|
cli_args = hub.config.args.parse()
|
|
cli_args = hub.config.args.render(cli_args, raw_cli)
|
|
cli_args = hub.config.args.clean_defaults(cli_args)
|
|
return cli_args, raw_cli
|
|
|
|
|
|
def clean_defaults(hub, cli_args: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
If anyone did not pass in an argument then the key will match the
|
|
bad default and needs to be removed
|
|
"""
|
|
ret = {}
|
|
for key, val in cli_args.items():
|
|
if val is not hub.config.args.DEFAULT:
|
|
ret[key] = val
|
|
|
|
return ret
|
|
|
|
|
|
def init_parser(hub):
|
|
if "parser" not in hub.config.ARGS:
|
|
# Instantiate the parser
|
|
hub.config.ARGS["parser"] = argparse.ArgumentParser()
|
|
|
|
|
|
def get_cli(hub, raw: Dict[str, Any], cli: str) -> Dict[str, Any]:
|
|
"""
|
|
Gather the arguments that need to be parsed by the CLI
|
|
"""
|
|
ret = {}
|
|
main = raw.get(cli, {}).get("CLI_CONFIG")
|
|
main_raw = raw.get(cli, {}).get("CONFIG")
|
|
|
|
for key, data in main.items():
|
|
ret[key] = {}
|
|
dict_tools.update.update(ret[key], data)
|
|
if key in main_raw:
|
|
dict_tools.update.update(ret[key], main_raw[key])
|
|
if "source" in data:
|
|
src = raw.get(data["source"], {}).get("CONFIG", {}).get(key)
|
|
if src is not None:
|
|
dict_tools.update.update(ret[key], src)
|
|
if "default" in ret[key]:
|
|
ret[key]["default"] = hub.config.args.DEFAULT
|
|
ret.update(hub.config.version.CONFIG)
|
|
return ret
|
|
|
|
|
|
def subparsers(hub, raw: Dict[str, Any], cli: str) -> bool:
|
|
"""
|
|
Look over the data and extract and set up the subparsers for subcommands
|
|
"""
|
|
subs = raw.get(cli, {}).get("SUBCOMMANDS")
|
|
if not subs:
|
|
return True
|
|
hub.config.ARGS["sub"] = hub.config.ARGS["parser"].add_subparsers(
|
|
dest="_subparser_"
|
|
)
|
|
hub.config.ARGS["subs"] = {}
|
|
for arg in _keys(subs):
|
|
if arg in ("_argparser_",):
|
|
continue
|
|
comps = subs[arg]
|
|
kwargs = {}
|
|
if "help" in comps:
|
|
kwargs["help"] = comps["help"]
|
|
if "desc" in comps:
|
|
kwargs["description"] = comps["desc"]
|
|
hub.config.ARGS["subs"][arg] = hub.config.ARGS["sub"].add_parser(arg, **kwargs)
|
|
return True
|
|
|
|
|
|
def setup(hub, raw_cli: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Take in a pre-defined dict and translate it to args
|
|
|
|
opts dict:
|
|
<arg>:
|
|
[group]: foo
|
|
[default]: bar
|
|
[action]: store_true
|
|
[options]: # arg will be turned into --arg
|
|
- '-A'
|
|
- '--cheese'
|
|
[choices]:
|
|
- foo
|
|
- bar
|
|
- baz
|
|
[nargs]: +
|
|
[type]: int
|
|
[dest]: cheese
|
|
help: Some great help message
|
|
"""
|
|
# TODO: This should be broken up
|
|
defaults = {}
|
|
groups = {}
|
|
ex_groups = {}
|
|
for arg in _keys(raw_cli):
|
|
if arg in ("_argparser_",):
|
|
continue
|
|
comps = raw_cli[arg]
|
|
positional = comps.pop("positional", False)
|
|
if positional:
|
|
args = [arg]
|
|
else:
|
|
|
|
args = [f"--{arg.replace('_', '-')}"]
|
|
for o_str in comps.get("options", ()):
|
|
if len(o_str) == 1:
|
|
o_str = f"-{o_str}"
|
|
elif not o_str.startswith("-"):
|
|
o_str = f"--{o_str}"
|
|
if o_str not in args:
|
|
args.append(o_str)
|
|
kwargs = {}
|
|
kwargs["action"] = action = comps.get("action", None)
|
|
|
|
if action is None:
|
|
# Non existing option defaults to a StoreAction in argparse
|
|
action = hub.config.ARGS["parser"]._registry_get("action", action)
|
|
|
|
if isinstance(action, str):
|
|
signature = inspect.signature(
|
|
hub.config.ARGS["parser"]._registry_get("action", action).__init__
|
|
)
|
|
else:
|
|
signature = inspect.signature(action.__init__)
|
|
|
|
for param in signature.parameters:
|
|
if param == "self" or param not in comps:
|
|
continue
|
|
if param == "dest":
|
|
kwargs["dest"] = comps.get("dest", arg)
|
|
continue
|
|
if param == "help":
|
|
kwargs["help"] = comps.get("help", "THIS NEEDS SOME DOCUMENTATION!!")
|
|
continue
|
|
if param == "default":
|
|
defaults[comps.get("dest", arg)] = comps[param]
|
|
kwargs[param] = comps[param]
|
|
|
|
if "group" in comps:
|
|
group = comps["group"]
|
|
if group not in groups:
|
|
groups[group] = hub.config.ARGS["parser"].add_argument_group(group)
|
|
groups[group].add_argument(*args, **kwargs)
|
|
continue
|
|
if "ex_group" in comps:
|
|
group = comps["ex_group"]
|
|
if group not in ex_groups:
|
|
ex_groups[group] = hub.config.ARGS[
|
|
"parser"
|
|
].add_mutually_exclusive_group()
|
|
ex_groups[group].add_argument(*args, **kwargs)
|
|
continue
|
|
if "subcommands" in comps:
|
|
subs = comps["subcommands"]
|
|
if not isinstance(subs, list):
|
|
subs = [subs]
|
|
for sub in subs:
|
|
if sub == "_global_":
|
|
if "subs" not in hub.config.ARGS:
|
|
continue
|
|
hub.config.ARGS["parser"].add_argument(*args, **kwargs)
|
|
for named, sparse in hub.config.ARGS["subs"].items():
|
|
sparse.add_argument(*args, **kwargs)
|
|
continue
|
|
sparse = hub.config.ARGS.get("subs", {}).get(sub)
|
|
if not sparse:
|
|
# Maybe raise exception here? Malformed config?
|
|
continue
|
|
sparse.add_argument(*args, **kwargs)
|
|
continue
|
|
hub.config.ARGS["parser"].add_argument(*args, **kwargs)
|
|
return defaults
|
|
|
|
|
|
def parse(
|
|
hub,
|
|
args: List[str] = None,
|
|
namespace: argparse.Namespace = None,
|
|
only_parse_known_arguments: bool = False,
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Parse the command line options
|
|
"""
|
|
if only_parse_known_arguments:
|
|
opts, unknown_args = hub.config.ARGS["parser"].parse_known_args(args, namespace)
|
|
opts_dict = opts.__dict__
|
|
opts_dict["_unknown_args_"] = unknown_args
|
|
else:
|
|
opts = hub.config.ARGS["parser"].parse_args(args, namespace)
|
|
opts_dict = opts.__dict__
|
|
hub.SUBPARSER = opts_dict.get("_subparser_", None)
|
|
return opts_dict
|
|
|
|
|
|
def render(hub, cli_args: Dict[str, Any], raw_cli: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
For options specified as such, take the string passed into the cli and
|
|
render it using the specified render flag
|
|
"""
|
|
for key, val in raw_cli.items():
|
|
if key not in cli_args:
|
|
continue
|
|
if "render" not in val:
|
|
continue
|
|
if val["default"] != cli_args[key]:
|
|
# The value was changed, render it
|
|
cli_args[key] = hub.config.render.init.process(val["render"], cli_args[key])
|
|
return cli_args
|