Automatically generate a simple CLI.
Automatically generate a simple CLI.
pip install funcli
def main(*args: int):
"""
Adds numbers together.
:param args: some numbers
"""
print("Sum:", sum(args))
if __name__ == '__main__':
import funcli
funcli.main()
# $ python sum.py 1 2 3
# Sum: 6
--help
is autogenerated by the backend (argparse):
$ python sum.py -h
usage: sum.py [-h] [args [args ...]]
Adds numbers together.
positional arguments:
args some numbers
optional arguments:
-h, --help show this help message and exit
from typing import Optional, List
def foo(): pass
def bleep(): pass
def bloop(a, b: int = 0, *c: str, d: Optional[List[float]] = None):
print("Args:", a, b, c, d)
if __name__ == '__main__':
import funcli
funcli.main({ 'foo': foo, 'bar': { bleep, bloop } })
# $ python advanced.py bar bloop arg0 c0 c1 --b=987 --d 42 -0.3
# Args: arg0 987 ('c0', 'c1'), [42, -0.3]
funcli.main(spec = None)
Sugar. spec
defaults to the main
function from the caller's scope.
Calls funcli.run
on spec
, and calls sys.exit
with the return value.
funcli.run(spec, args = None, converters = None)
spec
is either a callable, a sequence of callables, or a dict mapping strings to nested specs
args
default to sys.argv[1:]
converters
is a mapping from types (or whatever you want to use as annotations) to a function that parses a command line argumentGiven functions foo
, bar
, baz
, here are some sample invocations:
funcli.run(foo, ['arg0']) # Calls foo('arg0')
funcli.run({ foo, bar }, ['bar', 'arg0']) # Calls bar('arg0')
funcli.run({ 'beep': foo, 'bloop': [bar, baz] }, ['beep', 'arg0']) # Calls foo('arg0')
funcli.run({ 'beep': foo, 'bloop': [bar, baz] }, ['bloop', 'bar', 'arg0']) # Calls bar('arg0')
bool
argumentsNon-optional bool values should be passed as True
and False
on the command line.
Optional bool values, on the other hand, must be omitted.
def f(warnings: bool = False): ...
funcli.run(f, ['--warnings']) # f(warnings = True)
Note: currently, if the default value is True
, there is no way to pass False
.
Built-in converters handle int
, float
, bool
, and pathlib.Path
. Unannotated args are kept as a str
.
Basic sequence types (list
/List[T]
, set
/Set[T]
, tuple
/Tuple[T, ...]
, Iterable[T]
, Sequence[T]
) are supported, but only as optional arguments, e.g.:
def f(mylist: List[int] = []): ...
funcli.run(f, ['--mylist', '1', '2']) # f([1, 2])
Optional[T]
is supported, but there's currently no way to explicitly pass None
values.
Because of argparse
limitations:
**kwargs
aren't supported; if your function has them, they'll always be emptyf(a = 'default')
has to be invoked as python foo.py --a=nondefault