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().__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().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').read() except OSError:
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, Exploring the Demo App, 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'


CHANGES

4.11.0

  • mypy: Enable strict mode
  • pre-commit: Bump versions
  • Migrate setup configuration to pyproject.toml

4.10.0

  • Bump Python version used for linters to 3.10
  • Drop support for Python 3.9

4.9.1

  • hooks: Update type hints to reflect reality
  • command: Filter on empty epilog, not empty hooks

4.9.0

  • typing: Enable basic typing for tests
  • typing: Add typing to cliff.formatters
  • typing: Add typing to cliff.sphinxext
  • typing: Add typing to cliff.hooks
  • typing: Add typing to cliff.help
  • typing: Add typing to cliff.complete
  • typing: Add typing to cliff.command
  • typing: Add typing to cliff.commandmanager
  • typing: Add typing to cliff._argparse
  • typing: Add typing to cliff.columns
  • typing: Add typing to cliff.interactive
  • typing: Add typing to cliff.app
  • pre-commit: Enable mypy
  • typing: Fix initial typing issues
  • pre-commit: Bump versions

4.8.0

  • Remove unnecessary shebangs
  • ruff: Enable pyupgrade rules
  • Migrate to ruff, ruff-format
  • Drop support for Python 3.8, add Python 3.12
  • pre-commit: Bump versions
  • Normalize columns given by '-c'/'--columns'

4.7.0

  • Add fixtures explicitly in test requirements
  • tox: Use pre-commit for linter checks
  • Blacken code base
  • requirements: Bump minimums

4.6.0

Test python 3.10 and 3.11

4.5.0

Use importlib.metadata on Python 3.10+

4.4.0

  • Handle complex objects in yaml formatter better
  • Add pre-commit
  • Fix pre-commit issues
  • Autofit table output if stdout is a tty
  • Fix flake8 violation E721
  • Use upper-constraint in doc generation

4.3.0

Removing helper functions providing Python < 3.3 compatibility

4.2.0

  • Strip trailing periods when getting description
  • Clarification of the algorithm used

4.1.0

  • columns: Useful __str__, __repr__ implementation
  • Add Python3 antelope unit tests

4.0.0

  • Removing brackets around tested conditional
  • Replace abc.abstractproperty with property and abc.abstractmethod
  • Remove final use of pkg_resources
  • Defer loading PyYAML
  • Defer loading cmd2
  • requirements: Remove explicit python-subunit dependency
  • requirements: Remove explicit pbr dependency
  • requirements: Remove explicit pyparsing dependency
  • Update Python testing per Zed cycle testing runtime
  • Migrate Python 3.6/7 jobs to Python 3.8

3.10.1

Removing modindex link from docs

3.10.0

Add Python3 yoga unit tests

3.9.0

  • Automatically page interactive root help output
  • Colourise and automatically page help output
  • Update unit test to satisfy python3.10+
  • Handle SIGPIPE exit gracefully
  • Add conflict_handler parameter as attribut in Command class

3.8.0

  • setup.cfg: Replace dashes with underscores
  • Replace getargspec with getfullargspec
  • setup.cfg: Replace dashes with underscores
  • Use py3 as the default runtime for tox
  • Add Python3 xena unit tests

3.7.0

  • requirements: Uncap PrettyTable
  • Add '--sort-ascending', '--sort-descending' parameters
  • Make 'FormattableColumn' comparable
  • Handle null values when sorting
  • Remove unicode from code
  • gitignore: Ignore reno artefacts
  • Remove lower-constraints

3.6.0

trivial: Remove references to Python 2.7

3.5.0

  • columns: Make 'FormattableColumn' comparable
  • Update requirements URLs in tox config
  • Remove six
  • Update requirements
  • doc: Update bug tracker to storyboard
  • Remove references to setuptools
  • Add py38 package metadata
  • Remove Babel from lower-constraints.txt
  • Bump py37 to py38 in tox.ini
  • List setuptools under install_requires
  • Document KeyboardInterrupt exit code
  • Exit gracefully on Ctrl-C
  • change help action to use its own exception for exit
  • Add Python3 wallaby unit tests
  • Capturing argparse errors due to problem with cmd2

3.4.0

switch to stevedore for loading entry points

3.3.0

  • Remove cap on cmd2
  • Fix compatibility with new cmd2

