From 63ff2dbdd1642fb4e44139accf3207f4988e81e7 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 25 Apr 2012 17:02:35 -0400 Subject: add some real documentation --- docs/source/classes.rst | 39 ++++++++ docs/source/demoapp.rst | 217 ++++++++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 11 ++- docs/source/introduction.rst | 55 +++++++++++ docs/source/list_commands.rst | 52 ++++++++++ 5 files changed, 369 insertions(+), 5 deletions(-) create mode 100644 docs/source/classes.rst create mode 100644 docs/source/demoapp.rst create mode 100644 docs/source/introduction.rst create mode 100644 docs/source/list_commands.rst (limited to 'docs') diff --git a/docs/source/classes.rst b/docs/source/classes.rst new file mode 100644 index 0000000..1777590 --- /dev/null +++ b/docs/source/classes.rst @@ -0,0 +1,39 @@ +=============== + Cliff Classes +=============== + +App +=== + +.. autoclass:: cliff.app.App + :members: + +CommandManager +============== + +.. autoclass:: cliff.commandmanager.CommandManager + :members: + +Command +======= + +.. autoclass:: cliff.command.Command + :members: + +Lister +====== + +.. autoclass:: cliff.lister.Lister + :members: + +Formatter +========= + +.. autoclass:: cliff.formatters.base.Formatter + :members: + +ListFormatter +============= + +.. autoclass:: cliff.formatters.base.ListFormatter + :members: diff --git a/docs/source/demoapp.rst b/docs/source/demoapp.rst new file mode 100644 index 0000000..98242a3 --- /dev/null +++ b/docs/source/demoapp.rst @@ -0,0 +1,217 @@ +======================== + 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, run:: + + (.venv)$ cliffdemo -h + +To see a list of commands availble from the plugins, run:: + + (.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, include the command name on the +command line:: + + (.venv)$ cliffdemo files --help + +The Source +========== + +The ``cliffdemo`` application is defined in a ``cliffdemo`` package +containing several modules. + +main.py +------- + +The main application is defined in ``main.py``: + +.. literalinclude:: ../../demoapp/cliffdemo/main.py + :linenos: + +The :class:`DemoApp` class inherits from :class:`App` and overrides +:func:`__init__` to set the program description and version number. It +also passes a :class:`CommandManager` instance configured to look for +plugins in the ``cliff.demo`` namespace. + +The :func:`prepare_to_run_command` method of :class:`DemoApp` will be +invoked after the main program arguments are parsed and the command is +identified, but before the command is given its arguments and +run. This hook is intended for opening connections to remote web +services, databases, etc. using arguments passed to the main +application. + +The :func:`clean_up` method of :class:`DemoApp` is invoked after the +command runs. If the command raised an exception, the exception object +is passed to :func:`clean_up`. Otherwise the ``err`` argument is +``None``. + +The :func:`main` function defined in ``main.py`` is registered as a +console script entry point so that :class:`DemoApp` can be run from +the command line (see the discussion of ``setup.py`` below). + +simple.py +--------- + +Two commands are defined in ``simple.py``: + +.. literalinclude:: ../../demoapp/cliffdemo/simple.py + :linenos: + +:class:`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! + +:class:`Error` always raises a :class:`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 148, in run + result = cmd.run(parsed_args) + File ".../demoapp/cliffdemo/simple.py", line 24, in run + 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 + load_entry_point('cliffdemo==0.1', 'console_scripts', 'cliffdemo')() + File ".../demoapp/cliffdemo/main.py", line 30, in main + return myapp.run(argv) + File ".../cliff/app.py", line 148, in run + result = cmd.run(parsed_args) + File ".../demoapp/cliffdemo/simple.py", line 24, in run + raise RuntimeError('this is the expected exception') + RuntimeError: this is the expected exception + +.. _demoapp-list: + +list.py +------- + +``list.py`` includes a single command derived from +:class:`cliff.lister.Lister` which prints a list of the files in the +current directory. + +.. literalinclude:: ../../demoapp/cliffdemo/list.py + :linenos: + +:class:`Files` prepares the data, and :class:`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 + +setup.py +-------- + +The demo application is packaged using distribute_, the modern +implementation of setuptools. + +.. literalinclude:: ../../demoapp/setup.py + :linenos: + +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. + +.. _distribute: http://packages.python.org/distribute/ diff --git a/docs/source/index.rst b/docs/source/index.rst index 39ac06a..c52580b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,14 +3,18 @@ ======================================================= cliff is a framework for building command line programs. It uses -setuptools `entry points`_ to provide subcommands, output formatters, and -other extensions. +plugins to define sub-commands, output formatters, and other +extensions. Contents: .. toctree:: :maxdepth: 2 + introduction + demoapp + list_commands + classes Indices and tables @@ -19,6 +23,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - -.. _entry points: http://packages.python.org/distribute/setuptools.html - diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst new file mode 100644 index 0000000..afef2bf --- /dev/null +++ b/docs/source/introduction.rst @@ -0,0 +1,55 @@ +============== + 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 three objects that are combined to create a +useful command line program. + +The Application +--------------- + +An :class:`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 :class:`cliff.commandmanager.CommandManager` knows how to load +individual command plugins. The default implementation uses +`setuptools entry points`_ but any mechanism for loading commands can +be used by replacing the default :class:`CommandManager` when +instantiating an :class:`App`. + +The Command +----------- + +The :class:`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 :class:`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 +:func:`run` method that does the appropriate work. + +.. _setuptools entry points: http://packages.python.org/distribute/setuptools.html + +.. _argparse: http://docs.python.org/library/argparse.html diff --git a/docs/source/list_commands.rst b/docs/source/list_commands.rst new file mode 100644 index 0000000..84dfe6e --- /dev/null +++ b/docs/source/list_commands.rst @@ -0,0 +1,52 @@ +=============== + 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 :class:`cliff.lister.Lister` base class API extends +:class:`Command` to add a :func:`get_data` method. Subclasses should +provide a :func:`get_data` 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 :ref:`the files command in the demoapp ` +for details. + +List Output Formatters +====================== + +cliff is delivered with two output formatters for list +commands. :class:`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. + +PrettyTable +----------- + +The ``PrettyTable`` formatter uses PrettyTable_ to produce output +formatted for human consumption. + +.. _PrettyTable: http://code.google.com/p/prettytable/ + +Creating Your Own Formatter +--------------------------- + +If the standard formatters do not meet your needs, you can bundle +another formatter with your program by subclassing from +:class:`cliff.formatters.base.ListFormatter` and registering the +plugin in the ``cliff.formatter.list`` namespace. + -- cgit v1.2.1