cliff(1)

CLIFF(1) cliff CLIFF(1)

NAME

cliff - cliff Documentation

cliff is a framework for building command line programs. It uses plugins to define sub-commands, output formatters, and other extensions.

INSTALLATION

Python Versions

cliff is being developed under Python 3.

Basic Installation

cliff should be installed into the same site-packages area where the application and extensions are installed (either a virtualenv or the global site-packages). You may need administrative privileges to do that. The easiest way to install it is using pip. For example:

$ pip install cliff


Source Code

The source is hosted on OpenDev: https://opendev.org/openstack/cliff

Reporting Bugs

Please report bugs through the storyboard: https://storyboard.openstack.org/#!/project/openstack/cliff

USING CLIFF

Introduction

The cliff framework is meant to be used to create multi-level commands such as subversion and git, where the main program handles some basic argument parsing and then invokes a sub-command to do the work.

Command Plugins

Cliff takes advantage of Python's ability to load code dynamically to allow the sub-commands of a main program to be implemented, packaged, and distributed separately from the main program. This organization provides a unified view of the command for users, while giving developers the opportunity organize source code in any way they see fit.

Cliff Objects

Cliff is organized around five objects that are combined to create a useful command line program.

The Application

An cliff.app.App is the main program that you run from the shell command prompt. It is responsible for global operations that apply to all of the commands, such as configuring logging and setting up I/O streams.

The CommandManager

The cliff.commandmanager.CommandManager knows how to load individual command plugins. The default implementation uses entry points but any mechanism for loading commands can be used by replacing the default CommandManager when instantiating an App.

The Command

The cliff.command.Command class is where the real work happens. The rest of the framework is present to help the user discover the command plugins and invoke them, and to provide runtime support for those plugins. Each Command subclass is responsible for taking action based on instructions from the user. It defines its own local argument parser (usually using argparse) and a take_action() method that does the appropriate work.

The CommandHook

The cliff.hooks.CommandHook class can extend a Command by modifying the command line arguments available, for example to add options used by a driver. Each CommandHook subclass must implement the full hook API, defined by the base class. Extensions should be registered using an entry point namespace based on the application namespace and the command name:

application_namespace + '.' + command_name.replace(' ', '_')


The Interactive Application

The main program uses an cliff.interactive.InteractiveApp instance to provide a command-shell mode in which the user can type multiple commands before the program exits. Many cliff-based applications will be able to use the default implementation of InteractiveApp without subclassing it.

Exploring the Demo App

The cliff source package includes a demoapp directory containing an example main program with several command plugins.

Setup

To install and experiment with the demo app you should create a virtual environment and activate it. This will make it easy to remove the app later, since it doesn't do anything useful and you aren't likely to want to hang onto it after you understand how it works:

$ pip install virtualenv
$ virtualenv .venv
$ . .venv/bin/activate
(.venv)$


Next, install cliff in the same environment:

(.venv)$ python setup.py install


Finally, install the demo application into the virtual environment:

(.venv)$ cd demoapp
(.venv)$ python setup.py install


Usage

Both cliff and the demo installed, you can now run the command cliffdemo.

For basic command usage instructions and a list of the commands available from the plugins, run:

(.venv)$ cliffdemo -h


or:

(.venv)$ cliffdemo --help


Run the simple command by passing its name as argument to cliffdemo:

(.venv)$ cliffdemo simple


The simple command prints this output to the console:

sending greeting
hi!


To see help for an individual command, use the help command:

(.venv)$ cliffdemo help files


or the --help option:

(.venv)$ cliffdemo files --help


For more information, refer to the autogenerated documentation below.

The Source

The cliffdemo application is defined in a cliffdemo package containing several modules.

main.py

The main application is defined in main.py:

import sys
from cliff.app import App
from cliff.commandmanager import CommandManager
class DemoApp(App):

def __init__(self):
super(DemoApp, self).__init__(
description='cliff demo app',
version='0.1',
command_manager=CommandManager('cliff.demo'),
deferred_help=True,
)
def initialize_app(self, argv):
self.LOG.debug('initialize_app')
def prepare_to_run_command(self, cmd):
self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)
def clean_up(self, cmd, result, err):
self.LOG.debug('clean_up %s', cmd.__class__.__name__)
if err:
self.LOG.debug('got an error: %s', err) def main(argv=sys.argv[1:]):
myapp = DemoApp()
return myapp.run(argv) if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))


The DemoApp class inherits from App and overrides __init__() to set the program description and version number. It also passes a CommandManager instance configured to look for plugins in the cliff.demo namespace.

The initialize_app() method of DemoApp will be invoked after the main program arguments are parsed, but before any command processing is performed and before the application enters interactive mode. This hook is intended for opening connections to remote web services, databases, etc. using arguments passed to the main application.

The prepare_to_run_command() method of DemoApp will be invoked after a command is identified, but before the command is given its arguments and run. This hook is intended for pre-command validation or setup that must be repeated and cannot be handled by initialize_app().

The clean_up() method of DemoApp is invoked after a command runs. If the command raised an exception, the exception object is passed to clean_up(). Otherwise the err argument is None.

The main() function defined in main.py is registered as a console script entry point so that DemoApp can be run from the command line (see the discussion of setup.py below).

simple.py

Two commands are defined in simple.py:

import logging
from cliff.command import Command
class Simple(Command):