3.2.0

  • drop mock from lower-constraints and requirements
  • Import command group support from osc-lib
  • Remove unneeded tests
  • Migrate to stestr
  • Remove python3.5
  • Stop to use the __future__ module
  • Switch to newer openstackdocstheme version
  • Use unittest.mock instead of third party mock
  • Add Python3 victoria unit tests

3.1.0

  • Re-add support for python 3.5
  • Fix nested argument groups with ignore conflict handler
  • adding missing releasenote for the drop of py27 support

3.0.0

[ussuri][goal] Drop python 2.7 support and testing

2.18.0

  • Add autoprogram_cliff_app_dist_name config opt
  • Switch to Ussuri jobs
  • Add contributors link to readme

2.16.0

  • Pin cmd2 back to <0.9 on all versions
  • Modify the help message of `-c`/`--column` parameter
  • Add Python 3 Train unit tests
  • Stop wildcard importing argparse

2.15.0

  • Add an errexit attribute to InteractiveApp to exit on command errors
  • Dropping the py35 testing
  • Updates for OpenDev transition
  • OpenDev Migration Patch
  • add python 3.7 unit test job
  • Missing carriage return in some cases, using -f json

2.14.1

  • Use template for lower-constraints
  • Change openstack-dev to openstack-discuss

2.14.0

  • Don't try to run issubclass on non-classes
  • Removed unused err variable
  • Remove dead files
  • add lib-forward-testing-python3 test job
  • add python 3.6 unit test job
  • switch documentation job to new PTI
  • import zuul job settings from project-config

2.13.0

Assure executable name is kept when app is called as module

2.12.1

  • Build universal wheels
  • fix tox python3 overrides
  • support cmd2 0.9.1 in interactive mode

2.12.0

  • update cmd2 dependency to handle py3 only versions
  • Remove travis.yml
  • exclude cmd2 0.8.3 and update to 0.8.4
  • add lower-constraints job
  • fix typos in documentation

2.17.0

  • Allow finding command by partial name
  • Updated from global requirements
  • Remove the warning of getargspec removal
  • Align parsed() call with cmd2 versions >= 0.7.3
  • Fix cmd2 doc URL
  • add argparse conflict handler "ignore"
  • sphinxext: Warn if namespace or command pattern invalid
  • Zuul: Remove project name
  • Updated from global requirements

2.11.0

remove -s alias for --sort-columns

2.10.0

  • Remove empty files
  • Add ability to sort data by columns for list commands
  • Updated from global requirements
  • Remove tox_install.sh and just pass -c in tox
  • Replace legacy tips jobs with shiny new versions
  • Move doc requirements to doc/requirements.txt
  • do not require installing demo app to build docs
  • add support for legacy command name translation
  • Use in-tree cliffdemo app for docs build
  • Updated from global requirements
  • add bandit to pep8 job
  • sphinxext: Support cliff application
  • Fix PEP8 in gate
  • doc: Cleanup of demoapp doc
  • Generate demoapp CLI refernece
  • Fix codec error when format=csv

2.9.1

handle more varied top_level.txt files in distributions

2.9.0

  • show the distribution providing the command in help output
  • Update .gitignore
  • Docs update for more-hooks
  • Updates for stestr
  • Allow command hooks to make changes
  • Updated from global requirements
  • add actual column names to error msg Closes-Bug: 1712876
  • Alias exit to cmd2's quit command to exit interactive shell
  • Updated from global requirements
  • Update doc on Sphinx integration process
  • Fix regexp for detecting long options
  • sphinxext: Correct issues with usage formatting
  • Move comments up in [extras] section of setup.cfg
  • Updated from global requirements
  • Make openstackdocstheme an optional doc dependency
  • Updated from global requirements
  • doc: minor cleanup
  • Update and replace http with https for doc links
  • doc: Remove blank lines between term and definition
  • trivial: Fix comments in sphinxext module
  • Use assertIsNone(...) instead of assertIs(None,...)
  • Updated from global requirements

