tool v0.5.0 documentation

Command-line interface

«  Application   ::   Contents   ::   Built-in commands  »

Command-line interface

Shell commands subsystem for CLI. A thin wrapper around Argh which is in turn a thin wrapper around argparse (bundled with Python 2.7+ and available as a separate package).

The module also initializes colorama (for cross-platform terminal text output) and exports three of its objects: Fore, Back and Style. If colorama is not available, dummy no-op objects are exported instead.

Overview

Basic usage:

from tool.cli import ArghParser, arg

@arg('word')
def echo(args):
    print 'You said {0}'.format(args.word)

if __name__=='__main__':
    parser = ArghParser()
    parser.add_commands([echo])
    parser.dispatch()

Usage with application manager:

from tool import ApplicationManager
from tool.cli import ArghParser, arg

@arg('word')
def echo(args):
    print 'You said {0}'.format(args.word)

app = ApplicationManager('conf.yaml')

if __name__=='__main__':
    app.add_commands([echo])
    app.dispatch()

You can call tool.application.ApplicationManager.dispatch() instead of dispatch(), they are exactly the same.

To register a command just wrap it in the command() decorator and import the module that contains the command before dispatching.

API reference

class tool.cli.ArghParser(prog=None, usage=None, description=None, epilog=None, version=None, parents=[], formatter_class=<class 'argparse.HelpFormatter'>, prefix_chars='-', fromfile_prefix_chars=None, argument_default=None, conflict_handler='error', add_help=True)

An ArgumentParser suclass which adds a couple of convenience methods.

There is actually no need to subclass the parser. The methods are but wrappers for stand-alone functions add_commands() , autocomplete() and dispatch().

add_commands(*args, **kwargs)
Wrapper for add_commands().
dispatch(*args, **kwargs)
Wrapper for dispatch().
tool.cli.alias(name)

Defines the command name for given function. The alias will be used for the command instead of the original function name.

Note

Currently argparse does not support (multiple) aliases so this decorator actually renames the command. However, in the future it may accept multiple names for the same command.

tool.cli.arg(*args, **kwargs)

Declares an argument for given function. Does not register the function anywhere, not does it modify the function in any way. The signature is exactly the same as that of argparse.ArgumentParser.add_argument(), only some keywords are not required if they can be easily guessed.

Usage:

@arg('path')
@arg('--format', choices=['yaml','json'], default='json')
@arg('--dry-run', default=False)
@arg('-v', '--verbosity', choices=range(0,3), default=1)
def load(args):
    loaders = {'json': json.load, 'yaml': yaml.load}
    loader = loaders[args.format]
    data = loader(args.path)
    if not args.dry_run:
        if 1 < verbosity:
            print('saving to the database')
        put_to_database(data)

Note that:

  • you didn’t have to specify action="store_true" for --dry-run;
  • you didn’t have to specify type=int for --verbosity.
tool.cli.command(func)

Infers argument specifications from given function. Wraps the function in the plain_signature() decorator and also in an arg() decorator for every actual argument the function expects.

Usage:

@command
def foo(bar, quux=123):
    yield bar, quux

This is equivalent to:

@arg('-b', '--bar')
@arg('-q', '--quux', default=123)
def foo(args):
    yield args.bar, args.quux
tool.cli.confirm(action, default=None, skip=False)

A shortcut for typical confirmation prompt.

Parameters:
  • action – a string describing the action, e.g. “Apply changes”. A question mark will be appended.
  • defaultbool or None. Determines what happens when user hits Enter without typing in a choice. If True, default choice is “yes”. If False, it is “no”. If None the prompt keeps reappearing until user types in a choice (not necessarily acceptable) or until the number of iteration reaches the limit. Default is None.
  • skipbool; if True, no interactive prompt is used and default choice is returned (useful for batch mode). Default is False.

Usage:

@arg('key')
@arg('--silent', help='do not prompt, always give default answers')
def delete(args):
    item = db.get(Item, args.key)
    if confirm('Delete '+item.title, default=True, skip=args.silent):
        item.delete()
        print('Item deleted.')
    else:
        print('Operation cancelled.')

Returns None on KeyboardInterrupt event.

exception tool.cli.CommandError

The only exception that is wrapped by the dispatcher. Useful for print-and-exit tasks.

Consider the following example:

def foo(args):
    try:
        ...
    except KeyError as e:
        print(u'Could not fetch item: {0}'.format(e))
        return

It is exactly the same as:

def bar(args):
    try:
        ...
    except KeyError as e:
        raise CommandError(u'Could not fetch item: {0}'.format(e))

This exception can be safely used in both print-style and yield-style commands (see Tutorial).

tool.cli.plain_signature(func)

Marks that given function expects ordinary positional and named arguments instead of a single positional argument (a argparse.Namespace object). Useful for existing functions that you don’t want to alter nor write wrappers by hand. Usage:

@arg('filename')
@plain_signature
def load(filename):
    print json.load(filename)

...is equivalent to:

@argh('filename')
def load(args):
    print json.load(args.filename)

Whether to use the decorator is mostly a matter of taste. Without it the function declaration is more DRY. However, it’s a pure time saver when it comes to exposing a whole lot of existing CLI-agnostic code as a set of commands. You don’t need to rename each and every agrument all over the place; instead, you just stick this and some arg() decorators on top of every function and that’s it.

tool.cli.wrap_errors(*exceptions)

Decorator. Wraps given exceptions into CommandError. Usage:

@arg('-x')
@arg('-y')
@wrap_errors(AssertionError)
def foo(args):
    assert args.x or args.y, 'x or y must be specified'

If the assertion fails, its message will be correctly printed and the stack hidden. This helps to avoid boilerplate code.

«  Application   ::   Contents   ::   Built-in commands  »