"A simple command that prints a message."
log = logging.getLogger(__name__)
def take_action(self, parsed_args):
self.log.info('sending greeting')
self.log.debug('debugging')
self.app.stdout.write('hi!\n') class Error(Command):
"Always raises an error"
log = logging.getLogger(__name__)
def take_action(self, parsed_args):
self.log.info('causing error')
raise RuntimeError('this is the expected exception')


Simple demonstrates using logging to emit messages on the console at different verbose levels:

(.venv)$ cliffdemo simple
sending greeting
hi!
(.venv)$ cliffdemo -v simple
prepare_to_run_command Simple
sending greeting
debugging
hi!
clean_up Simple
(.venv)$ cliffdemo -q simple
hi!


Error always raises a RuntimeError exception when it is invoked, and can be used to experiment with the error handling features of cliff:

(.venv)$ cliffdemo error
causing error
ERROR: this is the expected exception
(.venv)$ cliffdemo -v error
prepare_to_run_command Error
causing error
ERROR: this is the expected exception
clean_up Error
got an error: this is the expected exception
(.venv)$ cliffdemo --debug error
causing error
this is the expected exception
Traceback (most recent call last):

File ".../cliff/app.py", line 218, in run_subcommand
result = cmd.run(parsed_args)
File ".../cliff/command.py", line 43, in run
self.take_action(parsed_args)
File ".../demoapp/cliffdemo/simple.py", line 24, in take_action
raise RuntimeError('this is the expected exception') RuntimeError: this is the expected exception Traceback (most recent call last):
File "/Users/dhellmann/Envs/cliff/bin/cliffdemo", line 9, in <module>
load_entry_point('cliffdemo==0.1', 'console_scripts', 'cliffdemo')()
File ".../demoapp/cliffdemo/main.py", line 33, in main
return myapp.run(argv)
File ".../cliff/app.py", line 160, in run
result = self.run_subcommand(remainder)
File ".../cliff/app.py", line 218, in run_subcommand
result = cmd.run(parsed_args)
File ".../cliff/command.py", line 43, in run
self.take_action(parsed_args)
File ".../demoapp/cliffdemo/simple.py", line 24, in take_action
raise RuntimeError('this is the expected exception') RuntimeError: this is the expected exception


list.py

list.py includes a single command derived from cliff.lister.Lister which prints a list of the files in the current directory.

import logging
import os
from cliff.lister import Lister
class Files(Lister):

"""Show a list of files in the current directory.
The file name and size are printed by default.
"""
log = logging.getLogger(__name__)
def take_action(self, parsed_args):
return (('Name', 'Size'),
((n, os.stat(n).st_size) for n in os.listdir('.'))
)


Files prepares the data, and Lister manages the output formatter and printing the data to the console:

(.venv)$ cliffdemo files
+---------------+------+
|      Name     | Size |
+---------------+------+
| build         |  136 |
| cliffdemo.log | 2546 |
| Makefile      | 5569 |
| source        |  408 |
+---------------+------+
(.venv)$ cliffdemo files -f csv
"Name","Size"
"build",136
"cliffdemo.log",2690
"Makefile",5569
"source",408


show.py

show.py includes a single command derived from cliff.show.ShowOne which prints the properties of the named file.

import logging
import os
from cliff.show import ShowOne
class File(ShowOne):

"Show details about a file"
log = logging.getLogger(__name__)
def get_parser(self, prog_name):
parser = super(File, self).get_parser(prog_name)
parser.add_argument('filename', nargs='?', default='.')
return parser
def take_action(self, parsed_args):
stat_data = os.stat(parsed_args.filename)
columns = ('Name',
'Size',
'UID',
'GID',
'Modified Time',
)
data = (parsed_args.filename,
stat_data.st_size,
stat_data.st_uid,
stat_data.st_gid,
stat_data.st_mtime,
)
return (columns, data)


File prepares the data, and ShowOne manages the output formatter and printing the data to the console:

(.venv)$ cliffdemo file setup.py
+---------------+--------------+
|     Field     |    Value     |
+---------------+--------------+
| Name          | setup.py     |
| Size          | 5825         |
| UID           | 502          |
| GID           | 20           |
| Modified Time | 1335569964.0 |
+---------------+--------------+


setup.py

The demo application is packaged using setuptools.

#!/usr/bin/env python
from setuptools import find_packages
from setuptools import setup
PROJECT = 'cliffdemo'
# Change docs/sphinx/conf.py too!
VERSION = '0.1'
try:

long_description = open('README.rst', 'rt').read() except IOError:
long_description = '' setup(
name=PROJECT,
version=VERSION,
description='Demo app for cliff',
long_description=long_description,
author='Doug Hellmann',
author_email='doug.hellmann@gmail.com',
url='https://github.com/openstack/cliff',
download_url='https://github.com/openstack/cliff/tarball/master',
classifiers=[
'Development Status :: 3 - Alpha',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Intended Audience :: Developers',
'Environment :: Console',
],
platforms=['Any'],
scripts=[],
provides=[],
install_requires=['cliff'],
namespace_packages=[],
packages=find_packages(),
include_package_data=True,
entry_points={
'console_scripts': [
'cliffdemo = cliffdemo.main:main'
],
'cliff.demo': [
'simple = cliffdemo.simple:Simple',
'two_part = cliffdemo.simple:Simple',
'error = cliffdemo.simple:Error',
'list files = cliffdemo.list:Files',
'files = cliffdemo.list:Files',
'file = cliffdemo.show:File',
'show file = cliffdemo.show:File',
'unicode = cliffdemo.encoding:Encoding',
'hooked = cliffdemo.hook:Hooked',
],
'cliff.demo.hooked': [
'sample-hook = cliffdemo.hook:Hook',
],
},
zip_safe=False, )