2.8.0

  • add tests for display command classes and hooks
  • Run hooks for DisplayCommandBase
  • add --fit-width option to table formatter
  • sphinxext: Add 'application' option to the autoprogram directive
  • use openstackdocstheme html context
  • switch from oslosphinx to openstackdocstheme
  • Fix erroneous line in command hook test
  • make smart help formatter test deterministic
  • remove references to distribute in the docs
  • add before and after hooks
  • add hook for get_epilog
  • add hook for manipulating the argument parser
  • Updated from global requirements
  • pass the command name from HelpCommand
  • Adjust completenames tests for cmd2 0.7.3+
  • rearrange existing content to follow new standard
  • sphinext: Use metavar where possible
  • sphinxext: Use 'argparse.description', 'argparse.epilog'
  • sphinxext: Allow configuration of ignorable options
  • sphinxext: Generate better usage examples
  • add cmd_name argument to CompleteCommand
  • Ensure python standard argparse module is loaded
  • Updated from global requirements

2.7.0

covert test suite to use testrepository

2.6.0

  • Updated from global requirements
  • Add smart help formatter for command parser
  • Add support for epilogs
  • Add 'autoprogram-cliff' Sphinx directive
  • .gitignore: Ignore eggs

2.5.0

  • Use Sphinx 1.5 warning-is-error
  • Update cmd2 fix to still work with 0.6.7
  • Remove support for py34
  • Fix broken test with cmd2 0.7.0
  • Updated from global requirements
  • Updated from global requirements
  • Updated from global requirements
  • Add newline if the output formatter is json

2.4.0

  • Add Constraints support
  • Remove tox environments that no longer work
  • Fix command order
  • Show team and repo badges on README
  • Add print_empty parameter

2.3.0

  • ignore Command docstring when looking for descriptions
  • let the Command get its one-liner description from a class attribute
  • flake8 fix
  • Replace dashes and colons when using bash formatter
  • Show entire command in error message
  • Updated from global requirements
  • Updated from global requirements
  • Fix spelling mistake
  • Add Python 3.5 classifier and venv
  • Updated from global requirements
  • Changed the home-page link
  • Add Apache 2.0 license to source file
  • Updated from global requirements
  • Updated from global requirements
  • Updated from global requirements
  • Updated from global requirements
  • Clean imports in code
  • [doc]Fix URL for 'setuptools entry points'
  • Fix a typo in comment

2.2.0

  • Avoid ASCII encoding errors when output is redirected
  • Fix cliff URLs in doc and demoapp
  • Remove announce.rst
  • Fix Command class doc typo
  • Updated from global requirements
  • Fixed broken link
  • add formattable columns concept
  • Add tests, cover more cases
  • Updated from global requirements
  • pep8: fix F405 error
  • command: make run() return take_action() value

2.1.0

  • Updated from global requirements
  • Update --max-width help
  • Add more test coverage for shell formatter
  • Add more test coverage for CSV formatter
  • Support multiple sub commands in completion
  • Factorize more test data
  • Factorize some test data
  • Factorize common test code
  • Factorize argparse importing
  • Updated from global requirements
  • Updated from global requirements
  • Add CLIFF_MAX_TERM_WIDTH envvar to complement --max-width
  • Fix prettytable.PrettyTable().max_width wrong usage
  • Fix AttributeError when entry point load failed
  • Distinguish no existed columns in ShowOne
  • Refactor HelpCommand
  • Updated from global requirements
  • Remove httplib2 from test-requirements.txt
  • Sync help message for --help

2.0.0

handle empty list in table formatter

1.17.0

  • Drop Python 2.6 support
  • Revert "app,command: disallow abbrev by default"
  • Fixes terminal_width on Windows

1.16.0

  • Updated from global requirements
  • remove openstack-common.conf
  • Add doc for deferred_help parameter
  • Fix pep8 failure
  • app,command: disallow abbrev by default
  • app: work-around abbrev
  • remove unnecessary dependency on argparse
  • Make verbose and quiet mutually exclusive
  • setup: fix Python versions classifiers
  • Don't import unused logging
  • Don't use non-existent method of Mock
  • Replace dashes with underscores in bash completion
  • Updated from global requirements
  • Resize columns to fit screen width
  • fix fuzzy search for same-distance case
  • Correct path to docs
  • only use unicodecsv for python 2.x
  • Fix test class docstring for py 3.5

1.15.0

  • Replace 'r' with ' ' for prettytable
  • Implement a json formatter in cliff
  • Implement a yaml formatter in cliff
  • Updated from global requirements
  • Improve help messages

