diff --git a/dtk/__init__.py b/dtk/__init__.py index fbc18ba..78c9094 100644 --- a/dtk/__init__.py +++ b/dtk/__init__.py @@ -2,4 +2,5 @@ # encoding: utf-8 from . import argv +from . import aux from . import cfg diff --git a/dtk/argv.py b/dtk/argv.py index 6b3cf30..1b9ee5f 100644 --- a/dtk/argv.py +++ b/dtk/argv.py @@ -3,12 +3,14 @@ import sys +from . import aux + class Action: """ A callable with signature: - p_args is a list of (name, type) tuples - kv_args is a dict with {name: (type, default value), ...} + p_args is a list of (name, type, desc) tuples + kv_args is a dict with {name: (type, default value, desc), ...} """ p_args = [] @@ -94,10 +96,10 @@ class Invocation: return # pre-populate args - for name, T in self.action.p_args: + for name, *_ in self.action.p_args: self.args[name] = None - for name, (T, default) in self.action.kv_args.items(): + for name, (T, default, _) in self.action.kv_args.items(): self.args[name] = T(default) # process positional (mandatory) args @@ -107,7 +109,7 @@ class Invocation: if not len(raw_p_args) == count_p_args: raise IncompleteInvocation - for raw, (name, T) in zip(raw_p_args, self.action.p_args): + for raw, (name, T, _) in zip(raw_p_args, self.action.p_args): try: self.args[name] = T(raw) except: @@ -131,35 +133,44 @@ class Invocation: def execute(self): self.action(self.cmd, **self.args) - -# ~ $ nicectl customer create NAME [--note=xxx] - -# ~ $ nicectl user create CUSTOMER LOGIN -# ~ generated password is printed - -# ~ $ nicectl unit list - -# ~ $ nicectl unit create CUSTOMER NAME MODEL [--serial=XXX] [--note=xxx] - -# ~ routing = { - # ~ "customer": { - # ~ "create": customer_create, - # ~ "list": customer_list - # ~ }, - # ~ "user": { - # ~ "create": user_create, - # ~ "list": user_list - # ~ }, - # ~ "unit": { - # ~ "create": unit_create, - # ~ "list": unit_list - # ~ } -# ~ } + + def help(self, stream=sys.stdout): + """ + Prints auto-generated usage information. + """ + + print("Usage:") + + for cmd, act in aux.flatten_dict(self.routing): + synopsis = ["$", sys.argv[0], cmd] + args = [] + + for name, T, desc in act.p_args: + synopsis.append(name) + args.append("* %s (%s) = %s" %(name, T.__name__, desc)) + + if act.kv_args: + synopsis.append("[") + + for name, (T, default, desc) in act.kv_args.items(): + synopsis.append("%s=%s" %(name, default or "…")) + args.append("+ %s (%s) = %s" %(name, T.__name__, desc)) + + synopsis.append("]") + + print(" " + " ".join(synopsis)) + for arg in args: + print(" " + arg) + + print() + print(" " + act.__doc__.strip()) + + print() if __name__ == "__main__": class TestActionHello(Action): - p_args = [("NAME", str), ("AGE", int)] - kv_args = {"speed": (float, 75)} + p_args = [("NAME", str, "description"), ("AGE", int, "another short piece of wisdom")] + kv_args = {"speed": (float, 75, "more stuff")} def __call__(self, cmd, NAME, AGE, speed): print("Hello %s, age %d." %(NAME, AGE)) diff --git a/dtk/aux.py b/dtk/aux.py new file mode 100644 index 0000000..687fc10 --- /dev/null +++ b/dtk/aux.py @@ -0,0 +1,15 @@ +#! /usr/bin/env python3 +# encoding: utf-8 + +def flatten_dict(root, glue=" ", prefix=[]): + lines = [] + + for k, v in root.items(): + new_prefix = prefix + [k] + + if type(v) == dict: + lines.extend(flatten_dict(v, glue, new_prefix)) + else: + lines.append((glue.join(new_prefix), v)) + + return lines