The important parts of the packaging instructions are the entry_points settings. All of the commands are registered in the cliff.demo namespace. Each main program should define its own command namespace so that it only loads the command plugins that it should be managing.

Command Extension Hooks

Individual subcommands of an application can be extended via hooks registered as separate plugins. In the demo application, the hooked command has a single extension registered.

The namespace for hooks is a combination of the application namespace and the command name. In this case, the application namespace is cliff.demo and the command is hooked, so the extension namespace is cliff.demo.hooked. If the subcommand name includes spaces, they are replaced with underscores ("_") to build the namespace.

# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
import logging
from cliff.command import Command
from cliff.hooks import CommandHook
class Hooked(Command):

"A command to demonstrate how the hooks work"
log = logging.getLogger(__name__)
def take_action(self, parsed_args):
self.app.stdout.write('this command has an extension\n') class Hook(CommandHook):
"""Hook sample for the 'hooked' command.
This would normally be provided by a separate package from the
main application, but is included in the demo app for simplicity.
"""
def get_parser(self, parser):
print('sample hook get_parser()')
parser.add_argument('--added-by-hook')
return parser
def get_epilog(self):
return 'extension epilog text'
def before(self, parsed_args):
self.cmd.app.stdout.write('before\n')
def after(self, parsed_args, return_code):
self.cmd.app.stdout.write('after\n')


Although the hooked command does not add any arguments to the parser it creates, the help output shows that the extension adds a single --added-by-hook option.

(.venv)$ cliffdemo hooked -h
sample hook get_parser()
usage: cliffdemo hooked [-h] [--added-by-hook ADDED_BY_HOOK]
A command to demonstrate how the hooks work
optional arguments:

-h, --help show this help message and exit
--added-by-hook ADDED_BY_HOOK extension epilog text (.venv)$ cliffdemo hooked sample hook get_parser() before this command has an extension after


SEE ALSO:

cliff.hooks.CommandHook -- The API for command hooks.


Autogenerated Documentation

The following documentation is generated using the following directive, which is provided by the cliff Sphinx extension.

.. autoprogram-cliff:: cliffdemo.main.DemoApp

:application: cliffdemo .. autoprogram-cliff:: cliff.demo
:application: cliffdemo


Output

Global Options

cliff demo app

cliffdemo [--version] [-v | -q] [--log-file LOG_FILE] [--debug]


--version
show program's version number and exit

-v, --verbose
Increase verbosity of output. Can be repeated.

-q, --quiet
Suppress output except warnings and errors.

--log-file <LOG_FILE>
Specify a file to log output. Disabled by default.

--debug
Show tracebacks on errors.

Command Options

error

Always raises an error

cliffdemo error


file

Show details about a file

cliffdemo file

[-f {json,shell,table,value,yaml}]
[-c COLUMN]
[--noindent]
[--prefix PREFIX]
[--max-width <integer>]
[--fit-width]
[--print-empty]
[filename]


-f <FORMATTER>, --format <FORMATTER>
the output format, defaults to table

-c COLUMN, --column COLUMN
specify the column(s) to include, can be repeated to show multiple columns

--noindent
whether to disable indenting the JSON

--prefix <PREFIX>
add a prefix to all variable names

--max-width <integer>
Maximum display width, <1 to disable. You can also use the CLIFF_MAX_TERM_WIDTH environment variable, but the parameter takes precedence.

--fit-width
Fit the table to the display width. Implied if --max-width greater than 0. Set the environment variable CLIFF_FIT_WIDTH=1 to always enable

--print-empty
Print empty table if there is no data to show.

filename

files

Show a list of files in the current directory.

The file name and size are printed by default.

cliffdemo files

[-f {csv,json,table,value,yaml}]
[-c COLUMN]
[--quote {all,minimal,none,nonnumeric}]
[--noindent]
[--max-width <integer>]
[--fit-width]
[--print-empty]
[--sort-column SORT_COLUMN]
[--sort-ascending | --sort-descending]


-f <FORMATTER>, --format <FORMATTER>
the output format, defaults to table

-c COLUMN, --column COLUMN
specify the column(s) to include, can be repeated to show multiple columns

--quote <QUOTE_MODE>
when to include quotes, defaults to nonnumeric

--noindent
whether to disable indenting the JSON

--max-width <integer>
Maximum display width, <1 to disable. You can also use the CLIFF_MAX_TERM_WIDTH environment variable, but the parameter takes precedence.

--fit-width
Fit the table to the display width. Implied if --max-width greater than 0. Set the environment variable CLIFF_FIT_WIDTH=1 to always enable

--print-empty
Print empty table if there is no data to show.

--sort-column SORT_COLUMN
specify the column(s) to sort the data (columns specified first have a priority, non-existing columns are ignored), can be repeated

--sort-ascending
sort the column(s) in ascending order

--sort-descending
sort the column(s) in descending order

hooked

A command to demonstrate how the hooks work

cliffdemo hooked


list files

Show a list of files in the current directory.

The file name and size are printed by default.

cliffdemo list files

