This commit adds a feature to generate a reference of global optoions of a cliff application to autoprogram-cliff directive. If a class path of a cliff application is specified as the argument of autoprogram-cliff directive, it will be interpreted as a cliff application and global options of the specified application are rendered. Change-Id: I20e46521a137ca721fae28f10c5cf75d26069e45
8.8 KiB
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
<demoapp-sphinx>
.
The Source
The cliffdemo
application is defined in a
cliffdemo
package containing several modules.
main.py
The main application is defined in main.py
:
../../../demoapp/cliffdemo/main.py
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
:
../../../demoapp/cliffdemo/simple.py
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.
../../../demoapp/cliffdemo/list.py
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.
../../../demoapp/cliffdemo/show.py
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.
../../../demoapp/setup.py
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.
../../../demoapp/cliffdemo/hook.py
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
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 <sphinxext>
.
.. autoprogram-cliff:: cliffdemo.main.DemoApp
:application: cliffdemo
.. autoprogram-cliff:: cliff.demo
:application: cliffdemo
Output
Global Options
cliffdemo.main.DemoApp
Command Options
cliff.demo