1.14.0

  • Add csv formatter test
  • Fix encoding issue with the default python csv output
  • Remove py26 as a default test from tox.ini
  • Set demo app up with deferred help
  • Add command fuzzy matching
  • Updated from global requirements
  • Remove requirements.txt from tox.ini
  • Updated from global requirements
  • Updated from global requirements
  • Allow subcommands to accept --help when using 'deferred_help'
  • Updated from global requirements
  • Fix logging config in demo app
  • Use base command for help test
  • Updated from global requirements
  • Include the automatically-generated changelog
  • Updated from global requirements

1.13.0

  • Fix object has no attribute debug error
  • Add some docs for list value formatter
  • Add value format for list command
  • Updated from global requirements
  • Remove run_cross_tests.sh
  • fix author contact details
  • Print help on help command

1.12.0

Do not check requirements when loading plugins

1.11.0

  • Catch and ignore error when locale can not be set
  • Uncap library requirements for liberty
  • Add documentation for the value formatter
  • Sort the fuzzy matches
  • Defer interactive import
  • Updated from global requirements
  • Update links to setuptools doc

1.10.1

  • Pass user command text to the Command object
  • Document print_help_if_requested method

1.10.0

  • Allow to call initialize_app when running --help
  • Hide prompt in batch/pipe mode
  • Correct completion in interactive mode
  • Change the argument passed to __init__ for help
  • Fix pep8 tests for lambda
  • Updated from global requirements
  • Fix git repo urls in tox.ini
  • Add deprecated attribute to commands
  • Workflow documentation is now in infra-manual

1.9.0

  • print the real error cmd argument
  • Updated from global requirements

1.8.0

  • Update link to docs in README
  • Bring doc build up to standard
  • Add pbr to installation requirements
  • Add more detail to the README
  • Updated from global requirements
  • Add docs environment to tox.ini
  • mock.assert_called_once() is not a valid method
  • Work toward Python 3.4 support and testing
  • warn against sorting requirements

1.7.0

  • Add release notes for 1.7.0
  • Fix stable integration tests
  • Updated from global requirements
  • Clean up default tox environment list
  • Do not allow wheels for stable tests
  • Set the main logger name to match the application
  • CSV formatter should use system-dependent line ending
  • Make show option compatible with Python 2.6
  • Use six.add_metaclass instead of __metaclass__
  • fixed typos found by RETF rules
  • The --variable option to shell format is redundant
  • Expose load_commands publicly
  • Fix wrong method name assert_called_once
  • Updated from global requirements
  • Fix pep8 failures on rule E265

1.6.1

  • Remove PrettyTable from documentation requirements
  • Fix a bug in ShellFormatter's escaping of double quotes in strings
  • Import run_cross_tests.sh from oslo-incubator
  • add doc requirements to venv

1.6.0

  • Add max-width support for table formatter
  • Add value only output formattter
  • Update readme with links to bug tracker and source
  • Move pep8 dependency into pep8 tox test
  • Fix doc build with Python 2.6.x
  • Fix interactive mode with command line args
  • Update .gitreview after repo rename
  • Escape double quotes in shell formatter
  • Add unit test for shell formatter
  • Rename private attribute to avoid conflict
  • Sync with global requirements
  • Add integration tests with known consumers
  • update history for previous change
  • Make the formatters a private part of the command

1.5.2

move to pbr for packaging

1.5.1

add venv environ to tox config

1.5.0

  • Update history for next release
  • Move to stackforge
  • update history for stevedore change
  • Use stevedore to load formatter plugins
  • use entry points for completion plugins
  • Clean up recursive data handling
  • Always install complete command
  • attribution for bash completion work in history
  • code style fixes
  • code style fixes
  • various python code optimizations; shuffle I/O to shell classes
  • add bash complete
  • Enable debug in help mode
  • Pass the right args when pulling help from commands
  • prepare for 1.4.5 release
  • add pypy test env configuration
  • Update pyparsing dependency to 2.0.1

1.4.4

  • update for release 1.4.4
  • Re-raise Exception on debug mode
  • Add test to check if return code is 2 on unknown command
  • Return code 1 is already use, use code 2 instead
  • Reraise error on debug
  • Display better error message on unknown command, and return code 1
  • update announce file

1.4.3

  • prepare for 1.4.3 release
  • force python2.6 for that test env
  • Provide a default output encoding

1.4.2

prepare for release 1.4.2