[-f {csv,json,table,value,yaml}]
[-c COLUMN]
[--quote {all,minimal,none,nonnumeric}]
[--noindent]
[--max-width <integer>]
[--fit-width]
[--print-empty]
[--sort-column SORT_COLUMN]
[--sort-ascending | --sort-descending]


-f <FORMATTER>, --format <FORMATTER>
the output format, defaults to table

-c COLUMN, --column COLUMN
specify the column(s) to include, can be repeated to show multiple columns

--quote <QUOTE_MODE>
when to include quotes, defaults to nonnumeric

--noindent
whether to disable indenting the JSON

--max-width <integer>
Maximum display width, <1 to disable. You can also use the CLIFF_MAX_TERM_WIDTH environment variable, but the parameter takes precedence.

--fit-width
Fit the table to the display width. Implied if --max-width greater than 0. Set the environment variable CLIFF_FIT_WIDTH=1 to always enable

--print-empty
Print empty table if there is no data to show.

--sort-column SORT_COLUMN
specify the column(s) to sort the data (columns specified first have a priority, non-existing columns are ignored), can be repeated

--sort-ascending
sort the column(s) in ascending order

--sort-descending
sort the column(s) in descending order

show file

Show details about a file

cliffdemo show file

[-f {json,shell,table,value,yaml}]
[-c COLUMN]
[--noindent]
[--prefix PREFIX]
[--max-width <integer>]
[--fit-width]
[--print-empty]
[filename]


-f <FORMATTER>, --format <FORMATTER>
the output format, defaults to table

-c COLUMN, --column COLUMN
specify the column(s) to include, can be repeated to show multiple columns

--noindent
whether to disable indenting the JSON

--prefix <PREFIX>
add a prefix to all variable names

--max-width <integer>
Maximum display width, <1 to disable. You can also use the CLIFF_MAX_TERM_WIDTH environment variable, but the parameter takes precedence.

--fit-width
Fit the table to the display width. Implied if --max-width greater than 0. Set the environment variable CLIFF_FIT_WIDTH=1 to always enable

--print-empty
Print empty table if there is no data to show.

filename

simple

A simple command that prints a message.

cliffdemo simple


two part

A simple command that prints a message.

cliffdemo two part


unicode

Show some unicode text

cliffdemo unicode

[-f {csv,json,table,value,yaml}]
[-c COLUMN]
[--quote {all,minimal,none,nonnumeric}]
[--noindent]
[--max-width <integer>]
[--fit-width]
[--print-empty]
[--sort-column SORT_COLUMN]
[--sort-ascending | --sort-descending]


-f <FORMATTER>, --format <FORMATTER>
the output format, defaults to table

-c COLUMN, --column COLUMN
specify the column(s) to include, can be repeated to show multiple columns

--quote <QUOTE_MODE>
when to include quotes, defaults to nonnumeric

--noindent
whether to disable indenting the JSON

--max-width <integer>
Maximum display width, <1 to disable. You can also use the CLIFF_MAX_TERM_WIDTH environment variable, but the parameter takes precedence.

--fit-width
Fit the table to the display width. Implied if --max-width greater than 0. Set the environment variable CLIFF_FIT_WIDTH=1 to always enable

--print-empty
Print empty table if there is no data to show.

--sort-column SORT_COLUMN
specify the column(s) to sort the data (columns specified first have a priority, non-existing columns are ignored), can be repeated

--sort-ascending
sort the column(s) in ascending order

--sort-descending
sort the column(s) in descending order

List Commands

One of the most common patterns with command line programs is the need to print lists of data. cliff provides a base class for commands of this type so that they only need to prepare the data, and the user can choose from one of several output formatter plugins to see the list of data in their preferred format.

Lister

The cliff.lister.Lister base class API extends Command to allow take_action() to return data to be formatted using a user-selectable formatter. Subclasses should provide a take_action() implementation that returns a two member tuple containing a tuple with the names of the columns in the dataset and an iterable that will yield the data to be output. See the description of the files command in the demoapp for details.

List Output Formatters

cliff is delivered with two output formatters for list commands. Lister adds a command line switch to let the user specify the formatter they want, so you don't have to do any extra work in your application.

csv

The csv formatter produces a comma-separated-values document as output. CSV data can be imported into a database or spreadsheet for further manipulation.

(.venv)$ cliffdemo files -f csv
"Name","Size"
"build",136
"cliffdemo.log",2690
"Makefile",5569
"source",408


table

The table formatter uses PrettyTable to produce output formatted for human consumption.

(.venv)$ cliffdemo files
+---------------+------+
|      Name     | Size |
+---------------+------+
| build         |  136 |
| cliffdemo.log | 2546 |
| Makefile      | 5569 |
| source        |  408 |
+---------------+------+


value

The value formatter produces a space separated output with no headers.

(.venv)$ cliffdemo files -f value
build 136
cliffdemo.log 2690
Makefile 5569
source 408


This format can be very convenient when you want to pipe the output to a script.

(.venv)$ cliffdemo files -f value | while read NAME SIZE
do

echo $NAME is $SIZE bytes done build is 136 bytes cliffdemo.log is 2690 bytes Makefile is 5569 bytes source is 408 bytes


yaml

The yaml formatter uses PyYAML to produce a YAML sequence of mappings.

(.venv)$ cliffdemo files -f yaml
- Name: dist

