Package: cli_tools
The command line interface tools module provides several decorators
which can be applied to a regular function to turn it into a console
script.  It is designed to adapt a function so that it can be used as
aconsole_scriptsentrypoint.  The decorators allow various
command line arguments to be declared, and for the command line to be
parsed using theargparsemodule; the results of the parsing are
then passed to the function as regular keyword arguments.  This does
not interfere with the normal calling conventions of the function; it
can be called from Python code directly.Simple usage ofcli_toolsThe simplest example of using thecli_toolsdecorators is as
follows:from cli_tools import *

@console
def function():
    """
    Performs an action.
    """
    ...In this declaration, the function is defined as taking no arguments
(exceptargparse’s default of “–help”).  The description of the
resulting script will be “Performs an action.”To declare this as an actual console script, the following clause will
need to be added to thesetup()call in your setup.py:entry_points={
    'console_scripts': [
        'function = your_module:function.console',
    ],
}Notice in particular the “.console” appended to the function name.
The decorators add several attributes to the function, including the
callableconsole(), which performs the actual command line
argument parsing.The above example is the simplest example, but it would be more
interesting with some defined arguments:@argument('--debug', '-d',
          dest='debug',
          action='store_true',
          default=False,
          help="Run the tool in debug mode.")
@argument('--dryrun', '--dry_run', '--dry-run', '-n',
          dest='dry_run',
          action='store_true',
          default=False,
          help="Perform a dry run.")
def function(dry_run=False):
    """
    Performs an action.
    """
    ...The first thing to notice is the elimination of the@consoledecorator.  It doesn’t hurt anything to use@console, but all the
decorators perform the same core actions; as long as one of the other
decorators is used,@consoleis unnecessary.The second thing to notice is that thedestspecified for the
“–dryrun” option matches the only function argument.  When run as a
console script, the value computed from the command line arguments
will be passed as this keyword parameter.The third thing to notice is that thedestspecified for the
“–debug” option matches no function arguments.  That flag will simply
not be passed to the function.(As it happens, thedebugargument is treated specially.  Under
normal circumstances, if the function raises an exception, the
exception is coerced to a string, printed to standard error, and then
the console script exits.  If thedebugargument isTrue,
however, the exception will not be caught, resulting in a print out of
the stack trace.)Getting a Little More Advanced: Set the DescriptionBy default, the first paragraph of the function docstring becomes the
description for the console script, which is printed out when the
“–help” option is given.  This and several otherargparseoptions
may be overridden using the following decorators:@prog()Overrides theprogparameter passed toargparse.ArgumentParser.  By default, it will beNone,
causing the program name to be derived fromsys.argv[0].@usage()Overrides theusageparameter passed toargparse.ArgumentParser.  By default, it will beNone,
causing the usage message to be automatically computed byargparse.@description()Overrides thedescriptionparameter passed toargparse.ArgumentParser.  By default, it will be the first
paragraph of the function docstring.@epilog()Overrides theepilogparameter passed toargparse.ArgumentParser.  By default, it will beNone; when
given, the text will be output at the end of the help text.@formatter_class()Overrides theformatter_classparameter passed toargparse.ArgumentParser.  By default, it will beargparse.HelpFormatter.  See theargparsedocumentation for
more details.Getting More Advanced: Argument GroupsTheargparsepackage provides the ability to group arguments.
There are two ways of grouping arguments; in the first, related
arguments are simply grouped together so their documentation is more
easily found, while in the second, a group of arguments are identified
as mutually exclusive.  Thecli_toolsdecorators accommodate this
by adding a specialgroupparameter to the@argument()decorator; this group name identifies a group added using the@argument_group()or@mutually_exclusive_group()decorators,
and must be unique.  These latter two decorators take the group name
as the first argument, and remaining keyword arguments are passed to
the underlyingargparse.ArgumentParser.add_argument_group()andargparse.ArgumentParser.add_mutually_exclusive_group()methods.Argument Declaration OrderArguments and groups are constructed in the order in which they appear
in the file; that is, in the earlier example, the “–debug” option
will be added to the argument parser before the “–dryrun” option.
This is opposite the normal decorator rules, but simplifies setting up
the arguments, particularly positional arguments.ProcessorsSome functions can’t act as stand-alone console scripts without some
sort of setup.  For instance, it may be necessary to configure
logging.  This can be handled by declaring a processor:@console
def function():
    """
    Performs an action.
    """
    ...

@function.processor
def _processor(args):
    logging.basicConfig()Here we declare the function_processor()as a processor for the
console scriptfunction().  After the command line arguments are
parsed,_processor()will be called with those arguments; after it
returns,function()will be called.It is also possible to perform actions after the function returns.
Consider the following example:@console
def function():
    """
    Performs an action.
    """
    ...
    return result

@function.processor
def _processor(args):
    result = yield
    print result
    yield NoneHere we turn_processor()into a generator; the result of the
firstyieldstatement is the return value offunction(), which
we can see will be whatever result it computed.  Thus,_processor()will print out that result, then yieldNone–this
is needed so that the script exits without any errors; a non-Nonevalue is interpreted as an error condition by the machinery
surrounding theconsole_scriptsendpoint.Generator-based processors also receive any exceptions thrown by the
function, like so:class BailoutException(Exception):
    pass