1.4.1

  • prepare for release 1.4.1
  • Tighten requirements on cmd2
  • remove use of distribute in demo app
  • Fix default encoding issue with python 2.6
  • move tests into cliff package
  • add tests for dict2columns
  • Add dict2columns()
  • turn off distribute in tox

1.4

  • prep for release 1.4
  • fix flake8 issues with setup.py
  • remove the other traces of distribute
  • Remove explicit depend on distribute
  • update history for recent contribution
  • Expose instantiated interpreter instance and assign it to the 'interpreter' variable on the App instance
  • Update announcement for release 1.3.3

1.3.3

  • Prepare for release 1.3.3
  • declare support for python 3.3
  • cmd2 0.6.5.1 was released, and is compatible
  • Restore compatibility with Prettytable < 0.7.2

1.3.2

  • Prepare 1.3.2 release
  • Bump prettytable version accepted
  • add python 3.3 to tox
  • add style checks to tests
  • Add tests for underscore handling
  • use flake8 for style checks
  • update history.rst with convert_underscores change
  • make converting underscores optional in CommandManager
  • fix version in docs

1.3.1

  • prepare for 1.3.1 release
  • Fix PyParsing dependency
  • Fix typo
  • update history file for previous merge
  • Make list of application commands lexicographically ordered for help command in interactive mode

1.3

  • Prepare for 1.3 release
  • clean up history file
  • Document dependency on distribute
  • fix rst formatting in docstring
  • Update history file
  • Add tests for new functionality
  • Allow user to pass argparse_kwargs argument to the build_option_parser method. Those arguments gets passed to the ArgumentParser constructor

1.2.1

  • Set up for 1.2.1 release
  • Remove unused logging import
  • Fix problem with missing izip import in lister.py
  • Update announcement file for new release

1.2

  • Set up release 1.2
  • Add python2.6 support
  • remove debug print
  • remove tablib from test requirements
  • Fix logging default behavior
  • Fix interactive help command

1.1.2

  • bumping version number for release
  • remove the entry point data for the moved formatters

1.1.1

bump the version number to release a clean build

1.1

  • Update version and status values
  • Remove tablib formatters from core
  • fix version # in announcement

1.0

  • Doc updates for API changes. Clean up docstrings. Bump version to 1.0
  • merge API refactoring work
  • yet more pep8 work
  • fix help and tests for API change
  • Move take_action() to Command
  • more pep8 work
  • Refactor DisplayBase.run() to make it easier to override separate parts in subclasses. Rename get_data() to take_action() so subclasses that do something other than query for values have a clear place to override
  • pep8 cleanup
  • add attribution to history for the previous merge
  • Adding new line to tablib formatters
  • fix tags declaration
  • document updates for 0.7
  • disable py26 tests since I do not have an environment for running them

0.7

  • bump version
  • fix interactive command processor to handle multi-part commands, including some that use the same first word as existing commands
  • declare a couple of commands that use builtin command names but use multiple words
  • update changelog
  • set the interactive mode flag before initializing the app so subclasses can check it; handle initialization errors more cleanly
  • add travis-ci status image to developer docs
  • add travis-ci status image to README
  • add a requirements file for travis-ci
  • bogus commit to trigger ci build
  • add travis-ci.org configuration file
  • add version num to history file

0.6

  • bump version number
  • pass more details to initialize_app so subclasses can decide what sort of initialization to do
  • enable to use in Python2.6

0.5.1

remove hard version requirement to unbreak the OpenStack build

0.5

  • prepare for 0.5 release
  • document changes in history file
  • make the organization of the classes page a little more clear
  • update formatter documentation
  • fix yaml, html, and json show formatters
  • move the column option so it applies to "show" commands, too
  • add yaml, json, and html formatters
  • move the columns option out of the table formatter and into the lister base
  • make help list commands if none match exactly; fixes #8
  • require at least PrettyTable 0.6 for Python 3 support, fixes #7
  • changes in the prettytable API rolled into the python 3 support update
  • add a tox stage for pep8 testing
  • python 3.2 does not have a unicode type so ignore the error if it is missing
  • move todo list to github issues
  • update todo list
  • note about prettytable and python3
  • refactor ShowOne and Lister to share a common base class
  • more todo notes
  • tests for cliff.help
  • pass the App to the help action instead of passing just the command manager, since the app has the stout handle we want to use for printing the help
  • 100% coverage of cliff.command
  • 100% coverage for commandmanager.py
  • 100% coverage of cliff.app module
  • let the interactive app set its own prompt
  • add tests for App and fix an issue with error handling and clean_up() in python 3
  • use the stderr handle given to the app instead of assuming the default