Size: 4096 - Name: cliffdemo.egg-info
Size: 4096 - Name: README.rst
Size: 960 - Name: setup.py
Size: 1807 - Name: build
Size: 4096 - Name: cliffdemo
Size: 4096


json

The json formatter produces an array of objects in indented JSON format.

(.venv)$ cliffdemo files -f json
[

{
"Name": "source",
"Size": 4096
},
{
"Name": "Makefile",
"Size": 5569
},
{
"Name": "build",
"Size": 4096
} ]


Other Formatters

A formatter using tablib to produce HTML is available as part of cliff-tablib.

Creating Your Own Formatter

If the standard formatters do not meet your needs, you can bundle another formatter with your program by subclassing from cliff.formatters.base.ListFormatter and registering the plugin in the cliff.formatter.list namespace.

Show Commands

One of the most common patterns with command line programs is the need to print properties of objects. cliff provides a base class for commands of this type so that they only need to prepare the data, and the user can choose from one of several output formatter plugins to see the data in their preferred format.

ShowOne

The cliff.show.ShowOne base class API extends Command to allow take_action() to return data to be formatted using a user-selectable formatter. Subclasses should provide a take_action() implementation that returns a two member tuple containing a tuple with the names of the columns in the dataset and an iterable that contains the data values associated with those names. See the description of the file command in the demoapp for details.

Show Output Formatters

cliff is delivered with output formatters for show commands. ShowOne adds a command line switch to let the user specify the formatter they want, so you don't have to do any extra work in your application.

table

The table formatter uses PrettyTable to produce output formatted for human consumption. This is the default formatter.

(.venv)$ cliffdemo file setup.py
+---------------+--------------+
|     Field     |    Value     |
+---------------+--------------+
| Name          | setup.py     |
| Size          | 5825         |
| UID           | 502          |
| GID           | 20           |
| Modified Time | 1335569964.0 |
+---------------+--------------+


shell

The shell formatter produces output that can be parsed directly by a typical UNIX shell as variable assignments. This avoids extra parsing overhead in shell scripts.

(.venv)$ cliffdemo file -f shell setup.py
name="setup.py"
size="5916"
uid="527"
gid="501"
modified_time="1335655655.0"
(.venv)$ eval "$(cliffdemo file -f shell --prefix example_ setup.py)"
(.venv)$ echo $example_size
5916


value

The value formatter produces output that only contains the value of the field or fields.

(.venv)$ cliffdemo file -f value -c Size setup.py
5916
(.venv)$ SIZE="$(cliffdemo file -f value -c Size setup.py)"
(.venv)$ echo $SIZE
5916


yaml

The yaml formatter uses PyYAML to produce a YAML mapping where the field name is the key.

(.venv)$ cliffdemo file -f yaml setup.py
Name: setup.py
Size: 1807
UID: 1000
GID: 1000
Modified Time: 1393531476.9587486


json

The json formatter produces a JSON object where the field name is the key.

(.venv)$ cliffdemo file -f json setup.py
{

"Modified Time": 1438726433.8055942,
"GID": 1000,
"UID": 1000,
"Name": "setup.py",
"Size": 1028 }


Other Formatters

A formatter using tablib to produce HTML is available as part of cliff-tablib.

Creating Your Own Formatter

If the standard formatters do not meet your needs, you can bundle another formatter with your program by subclassing from cliff.formatters.base.ShowFormatter and registering the plugin in the cliff.formatter.show namespace.

Command Completion

A generic command completion command is available to generate a bash-completion script. Currently, the command will generate a script for bash versions 3 or 4. There is also a mode that generates only data that can be used in your own script. The command completion script is generated based on the commands and options that you have specified in cliff.

Usage

In order for your command to support command completions, you need to add the cliff.complete.CompleteCommand class to your command manager.

self.command_manager.add_command('complete', cliff.complete.CompleteCommand)


When you run the command, it will generate a bash-completion script:

(.venv)$ mycmd complete
_mycmd()
{

local cur prev words
COMPREPLY=()
_get_comp_words_by_ref -n : cur prev words
# Command data:
cmds='agent aggregate backup'
cmds_agent='--name' ...
if [ -z "${completed}" ] ; then
COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) )
else
COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) )
fi
return 0 } complete -F _mycmd mycmd


Interactive Mode

In addition to running single commands from the command line, cliff supports an interactive mode in which the user is presented with a separate command shell. All of the command plugins available from the command line are automatically configured as commands within the shell.

Refer to the cmd2 documentation for more details about features of the shell.

Example

The cliffdemo application enters interactive mode if no command is specified on the command line.

(.venv)$ cliffdemo
(cliffdemo) help
Shell commands (type help <topic>):
===================================
cmdenvironment  edit  hi       l   list  pause  r    save  shell      show
ed              help  history  li  load  py     run  set   shortcuts
Undocumented commands:
======================
EOF  eof  exit  q  quit
Application commands (type help <topic>):
=========================================
files  help  simple  file  error  two part


To obtain instructions for a built-in or application command, use the help command:

(cliffdemo) help simple
usage: simple [-h]
A simple command that prints a message.
optional arguments:

-h, --help Show help message and exit.


The commands can be run, including options and arguments, as on the regular command line:

