Package: cmcaine-cli
CLIAn extremely easy to use library to generate python CLIs from functions through introspection.Automatically generate the equivalent of this:importargparseparser=argparse.ArgumentParser(description="Generate a cryptographic token with a given entropy.")parser.add_argument('method',nargs='?',default='xkcd',choices=('xkcd','short'))parser.add_argument('entropy',nargs='?',default=70,type=int)args=parser.parse_args()ifargs.method=='xkcd':print(xkcd(args.entropy))else:print(alphanumeric(args.entropy))from this:fromcliimportChoice,clideftoken(method:Choice('xkcd','short')='xkcd',entropy=70):"Generate a cryptographic token with a given entropy."ifmethod=='xkcd':returnxkcd(entropy)else:returnalphanumeric(entropy)cli(token)()Explicitly,cli(token)creates a new function that accepts an array of strings, parsesand automatically convertsthem according to rules derived from the function signature, applies the parsed arguments to the originaltokenand prints the output.cli(token)()calls that function using the defaultsys.argv.But wait, there's more!Keyword arguments (optional or mandatory) are supported as is one varargs argument per function.Arguments will be automatically convertedinto the type of their default argument (if it is not None) or their type annotation.The exact mapping from function signature to argparse rules is specified in the docstring ofgenerate_parser(), but the idea is that it should be fairly intuitive.You can even generate CLIs for a whole module (or any other object with function attributes):importexamplefromcliimportcli,opportunistic,coerce_number# You can use opportunistic(coerce_number) to convert any string that looks# like a number to a number so you don't have to annotate all the functions in a# module. YMMV.cli(example,default_type=opportunistic(coerce_number))()# If you want one, you can get a reference to the current module with# sys.modules[__name__]Lower level APIgenerate_parser(your_function_here)will return anargparse.Parserinstance.apply_namespace(your_function_here, namespace)will apply a namespace object (as returned byparser.parse_args()) to a function.generate_parser_obj(your_module_or_class_here)will return anargparse.Parserwith one subparser per callable on theCode qualityThe code is very short, clearly documented inline and all advertised features are tested.ReferenceFunction signature interpretation:
- (POSITIONAL_ONLY,POSITIONAL_OR_KEYWORD) = positional
- positional with default = optional positional
-KEYWORD_ONLY= options
- defaults = defaults
- Boolean special casing
- If the default isTrueorFalse, the option does not take any
arguments. Instead, if the option is given on the
commandline, the opposite value to the default is given to
the function.
- Example:
```python
def rm(*, force=False):
passcli(rm)(['--force']) # ~== rm(force=True)
	cli(rm)([]) # ~== rm(force=False)
	```
- type annotations = type
- If the `type` is callable, it is called by `argparse` on the relevant substring
- If the type is `bool`, it is replaced by `coerce_bool`
- Provide your own custom function or handle the strings in your
  function body if you need something fancier.Special types:
- cli.Choice
-def foo(x:Choice(1,2))interpreted asadd_argument('x', choices=(1,2), type=int)defexample(positional,arguments):passdefdefaults(normally_one=1):passdeftyped(positional:bool,positional2:int):pass# keyword some-positional-arg --keyword2=foo --keyword1=4defkeyword(positional,*,keyword1=3,keyword2='default_filename'):pass# Mandatory keywords are bad, but you can have them if you want.defmandatory_keywords(positional,*,keyword1,keyword2):passdefvarargs(pos1,pos2,*rest):passdefchoice_from_list(person:Choice('Ann','Bob','Charlie'))pass# `flags --flag` is similar to flags(flag=True)# `flags --inverse_flag` is similar to flags(inverse_flag=False)defflags(*,flag=False,inverse_flag=True):passRelated workOther nice ways to make CLIs:docoptcli2