0.4

  • version number and release note updates for 0.4
  • documentation improvements
  • simplify packaging file for demo app
  • ignore files generated by dev environment
  • first pass at interactive app
  • note to add more options to csv formatter
  • add --prefix option for shell formatter; add docs for shell formatter
  • clean up help text for the other formatters
  • add shell output formatter for single items
  • add longer docstring to show how it is printed by help
  • update todo list
  • fix typo in blog post

0.3

  • update blog announcement
  • bump the version number and update the release notes
  • add ShowOne base class for commands that need to show properties of an individual object make the table formatter work as a single object formatter update the docs for the new features
  • handle an empty data set
  • correct the doctoring
  • fix version # in doc build script
  • 0.2 release announcement post

0.2

  • bump version number
  • start a release log
  • update doc instructions for getting help
  • only show the one-line description in the command list; add a description of "help"
  • register a custom help action that knows how to print the list of commands available and a help command to generate help for the other commands
  • provide an internal API for applications to register commands without going through setuptools (used for help handler)
  • Use argparse for global arguments
  • fix doc build instructions
  • add some developer instructions and links ot the source repo and bug tracker
  • add announcement blog post source
  • advice from the distutils list was to stick with distribute for now
  • add Makefile with some common release operations
  • add example output to the list formatters
  • add a requirements file for doc build on readthedocs.org
  • add some real documentation
  • Add get_data() to the Lister base class
  • remove example that I was using as a syntax reminder
  • Add a link to the docs
  • while looking for documentation on entry points I realized distutils2 doesn't seem to support them in the same way
  • fill in a real description of the project
  • start sphinx documentation
  • Added a bit more to the README
  • flesh out instructions for using the demo app
  • add a few more ideas
  • Added a README for the demo app
  • Added download url to both setup.py files and updated the demo setup.py with the new url

0.1

  • Added missing distribute setup file
  • move repo link to the dreamhost project
  • more to-do items
  • add demoapp to release package and clean up files being distributed from the test directory
  • notes about work still to be done
  • require PrettyTable package for the table formatter
  • improve error handling when loading formatter plugins
  • add a csv formatter for list apps
  • start creating a subclass of command for producing a list of output in different formats, using prettytable as an example formatter
  • remove unused import
  • better error handling of post-action hook in app
  • Pass the I/O streams into the app
  • add some error handling to App
  • make the log messages slightly easier to parse
  • tweak App api to make it easier to override and perform global actions before and after a command runs
  • use logging for controlling console output verbosity
  • clean up argv handling
  • install nose for tox tests
  • if no arguments are provided at all show the help message
  • replace default --help processor with one that includes the list of subcommands available
  • add debug option to nose
  • clean up dead code
  • include version info when configuring opt parse
  • Sample program with command plugins
  • first pass at an app class that can invoke commands
  • save commands using the name representation to be used in help output; don't modify the input arg list when searching for the command; return the name of the command found so the app can stuff it into the help text of the command
  • start building command manager
  • change to apache license
  • add tox config file for tests
  • add distribute_setup.py so install works
  • add setup.py and package directory
  • add a basic description to readme
  • convert readme to rst
  • initial commit

CLIFF CLASS REFERENCE

Application

App

class cliff.app.App(description: str | None, version: str | None, command_manager: _commandmanager.CommandManager, stdin: TextIO | None = None, stdout: TextIO | None = None, stderr: TextIO | None = None, interactive_app_factory: type[_interactive.InteractiveApp] | None = None, deferred_help: bool = 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: str | None, version: str | None, argparse_kwargs: dict[str, Any] | None = None) -> ArgumentParser
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: _command.Command, result: int, err: BaseException | None) -> None
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() -> None
Create logging handlers for any log output.

get_fuzzy_matches(cmd: str) -> list[str]
return fuzzy matches of unknown command

initialize_app(argv: list[str]) -> None
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: _command.Command) -> None
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: list[str]) -> int
Equivalent to the main program for the application.
Parameters
argv (list of str) -- input arguments and options