(cliffdemo) simple
sending greeting
hi!
(cliffdemo) files
+----------------------+-------+
|         Name         |  Size |
+----------------------+-------+
| .git                 |   578 |
| .gitignore           |   268 |
| .tox                 |   238 |
| .venv                |   204 |
| announce.rst         |  1015 |
| announce.rst~        |   708 |
| cliff                |   884 |
| cliff.egg-info       |   340 |
| cliffdemo.log        |  2193 |
| cliffdemo.log.1      | 10225 |
| demoapp              |   408 |
| dist                 |   136 |
| distribute_setup.py  | 15285 |
| distribute_setup.pyc | 15196 |
| docs                 |   238 |
| LICENSE              | 11358 |
| Makefile             |   376 |
| Makefile~            |    94 |
| MANIFEST.in          |   186 |
| MANIFEST.in~         |   344 |
| README.rst           |  1063 |
| setup.py             |  5855 |
| setup.py~            |  8128 |
| tests                |   204 |
| tox.ini              |    76 |
| tox.ini~             |   421 |
+----------------------+-------+
(cliffdemo)


Sphinx Integration

Usage

cliff supports integration with Sphinx by way of a Sphinx directives.

Preparation

Before using the autoprogram-cliff directive you must add 'cliff.sphinxext' extension module to a list of extensions in conf.py:

extensions = ['cliff.sphinxext']


Directive

.. autoprogram-cliff:: <namespace> or <app class>
Automatically document an instance of cliff.command.Command or cliff.app.App including a description, usage summary, and overview of all options.

IMPORTANT:

There are two modes in this directive: command mode and app mode. The directive takes one required argument and the mode is determined based on the argument specified.


The command mode documents various information of a specified instance of cliff.command.Command. The command mode takes the namespace that the command(s) can be found in as the argument. This is generally defined in the entry_points section of either setup.cfg or setup.py. You can specify which command(s) should be displayed using :command: option.

.. autoprogram-cliff:: openstack.compute.v2

:command: server add fixed ip


The app mode documents various information of a specified instance of cliff.app.App. The app mode takes the python path of the corresponding class as the argument. In the app mode, :application: option is usually specified so that the command name is shown in the rendered output.

.. autoprogram-cliff:: cliffdemo.main.DemoApp

:application: cliffdemo


Refer to the example below for more information.

In addition, the following directive options can be supplied:

:command:
The name of the command, as it would appear if called from the command line without the executable name. This will be defined in setup.cfg or setup.py albeit with underscores. This is optional and fnmatch-style wildcarding is supported. Refer to the example below for more information.

This option is effective only in the command mode.

:arguments
The arguments to be passed when the cliff application is instantiated. Some cliff applications requires arguments when instantiated. This option can be used to specify such arguments.

This option is effective only in the app mode.

:application:
The top-level application name, which will be prefixed before all commands. This option overrides the global option autoprogram_cliff_application described below. In most cases the global configuration is enough, but this option is useful if your sphinx document handles multiple cliff applications.

SEE ALSO:

The autoprogram_cliff_application configuration option.


:ignored:
A comma-separated list of options to exclude from documentation for this option. This is useful for options that are of low value.

SEE ALSO:

The autoprogram_cliff_ignored configuration option.



The following global configuration values are supported. These should be placed in conf.py:

autoprogram_cliff_application
The top-level application name, which will be prefixed before all commands. This is generally defined in the console_scripts attribute of the entry_points section of either setup.cfg or setup.py. Refer to the example below for more information.

For example:

autoprogram_cliff_application = 'my-sample-application'


Defaults to ''

SEE ALSO:

The :command: directive option.


SEE ALSO:

The :application: directive option.


autoprogram_cliff_ignored
A global list of options to exclude from documentation. This can be used to prevent duplication of common options, such as those used for pagination, across all options.

For example:

autoprogram_cliff_ignored = ['--help', '--page', '--order']


Defaults to ['--help']

SEE ALSO:

The :ignored: directive option.


autoprogram_cliff_app_dist_name
The name of the python distribution (the name used with pip, as opposed to the package name used for importing) providing the commands/applications being documented. Generated documentation for plugin components includes a message indicating the name of the plugin. Setting this option tells cliff the name of the distribution providing components natively so their documentation does not include this message.


SEE ALSO:

Module sphinxcontrib.autoprogram
An equivalent library for use with plain-old argparse applications.
Module sphinx-click
An equivalent library for use with click applications.



IMPORTANT:

The autoprogram-cliff directive emits code-block snippets marked up as shell code. This requires pygments >= 0.6.


Examples

Simple Example (demoapp)

cliff provides a sample application, demoapp, to demonstrate some of the features of cliff. This application is documented using the cliff.sphinxext Sphinx extension.

Advanced Example (python-openstackclient)

It is also possible to document larger applications, such as python-openstackclient. Take a sample setup.cfg file, which is a minimal version of the setup.cfg provided by the python-openstackclient project:

[entry_points]
console_scripts =

openstack = openstackclient.shell:main openstack.compute.v2 =
host_list = openstackclient.compute.v2.host:ListHost
host_set = openstackclient.compute.v2.host:SetHost
host_show = openstackclient.compute.v2.host:ShowHost


This will register three commands - host list, host set and host show - for a top-level executable called openstack. To document the first of these, add the following:

.. autoprogram-cliff:: openstack.compute.v2

:command: host list


You could also register all of these at once like so:

.. autoprogram-cliff:: openstack.compute.v2

:command: host *


Finally, if these are the only commands available in that namespace, you can omit the :command: parameter entirely:

.. autoprogram-cliff:: openstack.compute.v2


In all cases, you should add the following to your conf.py to ensure all usage examples show the full command name:

