Usage¶
Defining commands¶
A command is a function decorated with @command
. cltoolbox extracts
as much as information as possible from the function’s docstring and
function signature.
The argparse library is configured by cltoolbox from three different sources.
- Function signature
each argument name
each argument type (from type hints)
- Function docstring (Sphinx, Google, Numpy, or epydoc formats)
function help which is the first paragraph of the docstring and shouldn’t be longer than one line.
function description is taken from the remaining paragraphs up until the documentation of arguments.
each argument help
The
cltoolbox.arg
decorator which overrides or appends to any of the information found in the function signature or docstring.
Note that the argument types in the function docstring are ignored and do not override the type hints found in the function signature.
For example, this program uses Sphinx formatted docstring:
from cltoolbox import command, main
@command
def cmd(foo, bar):
"""Here stands the help.
And here the description of this useless command.
:param foo: Well, the first arg.
:param bar: Obviously the second arg. Nonsense."""
print(foo, bar)
if __name__ == "__main__":
main()
The cltoolbox will take the previous function and docstring to create the following documentation available at the command line.
$ python command.py -h
usage: command.py [-h] {cmd} ...
positional arguments:
{cmd}
cmd Here stands the help.
optional arguments:
-h, --help show this help message and exit
$ python command.py cmd -h
usage: command.py cmd [-h] foo bar
And here the description of this useless command.
positional arguments:
foo Well, the first arg.
bar Obviously the second arg. Nonsense.
optional arguments:
-h, --help show this help message and exit
Overriding arguments with @arg
¶
You may need to specify some argument to argparse, and it is not possible to
include in the docstring or function signature. cltoolbox provides the
@arg
decorator to accomplish this. Its signature is as follows:
@arg(arg_name, *args, **kwargs)
, where arg_name
must be among the
function’s arguments, while the remaining arguments will be directly passed to
argparse.add_argument()
.
Note that this decorator will override other arguments that cltoolbox inferred either from the function signature or from the docstring.
The following are the argparse.add_argument()
positional and keyword
arguments:
Positional (if used must be first in @arg
¶
- name or flags
Either a name or a list of option strings, e.g. foo or -f, –foo.
Keyword arguments¶
- action
The basic type of action to be taken when this argument is encountered at the command line.
- nargs
The number of command-line arguments that should be consumed.
- const
A constant value required by some action and nargs selections.
- default
The value produced if the argument is absent from the command line and if it is absent from the namespace object.
- type
The type to which the command-line argument should be converted.
- choices
A container of the allowable values for the argument.
- required
Whether or not the command-line option may be omitted (optionals only).
- help
A brief description of what the argument does.
- metavar
A name for the argument in usage messages.
- dest
The name of the attribute to be added to the object returned by parse_args().
Long and short options (flags)¶
The @arg
decorator is useful for allowing long and short options for the
keyword arguments.
Example:
from cltoolbox import command, main, arg
@command
@arg("spam", "--spam", "-s")
def ex(foo, b=None, spam=None):
"""Nothing interesting.
:param foo: Bla bla.
:param b: A little flag.
:param spam: Spam spam spam spam."""
print((foo, b, spam))
if __name__ == "__main__":
main()
Usage:
$ python short_options.py ex -h
usage: short_options.py ex [-h] [-b B] [--spam SPAM] foo
positional arguments:
foo Bla bla.
optional arguments:
-h, --help show this help message and exit
-b B A little flag.
--spam SPAM, -s SPAM Spam spam spam spam.
$ python short_options.py ex 2
('2', None, None)
$ python short_options.py ex 2 -b 8
('2', '8', None)
$ python short_options.py ex 2 -b 8 -s 9
('2', '8', '9')
$ python short_options.py ex 2 -b 8 --spam 9
('2', '8', '9')
How default arguments are handled¶
If an argument has a default, then cltoolbox takes it as an optional argument, while those which do not have a default are interpreted as positional arguments. Here are the actions taken by cltoolbox when a default argument is encountered:
Default argument type |
What cltoolbox specifies in |
---|---|
bool |
action |
list |
action |
int |
type |
float |
type |
str |
type |
So, for example, if a default argument is an integer, cltoolbox will automatically
convert command line arguments to int()
:
from cltoolbox import command, main
@command
def po(a=2, b=3):
print(a ** b)
if __name__ == "__main__":
main()
$ python default_args.py po -h
usage: default_args.py po [-h] [-a A] [-b B]
optional arguments:
-h, --help show this help message and exit
-a A
-b B
$ python default_args.py po -a 4 -b 9
262144
Note that passing the arguments positionally does not work, because
argparse
expects optional args and a
and b
are already filled with
defaults:
$ python default_args.py po
8
$ python default_args.py po 9 8
usage: default_args.py [-h] {po} ...
default_args.py: error: unrecognized arguments: 9 8
To overcome this, cltoolbox allows you to specify positional arguments’ types in the type hints, as explained in the next section.
Adding type¶
This is especially useful for positional arguments, but it can be used for all type of arguments.
Adding type in the signature¶
The cltoolbox can use type annotations to convert argument types.
Simple usage:
from cltoolbox import command, main, arg
@command
@arg("mod", "--mod", "-m")
def pow(a:float, b:float, mod:int=None):
"""Mimic Python's pow() function.
:param a: The base.
:param b: The exponent.
:param mod: Modulus."""
if mod is not None:
print((a ** b) % mod)
else:
print(a ** b)
if __name__ == "__main__":
main()
$ python types.py pow -h
usage: types.py pow [-h] [-m <int>] a b
Mimic Python's pow() function.
positional arguments:
a The base.
b The exponent.
optional arguments:
-h, --help show this help message and exit
-m <int>, --mod <int>
Modulus.
$ python types.py pow 5 8
390625.0
$ python types.py pow 4.5 8.3
264036.437449
Since type annotations can be any callable, this allows more flexibility to convert what is given on the command line.
$ python types.py pow 5 8 -m 8
1.0
from cltoolbox import command, main
# Note: don't actually do this.
def double_int(n):
return int(n) * 2
@command
def dup(string, times: double_int):
"""
Duplicate text.
:param string: The text to duplicate.
:param times: How many times to duplicate.
"""
print(string * times)
if __name__ == "__main__":
main()
$ python3 dup_type_hints.py dup "test " 2
test test test test
$ python3 dup_type_hints.py dup "test " foo
usage: dup_type_hints.py dup [-h] string times
dup_type_hints.py dup: error: argument times: invalid double_int value: 'foo'
@command
Arguments¶
There are two special arguments to the @command()
decorator to allow for
special processing for the decorated function. The first argument, also
available as keyword name='alias_name'
will allow for an alias of the
command. The second is only available as keyword
formatter_class='argparse_formatter_class'
to format the display of the
docstring.
Aliasing Commands¶
A common use-case for this is represented by a function with underscores in it.
Usually commands have dashes instead. So, you may specify the aliasing name to
the @command()
decorator, this way:
@command('very-powerful-cmd')
def very_powerful_cmd(arg, verbose=False):
pass
And call it as follows:
$ python prog.py very-powerful-cmd 2 --verbose
Note that the original name will be discarded and won’t be usable.
Docstring Formats¶
There are three commonly accepted formats for docstrings. The Sphinx or
Restructured Text (REST) is the Python default, and the other two common styles are
numpy
and google
. cltoolbox will auto-detect the style used.
An example of using a Numpy formatted docstring in cltoolbox:
@command
def simple_numpy_docstring(arg1, arg2="string"):
'''One line summary.
Extended description.
Parameters
----------
arg1 : int
Description of `arg1`
arg2 : str
Description of `arg2`
Returns
-------
str
Description of return value.
'''
return int(arg1) * arg2
An example of using a Google formatted docstring in cltoolbox:
@command
def simple_google_docstring(arg1, arg2="string"):
'''One line summary.
Extended description.
Args:
arg1(int): Description of `arg1`
arg2(str): Description of `arg2`
Returns:
str: Description of return value.
'''
return int(arg1) * arg2
Formatter Class¶
For the help display there is the opportunity to use special formatters. Any argparse compatible formatter class can be used. There is an alternative formatter class available with cltoolbox that will display on ANSI terminals.
The ANSI formatter class has to be imported from cltoolbox and used as follows:
from cltoolbox.rst_text_formatter import RSTHelpFormatter
@command(formatter_class=RSTHelpFormatter)
def pow(a:float, b:float, mod:int=None):
'''Mimic Python's pow() function.
:param a: The base.
:param b: The exponent.
:param mod: Modulus.'''
if mod is not None:
print((a ** b) % mod)
else:
print(a ** b)
Shell autocompletion¶
The cltoolbox supports autocompletion via the optional dependency
argcomplete
. If that package is installed, cltoolbox detects it
automatically without the need to
do anything else.