summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Hellmann <doug.hellmann@dreamhost.com>2012-04-27 19:56:45 -0400
committerDoug Hellmann <doug.hellmann@dreamhost.com>2012-04-27 19:56:45 -0400
commit556495e530c9cb2dc67300d1f199780e247921dc (patch)
treee7c63773f3001742dce82082efb13a52939326b5
parentb8f3ad548d02eff5fe1b3c8d8515fab9db888204 (diff)
downloadcliff-tablib-556495e530c9cb2dc67300d1f199780e247921dc.tar.gz
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
-rw-r--r--cliff/formatters/base.py26
-rw-r--r--cliff/formatters/table.py20
-rw-r--r--cliff/show.py65
-rw-r--r--demoapp/cliffdemo/show.py31
-rw-r--r--demoapp/setup.py1
-rw-r--r--docs/source/classes.rst12
-rw-r--r--docs/source/demoapp.rst29
-rw-r--r--docs/source/index.rst1
-rw-r--r--docs/source/show_commands.rst58
-rw-r--r--setup.py5
10 files changed, 244 insertions, 4 deletions
diff --git a/cliff/formatters/base.py b/cliff/formatters/base.py
index 8634f41..e8ec524 100644
--- a/cliff/formatters/base.py
+++ b/cliff/formatters/base.py
@@ -18,9 +18,33 @@ class Formatter(object):
class ListFormatter(Formatter):
+ """Base class for formatters that know how to deal with multiple objects.
+ """
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
- def emit_list(self, column_names, data, stdout):
+ def emit_list(self, column_names, data, stdout, parsed_args):
"""Format and print the list from the iterable data source.
+
+ :param column_names: names of the columns
+ :param data: iterable data source, one tuple per object
+ with values in order of column names
+ :param stdout: output stream where data should be written
+ :param parsed_args: argparse namespace from our local options
+ """
+
+
+class SingleFormatter(Formatter):
+ """Base class for formatters that work with single objects.
+ """
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ def emit_one(self, column_names, data, stdout, parsed_args):
+ """Format and print the values associated with the single object.
+
+ :param column_names: names of the columns
+ :param data: iterable data source with values in order of column names
+ :param stdout: output stream where data should be written
+ :param parsed_args: argparse namespace from our local options
"""
diff --git a/cliff/formatters/table.py b/cliff/formatters/table.py
index 43066b4..21960a8 100644
--- a/cliff/formatters/table.py
+++ b/cliff/formatters/table.py
@@ -3,10 +3,10 @@
import prettytable
-from .base import ListFormatter
+from .base import ListFormatter, SingleFormatter
-class TableLister(ListFormatter):
+class TableFormatter(ListFormatter, SingleFormatter):
ALIGNMENTS = {
int: 'r',
@@ -48,3 +48,19 @@ class TableLister(ListFormatter):
stdout.write(formatted)
stdout.write('\n')
return
+
+ def emit_one(self, column_names, data, stdout, parsed_args):
+ x = prettytable.PrettyTable(('Field', 'Value'))
+ x.set_padding_width(1)
+ # Align all columns left because the values are
+ # not all the same type.
+ x.set_field_align('Field', 'l')
+ x.set_field_align('Value', 'l')
+ desired_columns = parsed_args.columns
+ for name, value in zip(column_names, data):
+ if name in desired_columns or not desired_columns:
+ x.add_row((name, value))
+ formatted = x.get_string(fields=('Field', 'Value'))
+ stdout.write(formatted)
+ stdout.write('\n')
+ return
diff --git a/cliff/show.py b/cliff/show.py
new file mode 100644
index 0000000..9f77703
--- /dev/null
+++ b/cliff/show.py
@@ -0,0 +1,65 @@
+"""Application base class for displaying data about a single object.
+"""
+import abc
+import logging
+
+import pkg_resources
+
+from .command import Command
+
+
+LOG = logging.getLogger(__name__)
+
+
+class ShowOne(Command):
+ """Command base class for displaying data about a single object.
+ """
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self, app, app_args):
+ super(ShowOne, self).__init__(app, app_args)
+ self.load_formatter_plugins()
+
+ def load_formatter_plugins(self):
+ self.formatters = {}
+ for ep in pkg_resources.iter_entry_points('cliff.formatter.show'):
+ try:
+ self.formatters[ep.name] = ep.load()()
+ except Exception as err:
+ LOG.error(err)
+ if self.app_args.debug:
+ raise
+
+ def get_parser(self, prog_name):
+ parser = super(ShowOne, self).get_parser(prog_name)
+ formatter_group = parser.add_argument_group(
+ title='Output Formatters',
+ description='List output formatter options',
+ )
+ formatter_choices = sorted(self.formatters.keys())
+ formatter_default = 'table'
+ if formatter_default not in formatter_choices:
+ formatter_default = formatter_choices[0]
+ formatter_group.add_argument(
+ '-f', '--format',
+ dest='formatter',
+ action='store',
+ choices=formatter_choices,
+ default=formatter_default,
+ help='the output format to use, defaults to %s' % formatter_default,
+ )
+ for name, formatter in sorted(self.formatters.items()):
+ formatter.add_argument_group(parser)
+ return parser
+
+ @abc.abstractmethod
+ def get_data(self, parsed_args):
+ """Return a two-part tuple with a tuple of column names
+ and a tuple of values.
+ """
+
+ def run(self, parsed_args):
+ column_names, data = self.get_data(parsed_args)
+ formatter = self.formatters[parsed_args.formatter]
+ formatter.emit_one(column_names, data, self.app.stdout, parsed_args)
+ return 0
diff --git a/demoapp/cliffdemo/show.py b/demoapp/cliffdemo/show.py
new file mode 100644
index 0000000..54d98be
--- /dev/null
+++ b/demoapp/cliffdemo/show.py
@@ -0,0 +1,31 @@
+import logging
+import os
+
+from cliff.show import ShowOne
+
+
+class File(ShowOne):
+ "Show details about a file"
+
+ log = logging.getLogger(__name__)
+
+ def get_parser(self, prog_name):
+ parser = super(File, self).get_parser(prog_name)
+ parser.add_argument('filename', nargs='?', default='.')
+ return parser
+
+ def get_data(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)
diff --git a/demoapp/setup.py b/demoapp/setup.py
index f24e792..83c957e 100644
--- a/demoapp/setup.py
+++ b/demoapp/setup.py
@@ -166,6 +166,7 @@ setup(
'two_part = cliffdemo.simple:Simple',
'error = cliffdemo.simple:Error',
'files = cliffdemo.list:Files',
+ 'file = cliffdemo.show:File',
],
},
diff --git a/docs/source/classes.rst b/docs/source/classes.rst
index 1777590..a1cadbb 100644
--- a/docs/source/classes.rst
+++ b/docs/source/classes.rst
@@ -20,6 +20,12 @@ Command
.. autoclass:: cliff.command.Command
:members:
+ShowOne
+=======
+
+.. autoclass:: cliff.show.ShowOne
+ :members:
+
Lister
======
@@ -37,3 +43,9 @@ ListFormatter
.. autoclass:: cliff.formatters.base.ListFormatter
:members:
+
+SingleFormatter
+===============
+
+.. autoclass:: cliff.formatters.base.SingleFormatter
+ :members:
diff --git a/docs/source/demoapp.rst b/docs/source/demoapp.rst
index 9c1ece8..1832077 100644
--- a/docs/source/demoapp.rst
+++ b/docs/source/demoapp.rst
@@ -199,6 +199,35 @@ output formatter and printing the data to the console.
"Makefile",5569
"source",408
+.. _demoapp-show:
+
+show.py
+-------
+
+``show.py`` includes a single command derived from
+:class:`cliff.show.ShowOne` which prints the properties of the named
+file.
+
+.. literalinclude:: ../../demoapp/cliffdemo/show.py
+ :linenos:
+
+:class:`File` prepares the data, and :class:`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
--------
diff --git a/docs/source/index.rst b/docs/source/index.rst
index d98a6cb..8994669 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -14,6 +14,7 @@ Contents:
introduction
demoapp
list_commands
+ show_commands
classes
install
developers
diff --git a/docs/source/show_commands.rst b/docs/source/show_commands.rst
new file mode 100644
index 0000000..c3b8914
--- /dev/null
+++ b/docs/source/show_commands.rst
@@ -0,0 +1,58 @@
+===============
+ 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 :class:`cliff.show.ShowOne` 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 contains the data values associated with those
+names. See the description of :ref:`the file command in the demoapp
+<demoapp-show>` for details.
+
+Show Output Formatters
+======================
+
+cliff is delivered with output formatters for show
+commands. :class:`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.
+
+PrettyTable
+-----------
+
+The ``PrettyTable`` formatter uses PrettyTable_ to produce output
+formatted for human consumption.
+
+.. _PrettyTable: http://code.google.com/p/prettytable/
+
+::
+
+ (.venv)$ cliffdemo file setup.py
+ +---------------+--------------+
+ | Field | Value |
+ +---------------+--------------+
+ | Name | setup.py |
+ | Size | 5825 |
+ | UID | 502 |
+ | GID | 20 |
+ | Modified Time | 1335569964.0 |
+ +---------------+--------------+
+
+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.ShowFormatter` and registering the
+plugin in the ``cliff.formatter.show`` namespace.
+
diff --git a/setup.py b/setup.py
index 7dd43ff..6079333 100644
--- a/setup.py
+++ b/setup.py
@@ -162,9 +162,12 @@ setup(
entry_points={
'cliff.formatter.list': [
- 'table = cliff.formatters.table:TableLister',
+ 'table = cliff.formatters.table:TableFormatter',
'csv = cliff.formatters.commaseparated:CSVLister',
],
+ 'cliff.formatter.show': [
+ 'table = cliff.formatters.table:TableFormatter',
+ ],
},
zip_safe=False,