autoprogram_cliff_application = 'openstack'


CLIFF CLASS REFERENCE

Application

App

class cliff.app.App(description, version, command_manager, stdin=None, stdout=None, stderr=None, interactive_app_factory=None, deferred_help=False)
Application base class.
Parameters
  • description (str) -- one-liner explaining the program purpose
  • version (str) -- application version number
  • command_manager (cliff.commandmanager.CommandManager) -- plugin loader
  • stdin (readable I/O stream) -- Standard input stream
  • stdout (writable I/O stream) -- Standard output stream
  • stderr (writable I/O stream) -- Standard error output stream
  • interactive_app_factory (cliff.interactive.InteractiveApp) -- callable to create an interactive application
  • deferred_help (bool) -- True - Allow subcommands to accept --help with allowing to defer help print after initialize_app


build_option_parser(description, version, argparse_kwargs=None)
Return an argparse option parser for this application.

Subclasses may override this method to extend the parser with more global options.

Parameters
  • description (str) -- full description of the application
  • version (str) -- version number for the application
  • argparse_kwargs -- extra keyword argument passed to the ArgumentParser constructor



clean_up(cmd, result, err)
Hook run after a command is done to shutdown the app.
Parameters
  • cmd (cliff.command.Command) -- command processor being invoked
  • result (int) -- return value of cmd
  • err (Exception) -- exception or None



configure_logging()
Create logging handlers for any log output.

get_fuzzy_matches(cmd)
return fuzzy matches of unknown command

initialize_app(argv)
Hook for subclasses to take global initialization action after the arguments are parsed but before a command is run. Invoked only once, even in interactive mode.
Parameters
argv -- List of arguments, including the subcommand to run. Empty for interactive mode.


prepare_to_run_command(cmd)
Perform any preliminary work needed to run a command.
Parameters
cmd (cliff.command.Command) -- command processor being invoked


Print help and exits if deferred help is enabled and requested.
'--help' shows the help message and exits:
  • without calling initialize_app if not self.deferred_help (default),
  • after initialize_app call if self.deferred_help,
  • during initialize_app call if self.deferred_help and subclass calls explicitly this method in initialize_app.



run(argv)
Equivalent to the main program for the application.
Parameters
argv (list of str) -- input arguments and options



InteractiveApp

class cliff.interactive.InteractiveApp(parent_app, command_manager, stdin, stdout, errexit=False)
Provides "interactive mode" features.

Refer to the cmd2 and cmd documentation for details about subclassing and configuring this class.

Parameters
  • parent_app -- The calling application (expected to be derived from cliff.main.App).
  • command_manager -- A cliff.commandmanager.CommandManager instance.
  • stdin -- Standard input stream
  • stdout -- Standard output stream


cmdloop()
This is an outer wrapper around _cmdloop() which deals with extra features provided by cmd2.

_cmdloop() provides the main loop equivalent to cmd.cmdloop(). This is a wrapper around that which deals with the following extra features provided by cmd2: - transcript testing - intro banner - exit code

Parameters
intro -- if provided this overrides self.intro and serves as the intro banner printed once at start


completedefault(text, line, begidx, endidx)
Default tab-completion for command prefix with completer delimiter.

This method filters only cliff style commands matching provided command prefix (line) as cmd2 style commands cannot contain spaces. This method returns text + missing command part of matching commands. This method does not handle options in cmd2/cliff style commands, you must define complete_$method to handle them.


completenames(text, line, begidx, endidx)
Tab-completion for command prefix without completer delimiter.

This method returns cmd style and cliff style commands matching provided command prefix (text).


default(line)
Executed when the command given isn't a recognized command implemented by a do_* method.
Parameters
statement -- Statement object with parsed input


do_exit(_: argparse.Namespace) -> Optional[bool]
Exit this application

do_help(arg)
List available commands or provide detailed help for a specific command

get_names()
Return an alphabetized list of names comprising the attributes of the cmd2 class instance.

precmd(statement)
Hook method executed just before the command is executed by onecmd() and after adding it to history.
Parameters
statement -- subclass of str which also contains the parsed input
Returns
a potentially modified version of the input Statement object



CommandManager

class cliff.commandmanager.CommandManager(namespace, convert_underscores=True)
Discovers commands and handles lookup based on argv data.
Parameters
  • namespace -- String containing the entrypoint namespace for the plugins to be loaded. For example, 'cliff.formatter.list'.
  • convert_underscores -- Whether cliff should convert underscores to spaces in entry_point commands.


add_command_group(group=None)
Adds another group of command entrypoints

add_legacy_command(old_name, new_name)
Map an old command name to the new name.
Parameters
  • old_name (str) -- The old command name.
  • new_name (str) -- The new command name.



find_command(argv)
Given an argument list, find a command and return the processor and any remaining arguments.

get_command_groups()
Returns a list of the loaded command groups

get_command_names(group=None)
Returns a list of commands loaded for the specified group

load_commands(namespace)
Load all the commands from an entrypoint


Command

class cliff.command.Command(app, app_args, cmd_name=None)
Base class for command plugins.

When the command is instantiated, it loads extensions from a namespace based on the parent application namespace and the command name:

app.namespace + '.' + cmd_name.replace(' ', '_')


Parameters
app (cliff.app.App) -- Application instance invoking the command.

get_description()
Return the command description.