InteractiveApp

class cliff.interactive.InteractiveApp(parent_app: _app.App, command_manager: _commandmanager.CommandManager, stdin: TextIO | None, stdout: TextIO | None, errexit: bool = 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.app.App).
  • command_manager -- A cliff.commandmanager.CommandManager instance.
  • stdin -- Standard input stream
  • stdout -- Standard output stream


cmdloop(intro: str | None = None) -> None
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: str, line: str, begidx: int, endidx: int) -> list[str]
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: str, *ignored: Any) -> list[str]
Tab-completion for command prefix without completer delimiter.

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


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


do_exit(_: Namespace) -> bool | None
Exit this application

do_help(arg: str | None) -> None
List available commands or provide detailed help for a specific command

get_names() -> list[str]
Return an alphabetized list of names comprising the attributes of the cmd2 class instance.

precmd(statement: Statement | str) -> 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: str, convert_underscores: bool = 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: str | None = None) -> None
Adds another group of command entrypoints

add_legacy_command(old_name: str, new_name: str) -> None
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: list[str]) -> tuple[type[Command], str, list[str]]
Given an argument list, find a command and return the processor and any remaining arguments.

get_command_groups() -> list[str]
Returns a list of the loaded command groups

get_command_names(group: str | None = None) -> list[str]
Returns a list of commands loaded for the specified group

load_commands(namespace: str) -> None
Load all the commands from an entrypoint


Command

class cliff.command.Command(app: _app.App, app_args: Namespace | None, cmd_name: str | None = 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 -- Application instance invoking the command.
  • app_args -- Parsed arguments from options associated with the application instance..
  • cmd_name -- The name of the command.


get_description() -> str
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() -> str
Return the command epilog.

get_parser(prog_name: str) -> ArgumentParser
Return an argparse.ArgumentParser.

run(parsed_args: Namespace) -> int
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.


abstractmethod take_action(parsed_args: Namespace) -> Any
Override to do something useful.

The returned value will be returned by the program.



CommandHook

class cliff.hooks.CommandHook(command: Command)
Base class for command hooks.

Hook methods are executed in the following order:

1.
get_epilog()
2.
get_parser()
3.
before()
4.
after()

Parameters
command -- Command instance being invoked

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

Returns
int


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


abstractmethod get_epilog() -> str | None
Return text to add to the command help epilog.
Returns
An epilog string or None.


abstractmethod get_parser(parser: ArgumentParser) -> ArgumentParser | None
Modify the command argparse.ArgumentParser.

The provided parser is modified in-place, and the return value is not used.

Parameters
parser -- An existing ArgumentParser instance to be modified.
Returns
ArgumentParser or None



ShowOne

class cliff.show.ShowOne(app: App, app_args: Namespace | None, cmd_name: str | None = None)
Command base class for displaying data about a single object.
dict2columns(data: dict[str, Any]) -> tuple[tuple[str, ...], tuple[Any, ...]]
Implement the common task of converting a dict-based object to the two-column output that ShowOne expects.

property formatter_default: str
String specifying the name of the default formatter.

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

produce_output(parsed_args: Namespace, column_names: Sequence[str], data: Iterable[Sequence[Any]]) -> int
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

Returns
a status code


abstractmethod take_action(parsed_args: Namespace) -> tuple[tuple[str, ...], tuple[Any, ...]]
Return a two-part tuple with a tuple of column names and a tuple of values.


Lister

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

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

get_parser(prog_name: str) -> ArgumentParser
Return an argparse.ArgumentParser.

property need_sort_by_cliff: bool
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: Namespace, column_names: Sequence[str], data: Iterable[Sequence[Any]]) -> int
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

Returns
a status code


abstractmethod take_action(parsed_args: Namespace) -> tuple[Sequence[str], Iterable[Any]]
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
abstractmethod add_argument_group(parser: ArgumentParser) -> None
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.
abstractmethod emit_list(column_names: Sequence[str], data: Iterable[Sequence[Any]], stdout: TextIO, parsed_args: Namespace) -> None
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.
abstractmethod emit_one(column_names: Sequence[str], data: Sequence[Any], stdout: TextIO, parsed_args: Namespace) -> None
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

  • Index
  • Search Page

AUTHOR

Doug Hellmann

COPYRIGHT

2012-2025, Doug Hellmann

October 24, 2025