@console
def function():
    """
    Performs an action.
    """
    ...
    raise BailoutException("I'm done")

@function.processor
def _processor(args):
    try:
        result = yield
    except BailoutException:
        print "All done!"
    else:
        print "Results so far: %s" % result
    yield NoneNote thetryblock around the firstyield, which allows the
processor function to catch this special exception and do something
appropriate.Argument HooksIt may be necessary to arbitrarily manipulate the argument parser
before parsing the command line arguments.  For instance, a system
which used pluggable authentication modules may need to allow those
modules to add specific command line arguments.  This can be handled
by declaring an argument hook:@console
def function():
    """
    Performs an action.
    """
    ...

@function.args_hook
def _hook(parser):
    parser.add_argument(...)Here we declare the function_hook()as an argument hook for the
console scriptfunction().  After the declared arguments have been
added to the parser, the hook will be called with the parser (anargparse.ArgumentParserinstance), which it can manipulate in any
way.It is also possible to manipulate the parser prior to adding the
declared arguments.  Consider the following example:@console
def function():
    """
    Performs an action.
    """
    ...

@function.args_hook
def _hook(parser):
    parser.add_argument(...)
    yieldHere we turn_hook()into a generator.  The statements preceding
the firstyieldstatement will be run immediately before adding
the declared arguments, and can manipulate the parser in any way
necessary.  If manipulation needs to be done after the declared
arguments are added, that can be done in statements following theyieldstatement.Advancedcli_toolsUsageTheconsole()function added to the decorated function uses
several other functions for setting up the argument parser
(setup_args()), building the keyword arguments to pass to the
underlying function (get_kwargs()), and safely calling the
function and handling exceptions (safe_call()).  These functions
are provided to allow other consumers to make use of the argument
information.  This could be used to build a “Swiss army knife” command
interpreter, for instance.In fact, such “Swiss army knife” command interpreters are supported
directly bycli_tools, through the use of such decorators as@subparsers(),@load_subcommands(), and the@subcommand()argument parser decorator.We begin by showing how to directly declare one function as a
subcommand of another:@console
def function():
    """
    Performs an action.
    """
    pass

@function.subcommand
def subcmd1():
    """
    Performs subcmd1.
    """
    ...

@function.subcommand('sub2')
def subcmd2():
    """
    Performs sub2.
    """
    ...In this example, we have defined two subcommands.  The subcommand
defined bysubcmd1()has a name derived from the function name,
while the subcommand defined bysubcmd2()has its name explicitly
set to “sub2”.To introspect the declared subcommands, use theget_subcommands()function which is also added to the decorated function.  Theget_subcommands()function returns a dictionary mapping the
subcommand name to the function which implements that subcommand.  For
instance, in the example above,function.get_subcommands()would
return the dictionary{"subcmd1": subcmd1, "sub2": subcmd2}.Note that, when using subcommands, the original function will never be
called.  If no subcommand is passed on the command line, the
underlyingargparsemodule reports an error.It is also possible to load subcommands using apkg_resourcesentrypoint group, using the@load_subcommands()decorator like
so:@load_subcommands('example.subcommands')
def function():
    """
    Performs an action.
    """
    ...In this example, all functions listed under the “example.subcommands”
entrypoint group will be added as subcommands offunction(), with
the subcommand name being set to the name of the entrypoint.  For
instance, if the following entrypoint entries existed:entry_points={
    'example.subcommands': [
        'subcmd1 = your_module:subcommand1',
        'subcmd2 = your_module:subcommand2',
        'subcmd3 = other_module:subcommand3',
     ],
}Then in the example above, the three subcommands “subcmd1”, “subcmd2”,
and “subcmd3” would be defined.  (Carefully note that these
entrypoints arenotfollowed by the “.console” that was required in
the “console_scripts” entrypoint.)As a final point, subcommands are handled by calling theargparse.ArgumentParser.add_subparsers()method.  This method can
take certain keyword arguments for nicer rendering of the help text;
to set these arguments, use the@subparsers()decorator:@subparsers(title="My subcommands")
def function():
    """
    Perform an action.
    """
    ...Argument CompletionThe command line interface tools module does not provide any
integration with shell argument completion directly.  However,cli_toolsuses theargparsemodule, and any argument
completion framework that works withargparsecan be used with it.
As an example, consider theargcompletemodule; here’s an example
of how it might be integrated into acli_tools-compatible CLI:from cli_tools import *
import argcomplete

# PYTHON_ARGCOMPLETE_OK

@argument('--debug', '-d',
          dest='debug',
          action='store_true',
          default=False,
          help="Run the tool in debug mode.")
@argument('--dryrun', '--dry_run', '--dry-run', '-n',
          dest='dry_run',
          action='store_true',
          default=False,
          help="Perform a dry run.")
def function(dry_run=False):
    """
    Performs an action.
    """
    ...

@function.args_hook
def _hook(parser):
    argcomplete.autocomplete(parser)Note the use of an argument hook to invoke theargcomplete.autocomplete()function; forargcomplete, this
performs the actual argument completion.  Also note the comment
containingPYTHON_ARGCOMPLETE_OK, which enablesargcomplete’s
global completion mode.For more information aboutargcomplete, see:https://pypi.python.org/pypi/argcomplete