The default is to use the first line of the class' docstring as the description. Set the _description class attribute to a one-line description of a command to use a different value. This is useful for enabling translations, for example, with _description set to a string wrapped with a gettext translation marker.


get_epilog()
Return the command epilog.

get_parser(prog_name)
Return an argparse.ArgumentParser.

run(parsed_args)
Invoked by the application when the command is run.

Developers implementing commands should override take_action().

Developers creating new command base classes (such as Lister and ShowOne) should override this method to wrap take_action().

Return the value returned by take_action() or 0.


abstract take_action(parsed_args)
Override to do something useful.

The returned value will be returned by the program.



CommandHook

class cliff.hooks.CommandHook(command)
Base class for command hooks.
Parameters
app (cliff.command.Command) -- Command instance being invoked

abstract after(parsed_args, return_code)
Called after the command's take_action() method.
Parameters
  • parsed_args (argparse.Namespace) -- The arguments to the command.
  • return_code (int) -- The value returned from take_action().

Returns
int


abstract before(parsed_args)
Called before the command's take_action() method.
Parameters
parsed_args (argparse.Namespace) -- The arguments to the command.
Returns
argparse.Namespace


abstract get_epilog()
Return text to add to the command help epilog.

abstract get_parser(parser)
Return an argparse.ArgumentParser.
Parameters
parser (ArgumentParser) -- An existing ArgumentParser instance to be modified.
Returns
ArgumentParser



ShowOne

class cliff.show.ShowOne(app, app_args, cmd_name=None)
Command base class for displaying data about a single object.
dict2columns(data)
Implement the common task of converting a dict-based object to the two-column output that ShowOne expects.

property formatter_default
String specifying the name of the default formatter.

property formatter_namespace
String specifying the namespace to use for loading formatter plugins.

produce_output(parsed_args, column_names, data)
Use the formatter to generate the output.
Parameters
  • parsed_args -- argparse.Namespace instance with argument values
  • column_names -- sequence of strings containing names of output columns
  • data -- iterable with values matching the column names



abstract take_action(parsed_args)
Return a two-part tuple with a tuple of column names and a tuple of values.


Lister

class cliff.lister.Lister(app, app_args, cmd_name=None)
Command base class for providing a list of data as output.
property formatter_default
String specifying the name of the default formatter.

property formatter_namespace
String specifying the namespace to use for loading formatter plugins.

get_parser(prog_name)
Return an argparse.ArgumentParser.

property need_sort_by_cliff
Whether sort procedure is performed by cliff itself.

Should be overridden (return False) when there is a need to implement custom sorting procedure or data is already sorted.


produce_output(parsed_args, column_names, data)
Use the formatter to generate the output.
Parameters
  • parsed_args -- argparse.Namespace instance with argument values
  • column_names -- sequence of strings containing names of output columns
  • data -- iterable with values matching the column names



abstract take_action(parsed_args)
Run command.

Return a tuple containing the column names and an iterable containing the data to be listed.



Formatting Output

Formatter

class cliff.formatters.base.Formatter
abstract add_argument_group(parser)
Add any options to the argument parser.

Should use our own argument group.



ListFormatter

class cliff.formatters.base.ListFormatter
Base class for formatters that know how to deal with multiple objects.
abstract emit_list(column_names, data, stdout, parsed_args)
Format and print the list from the iterable data source.

Data values can be primitive types like ints and strings, or can be an instance of a FormattableColumn for situations where the value is complex, and may need to be handled differently for human readable output vs. machine readable output.

Parameters
  • column_names -- names of the columns
  • data -- iterable data source, one tuple per object with values in order of column names
  • stdout -- output stream where data should be written
  • parsed_args -- argparse namespace from our local options




SingleFormatter

class cliff.formatters.base.SingleFormatter
Base class for formatters that work with single objects.
abstract emit_one(column_names, data, stdout, parsed_args)
Format and print the values associated with the single object.

Data values can be primitive types like ints and strings, or can be an instance of a FormattableColumn for situations where the value is complex, and may need to be handled differently for human readable output vs. machine readable output.

Parameters
  • column_names -- names of the columns
  • data -- iterable data source with values in order of column names
  • stdout -- output stream where data should be written
  • parsed_args -- argparse namespace from our local options




FOR CONTRIBUTORS

If you would like to contribute to cliff directly, these instructions should help you get started. Bug reports, and feature requests are all welcome through the Storyboard project.

Changes to cliff should be submitted for review via the Gerrit tool, following the workflow documented at https://docs.openstack.org/infra/manual/developers.html#development-workflow

Pull requests submitted through GitHub will be ignored.

Bugs should be filed under the Storyboard project.

NOTE:

Before contributing new features to clif core, please consider whether they should be implemented as an extension instead. The architecture is highly pluggable precisely to keep the core small.


Running Tests

The test suite for cliff uses tox, which must be installed separately (pip install tox).

To run the standard set of tests, run tox from the top level directory of the git repository.

To run a single environment, specify it using the -e parameter. For example:

$ tox -e pep8


Add new tests by modifying an existing file or creating new script in the tests directory.

Building Documentation

The documentation for cliff is written in reStructuredText and converted to HTML using Sphinx. Like tests, the documentation can be built using tox:

$ tox -e docs


The output version of the documentation ends up in ./docs/build/html.

Indices and tables

  • genindex
  • search

AUTHOR

Doug Hellmann

COPYRIGHT

2012-2023, Doug Hellmann

November 29, 2023