summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Hellmann <doug.hellmann@dreamhost.com>2012-07-31 14:01:39 -0400
committerDoug Hellmann <doug.hellmann@dreamhost.com>2012-07-31 15:12:19 -0400
commit8896e385ebc963145677303bf8d6eb134dcf582c (patch)
tree1dce3d965f14f8b9dc27e5ba9b56e2b084014638
parentce0aa4609c76e60ea580afcfee1f8103931b0fee (diff)
downloadcliff-tablib-8896e385ebc963145677303bf8d6eb134dcf582c.tar.gz
Start cliff-tablib repo
This repo is a fork of the original cliff repo, with the cliff parts removed and the rest changed to just package the tablib-based formatters. Signed-off-by: Doug Hellmann <doug.hellmann@dreamhost.com>
-rw-r--r--README.rst12
-rw-r--r--announce.rst29
-rw-r--r--cliff/app.py241
-rw-r--r--cliff/command.py50
-rw-r--r--cliff/commandmanager.py65
-rw-r--r--cliff/display.py86
-rw-r--r--cliff/formatters/__init__.py0
-rw-r--r--cliff/formatters/base.py48
-rw-r--r--cliff/formatters/commaseparated.py35
-rw-r--r--cliff/formatters/shell.py38
-rw-r--r--cliff/formatters/table.py60
-rw-r--r--cliff/help.py77
-rw-r--r--cliff/interactive.py115
-rw-r--r--cliff/lister.py57
-rw-r--r--cliff/show.py46
-rw-r--r--clifftablib/__init__.py (renamed from cliff/__init__.py)0
-rw-r--r--clifftablib/formatters.py (renamed from cliff/formatters/tablibformatters.py)2
-rw-r--r--docs/requirements.txt2
-rw-r--r--docs/source/classes.rst63
-rw-r--r--docs/source/conf.py15
-rw-r--r--docs/source/demoapp.rst258
-rw-r--r--docs/source/developers.rst34
-rw-r--r--docs/source/history.rst57
-rw-r--r--docs/source/index.rst16
-rw-r--r--docs/source/install.rst18
-rw-r--r--docs/source/interactive_mode.rst94
-rw-r--r--docs/source/introduction.rst60
-rw-r--r--docs/source/list_commands.rst85
-rw-r--r--docs/source/show_commands.rst76
-rw-r--r--setup.py39
-rw-r--r--test-requirements.txt3
-rw-r--r--tests/test_app.py170
-rw-r--r--tests/test_command.py22
-rw-r--r--tests/test_commandmanager.py95
-rw-r--r--tests/test_help.py116
-rw-r--r--tox.ini4
36 files changed, 88 insertions, 2100 deletions
diff --git a/README.rst b/README.rst
index d7f9583..aa014d9 100644
--- a/README.rst
+++ b/README.rst
@@ -1,14 +1,16 @@
=======================================================
- cliff -- Command Line Interface Formulation Framework
+ cliff-tablib -- Formatting Extensions for Cliff
=======================================================
.. image:: https://secure.travis-ci.org/dhellmann/cliff.png?branch=master
-cliff is a framework for building command line programs. It uses
-setuptools entry points to provide subcommands, output formatters, and
-other extensions.
+cliff-tablib is a set of formatter extensions for producing JSON,
+YAML, and HTML output in programs created with the cliff
+framework. Installing cliff-tablib activates these formatters for any
+cliff-based programs automatically.
Documentation
=============
-Documentation for cliff is hosted on readthedocs.org at http://readthedocs.org/docs/cliff/en/latest/
+Documentation for cliff-tablib is hosted on readthedocs.org at
+http://cliff-tablib.readthedocs.org/
diff --git a/announce.rst b/announce.rst
index 91bcfac..170703f 100644
--- a/announce.rst
+++ b/announce.rst
@@ -1,6 +1,6 @@
-======================================================================
- cliff -- Command Line Interface Formulation Framework -- version 1.0
-======================================================================
+=================================================
+ cliff-tablib -- Formatting Extensions for Cliff
+=================================================
.. tags:: python cliff release DreamHost
@@ -8,25 +8,22 @@ cliff is a framework for building command line programs. It uses
setuptools entry points to provide subcommands, output formatters, and
other extensions.
+cliff-tablib is a set of formatter extensions for producing JSON,
+YAML, and HTML output. Installing cliff-tablib activates these
+formatters for any cliff-based programs automatically.
+
What's New In This Release?
===========================
-- Add trailing newlines after output from tablib-based formatters
- (JSON, YAML, and HTML). Contributed by Matt Joyce.
-- Some PEP-8 fixes.
-- Refactor the API in ``Command`` to add ``take_action()``
- and make ``run()`` a concrete method. Existing users should only
- need to rename ``run()`` to ``take_action()`` since the function
- signatures have not changed.
-- In ``Lister`` and ``ShowOne`` use ``take_action()`` instead of
- ``get_data()``.
+- This is the first release in which the tablib extensions
+ are being released as a separate project from cliff.
Documentation
=============
-`Documentation for cliff`_ is hosted on `readthedocs.org`_
+`Documentation for cliff-tablib`_ is hosted on `readthedocs.org`_
-.. _Documentation for cliff: http://readthedocs.org/docs/cliff/en/latest/
+.. _Documentation for cliff-tablib: http://readthedocs.org/docs/cliff-tablib/en/latest/
.. _readthedocs.org: http://readthedocs.org
@@ -35,9 +32,9 @@ Installation
Use pip::
- $ pip install cliff
+ $ pip install cliff-tablib
See `the installation guide`_ for more details.
-.. _the installation guide: http://cliff.readthedocs.org/en/latest/install.html
+.. _the installation guide: http://cliff-tablib.readthedocs.org/en/latest/install.html
diff --git a/cliff/app.py b/cliff/app.py
deleted file mode 100644
index bc0b65e..0000000
--- a/cliff/app.py
+++ /dev/null
@@ -1,241 +0,0 @@
-"""Application base class.
-"""
-
-import argparse
-import logging
-import logging.handlers
-import os
-import sys
-
-from .help import HelpAction, HelpCommand
-from .interactive import InteractiveApp
-
-LOG = logging.getLogger(__name__)
-
-
-class App(object):
- """Application base class.
-
- :param description: one-liner explaining the program purpose
- :paramtype description: str
- :param version: application version number
- :paramtype version: str
- :param command_manager: plugin loader
- :paramtype command_manager: cliff.commandmanager.CommandManager
- :param stdin: Standard input stream
- :paramtype stdin: readable I/O stream
- :param stdout: Standard output stream
- :paramtype stdout: writable I/O stream
- :param stderr: Standard error output stream
- :paramtype stderr: writable I/O stream
- :param interactive_app_factory: callable to create an
- interactive application
- :paramtype interactive_app_factory: cliff.interactive.InteractiveApp
- """
-
- NAME = os.path.splitext(os.path.basename(sys.argv[0]))[0]
-
- CONSOLE_MESSAGE_FORMAT = '%(message)s'
- LOG_FILE_MESSAGE_FORMAT = \
- '[%(asctime)s] %(levelname)-8s %(name)s %(message)s'
- DEFAULT_VERBOSE_LEVEL = 1
-
- def __init__(self, description, version, command_manager,
- stdin=None, stdout=None, stderr=None,
- interactive_app_factory=InteractiveApp):
- """Initialize the application.
- """
- self.command_manager = command_manager
- self.command_manager.add_command('help', HelpCommand)
- self.stdin = stdin or sys.stdin
- self.stdout = stdout or sys.stdout
- self.stderr = stderr or sys.stderr
- self.interactive_app_factory = interactive_app_factory
- self.parser = self.build_option_parser(description, version)
- self.interactive_mode = False
-
- def build_option_parser(self, description, version):
- """Return an argparse option parser for this application.
-
- Subclasses may override this method to extend
- the parser with more global options.
-
- :param description: full description of the application
- :paramtype description: str
- :param version: version number for the application
- :paramtype version: str
- """
- parser = argparse.ArgumentParser(
- description=description,
- add_help=False,
- )
- parser.add_argument(
- '--version',
- action='version',
- version='%(prog)s {0}'.format(version),
- )
- parser.add_argument(
- '-v', '--verbose',
- action='count',
- dest='verbose_level',
- default=self.DEFAULT_VERBOSE_LEVEL,
- help='Increase verbosity of output. Can be repeated.',
- )
- parser.add_argument(
- '-q', '--quiet',
- action='store_const',
- dest='verbose_level',
- const=0,
- help='suppress output except warnings and errors',
- )
- parser.add_argument(
- '-h', '--help',
- action=HelpAction,
- nargs=0,
- default=self, # tricky
- help="show this help message and exit",
- )
- parser.add_argument(
- '--debug',
- default=False,
- action='store_true',
- help='show tracebacks on errors',
- )
- return parser
-
- def configure_logging(self):
- """Create logging handlers for any log output.
- """
- root_logger = logging.getLogger('')
-
- # Set up logging to a file
- root_logger.setLevel(logging.DEBUG)
- file_handler = logging.handlers.RotatingFileHandler(
- self.NAME + '.log',
- maxBytes=10240,
- backupCount=1,
- )
- formatter = logging.Formatter(self.LOG_FILE_MESSAGE_FORMAT)
- file_handler.setFormatter(formatter)
- root_logger.addHandler(file_handler)
-
- # Send higher-level messages to the console via stderr
- console = logging.StreamHandler(self.stderr)
- console_level = {0: logging.WARNING,
- 1: logging.INFO,
- 2: logging.DEBUG,
- }.get(self.options.verbose_level, logging.DEBUG)
- console.setLevel(console_level)
- formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT)
- console.setFormatter(formatter)
- root_logger.addHandler(console)
- return
-
- def run(self, argv):
- """Equivalent to the main program for the application.
-
- :param argv: input arguments and options
- :paramtype argv: list of str
- """
- try:
- self.options, remainder = self.parser.parse_known_args(argv)
- self.configure_logging()
- self.interactive_mode = not remainder
- self.initialize_app(remainder)
- except Exception as err:
- if hasattr(self, 'options'):
- debug = self.options.debug
- else:
- debug = True
- if debug:
- LOG.exception(err)
- raise
- else:
- LOG.error(err)
- return 1
- result = 1
- if self.interactive_mode:
- result = self.interact()
- else:
- result = self.run_subcommand(remainder)
- return result
-
- # FIXME(dhellmann): Consider moving these command handling methods
- # to a separate class.
- def initialize_app(self, argv):
- """Hook for subclasses to take global initialization action
- after the arguments are parsed but before a command is run.
- Invoked only once, even in interactive mode.
-
- :param argv: List of arguments, including the subcommand to run.
- Empty for interactive mode.
- """
- return
-
- def prepare_to_run_command(self, cmd):
- """Perform any preliminary work needed to run a command.
-
- :param cmd: command processor being invoked
- :paramtype cmd: cliff.command.Command
- """
- return
-
- def clean_up(self, cmd, result, err):
- """Hook run after a command is done to shutdown the app.
-
- :param cmd: command processor being invoked
- :paramtype cmd: cliff.command.Command
- :param result: return value of cmd
- :paramtype result: int
- :param err: exception or None
- :paramtype err: Exception
- """
- return
-
- def interact(self):
- interpreter = self.interactive_app_factory(self,
- self.command_manager,
- self.stdin,
- self.stdout,
- )
- interpreter.cmdloop()
- return 0
-
- def run_subcommand(self, argv):
- subcommand = self.command_manager.find_command(argv)
- cmd_factory, cmd_name, sub_argv = subcommand
- cmd = cmd_factory(self, self.options)
- err = None
- result = 1
- try:
- self.prepare_to_run_command(cmd)
- full_name = (cmd_name
- if self.interactive_mode
- else ' '.join([self.NAME, cmd_name])
- )
- cmd_parser = cmd.get_parser(full_name)
- parsed_args = cmd_parser.parse_args(sub_argv)
- result = cmd.run(parsed_args)
- except Exception as err:
- if self.options.debug:
- LOG.exception(err)
- else:
- LOG.error(err)
- try:
- self.clean_up(cmd, result, err)
- except Exception as err2:
- if self.options.debug:
- LOG.exception(err2)
- else:
- LOG.error('Could not clean up: %s', err2)
- if self.options.debug:
- raise
- else:
- try:
- self.clean_up(cmd, result, None)
- except Exception as err3:
- if self.options.debug:
- LOG.exception(err3)
- else:
- LOG.error('Could not clean up: %s', err3)
- return result
diff --git a/cliff/command.py b/cliff/command.py
deleted file mode 100644
index 1661313..0000000
--- a/cliff/command.py
+++ /dev/null
@@ -1,50 +0,0 @@
-
-import abc
-import argparse
-import inspect
-
-
-class Command(object):
- """Base class for command plugins.
-
- :param app: Application instance invoking the command.
- :paramtype app: cliff.app.App
- """
- __metaclass__ = abc.ABCMeta
-
- def __init__(self, app, app_args):
- self.app = app
- self.app_args = app_args
- return
-
- def get_description(self):
- """Return the command description.
- """
- return inspect.getdoc(self.__class__) or ''
-
- def get_parser(self, prog_name):
- """Return an :class:`argparse.ArgumentParser`.
- """
- parser = argparse.ArgumentParser(
- description=self.get_description(),
- prog=prog_name,
- )
- return parser
-
- @abc.abstractmethod
- def take_action(self, parsed_args):
- """Override to do something useful.
- """
-
- def run(self, parsed_args):
- """Invoked by the application when the command is run.
-
- Developers implementing commands should override
- :meth:`take_action`.
-
- Developers creating new command base classes (such as
- :class:`Lister` and :class:`ShowOne`) should override this
- method to wrap :meth:`take_action`.
- """
- self.take_action(parsed_args)
- return 0
diff --git a/cliff/commandmanager.py b/cliff/commandmanager.py
deleted file mode 100644
index 135714a..0000000
--- a/cliff/commandmanager.py
+++ /dev/null
@@ -1,65 +0,0 @@
-"""Discover and lookup command plugins.
-"""
-
-import logging
-
-import pkg_resources
-
-
-LOG = logging.getLogger(__name__)
-
-
-class EntryPointWrapper(object):
- """Wrap up a command class already imported to make it look like a plugin.
- """
-
- def __init__(self, name, command_class):
- self.name = name
- self.command_class = command_class
-
- def load(self):
- return self.command_class
-
-
-class CommandManager(object):
- """Discovers commands and handles lookup based on argv data.
-
- :param namespace: String containing the setuptools entrypoint namespace
- for the plugins to be loaded. For example,
- ``'cliff.formatter.list'``.
- """
- def __init__(self, namespace):
- self.commands = {}
- self.namespace = namespace
- self._load_commands()
-
- def _load_commands(self):
- for ep in pkg_resources.iter_entry_points(self.namespace):
- LOG.debug('found command %r', ep.name)
- self.commands[ep.name.replace('_', ' ')] = ep
- return
-
- def __iter__(self):
- return iter(self.commands.items())
-
- def add_command(self, name, command_class):
- self.commands[name] = EntryPointWrapper(name, command_class)
-
- def find_command(self, argv):
- """Given an argument list, find a command and
- return the processor and any remaining arguments.
- """
- search_args = argv[:]
- name = ''
- while search_args:
- if search_args[0].startswith('-'):
- raise ValueError('Invalid command %r' % search_args[0])
- next_val = search_args.pop(0)
- name = '%s %s' % (name, next_val) if name else next_val
- if name in self.commands:
- cmd_ep = self.commands[name]
- cmd_factory = cmd_ep.load()
- return (cmd_factory, name, search_args)
- else:
- raise ValueError('Unknown command %r' %
- (argv,))
diff --git a/cliff/display.py b/cliff/display.py
deleted file mode 100644
index 3c05760..0000000
--- a/cliff/display.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""Application base class for displaying data.
-"""
-import abc
-import logging
-
-import pkg_resources
-
-from .command import Command
-
-
-LOG = logging.getLogger(__name__)
-
-
-class DisplayCommandBase(Command):
- """Command base class for displaying data about a single object.
- """
- __metaclass__ = abc.ABCMeta
-
- def __init__(self, app, app_args):
- super(DisplayCommandBase, self).__init__(app, app_args)
- self.load_formatter_plugins()
-
- @abc.abstractproperty
- def formatter_namespace(self):
- "String specifying the namespace to use for loading formatter plugins."
-
- @abc.abstractproperty
- def formatter_default(self):
- "String specifying the name of the default formatter."
-
- def load_formatter_plugins(self):
- self.formatters = {}
- for ep in pkg_resources.iter_entry_points(self.formatter_namespace):
- try:
- self.formatters[ep.name] = ep.load()()
- except Exception as err:
- LOG.error(err)
- if self.app_args.debug:
- raise
- return
-
- def get_parser(self, prog_name):
- parser = super(DisplayCommandBase, self).get_parser(prog_name)
- formatter_group = parser.add_argument_group(
- title='output formatters',
- description='output formatter options',
- )
- formatter_choices = sorted(self.formatters.keys())
- formatter_default = self.formatter_default
- 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, defaults to %s' % formatter_default,
- )
- formatter_group.add_argument(
- '-c', '--column',
- action='append',
- default=[],
- dest='columns',
- metavar='COLUMN',
- help='specify the column(s) to include, can be repeated',
- )
- for name, formatter in sorted(self.formatters.items()):
- formatter.add_argument_group(parser)
- return parser
-
- @abc.abstractmethod
- def produce_output(self, parsed_args, column_names, data):
- """Use the formatter to generate the output.
-
- :param parsed_args: argparse.Namespace instance with argument values
- :param column_names: sequence of strings containing names
- of output columns
- :param data: iterable with values matching the column names
- """
-
- def run(self, parsed_args):
- self.formatter = self.formatters[parsed_args.formatter]
- column_names, data = self.take_action(parsed_args)
- self.produce_output(parsed_args, column_names, data)
- return 0
diff --git a/cliff/formatters/__init__.py b/cliff/formatters/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/cliff/formatters/__init__.py
+++ /dev/null
diff --git a/cliff/formatters/base.py b/cliff/formatters/base.py
deleted file mode 100644
index 43b8f17..0000000
--- a/cliff/formatters/base.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""Base classes for formatters.
-"""
-
-import abc
-
-
-class Formatter(object):
- __metaclass__ = abc.ABCMeta
-
- @abc.abstractmethod
- def add_argument_group(self, parser):
- """Add any options to the argument parser.
-
- Should use our own argument group.
- """
-
-
-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, 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/commaseparated.py b/cliff/formatters/commaseparated.py
deleted file mode 100644
index 155e0ca..0000000
--- a/cliff/formatters/commaseparated.py
+++ /dev/null
@@ -1,35 +0,0 @@
-"""Output formatters using csv format.
-"""
-
-import csv
-
-from .base import ListFormatter
-
-
-class CSVLister(ListFormatter):
-
- QUOTE_MODES = {
- 'all': csv.QUOTE_ALL,
- 'minimal': csv.QUOTE_MINIMAL,
- 'nonnumeric': csv.QUOTE_NONNUMERIC,
- 'none': csv.QUOTE_NONE,
- }
-
- def add_argument_group(self, parser):
- group = parser.add_argument_group('CSV Formatter')
- group.add_argument(
- '--quote',
- choices=sorted(self.QUOTE_MODES.keys()),
- dest='quote_mode',
- default='nonnumeric',
- help='when to include quotes, defaults to nonnumeric',
- )
-
- def emit_list(self, column_names, data, stdout, parsed_args):
- writer = csv.writer(stdout,
- quoting=self.QUOTE_MODES[parsed_args.quote_mode],
- )
- writer.writerow(column_names)
- for row in data:
- writer.writerow(row)
- return
diff --git a/cliff/formatters/shell.py b/cliff/formatters/shell.py
deleted file mode 100644
index c45dc2b..0000000
--- a/cliff/formatters/shell.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""Output formatters using shell syntax.
-"""
-
-from .base import SingleFormatter
-
-
-class ShellFormatter(SingleFormatter):
-
- def add_argument_group(self, parser):
- group = parser.add_argument_group(
- title='shell formatter',
- description='a format a UNIX shell can parse (variable="value")',
- )
- group.add_argument(
- '--variable',
- action='append',
- default=[],
- dest='variables',
- metavar='VARIABLE',
- help='specify the variable(s) to include, can be repeated',
- )
- group.add_argument(
- '--prefix',
- action='store',
- default='',
- dest='prefix',
- help='add a prefix to all variable names',
- )
-
- def emit_one(self, column_names, data, stdout, parsed_args):
- variable_names = [c.lower().replace(' ', '_')
- for c in column_names
- ]
- desired_columns = parsed_args.variables
- for name, value in zip(variable_names, data):
- if name in desired_columns or not desired_columns:
- stdout.write('%s%s="%s"\n' % (parsed_args.prefix, name, value))
- return
diff --git a/cliff/formatters/table.py b/cliff/formatters/table.py
deleted file mode 100644
index d03bcca..0000000
--- a/cliff/formatters/table.py
+++ /dev/null
@@ -1,60 +0,0 @@
-"""Output formatters using prettytable.
-"""
-
-import prettytable
-
-from .base import ListFormatter, SingleFormatter
-
-
-class TableFormatter(ListFormatter, SingleFormatter):
-
- ALIGNMENTS = {
- int: 'r',
- str: 'l',
- float: 'r',
- }
- try:
- ALIGNMENTS[unicode] = 'l'
- except NameError:
- pass
-
- def add_argument_group(self, parser):
- pass
-
- def emit_list(self, column_names, data, stdout, parsed_args):
- x = prettytable.PrettyTable(column_names)
- x.padding_width = 1
- # Figure out the types of the columns in the
- # first row and set the alignment of the
- # output accordingly.
- data_iter = iter(data)
- try:
- first_row = next(data_iter)
- except StopIteration:
- pass
- else:
- for value, name in zip(first_row, column_names):
- alignment = self.ALIGNMENTS.get(type(value), 'l')
- x.align[name] = alignment
- # Now iterate over the data and add the rows.
- x.add_row(first_row)
- for row in data_iter:
- x.add_row(row)
- formatted = x.get_string(fields=column_names)
- stdout.write(formatted)
- stdout.write('\n')
- return
-
- def emit_one(self, column_names, data, stdout, parsed_args):
- x = prettytable.PrettyTable(field_names=('Field', 'Value'))
- x.padding_width = 1
- # Align all columns left because the values are
- # not all the same type.
- x.align['Field'] = 'l'
- x.align['Value'] = 'l'
- for name, value in zip(column_names, data):
- x.add_row((name, value))
- formatted = x.get_string(fields=('Field', 'Value'))
- stdout.write(formatted)
- stdout.write('\n')
- return
diff --git a/cliff/help.py b/cliff/help.py
deleted file mode 100644
index 9a7f848..0000000
--- a/cliff/help.py
+++ /dev/null
@@ -1,77 +0,0 @@
-import argparse
-import logging
-import sys
-
-from .command import Command
-
-
-class HelpAction(argparse.Action):
- """Provide a custom action so the -h and --help options
- to the main app will print a list of the commands.
-
- The commands are determined by checking the CommandManager
- instance, passed in as the "default" value for the action.
- """
- def __call__(self, parser, namespace, values, option_string=None):
- log = logging.getLogger(__name__)
- app = self.default
- parser.print_help(app.stdout)
- app.stdout.write('\nCommands:\n')
- command_manager = app.command_manager
- for name, ep in sorted(command_manager):
- try:
- factory = ep.load()
- except Exception as err:
- app.stdout.write('Could not load %r\n' % ep)
- continue
- try:
- cmd = factory(self, None)
- except Exception as err:
- app.stdout.write('Could not instantiate %r: %s\n' % (ep, err))
- continue
- one_liner = cmd.get_description().split('\n')[0]
- app.stdout.write(' %-13s %s\n' % (name, one_liner))
- sys.exit(0)
-
-
-class HelpCommand(Command):
- """print detailed help for another command
- """
-
- def get_parser(self, prog_name):
- parser = super(HelpCommand, self).get_parser(prog_name)
- parser.add_argument('cmd',
- nargs='*',
- help='name of the command',
- )
- return parser
-
- def take_action(self, parsed_args):
- if parsed_args.cmd:
- try:
- the_cmd = self.app.command_manager.find_command(
- parsed_args.cmd,
- )
- cmd_factory, cmd_name, search_args = the_cmd
- except ValueError:
- # Did not find an exact match
- cmd = parsed_args.cmd[0]
- fuzzy_matches = [k[0] for k in self.app.command_manager
- if k[0].startswith(cmd)
- ]
- if not fuzzy_matches:
- raise
- self.app.stdout.write('Command "%s" matches:\n' % cmd)
- for fm in fuzzy_matches:
- self.app.stdout.write(' %s\n' % fm)
- return
- cmd = cmd_factory(self.app, search_args)
- full_name = (cmd_name
- if self.app.interactive_mode
- else ' '.join([self.app.NAME, cmd_name])
- )
- cmd_parser = cmd.get_parser(full_name)
- else:
- cmd_parser = self.get_parser(' '.join([self.app.NAME, 'help']))
- cmd_parser.print_help(self.app.stdout)
- return 0
diff --git a/cliff/interactive.py b/cliff/interactive.py
deleted file mode 100644
index 77aea9a..0000000
--- a/cliff/interactive.py
+++ /dev/null
@@ -1,115 +0,0 @@
-"""Application base class.
-"""
-
-import itertools
-import logging
-import logging.handlers
-import shlex
-
-import cmd2
-
-LOG = logging.getLogger(__name__)
-
-
-class InteractiveApp(cmd2.Cmd):
- """Provides "interactive mode" features.
-
- Refer to the cmd2_ and cmd_ documentation for details
- about subclassing and configuring this class.
-
- .. _cmd2: http://packages.python.org/cmd2/index.html
- .. _cmd: http://docs.python.org/library/cmd.html
-
- :param parent_app: The calling application (expected to be derived
- from :class:`cliff.main.App`).
- :param command_manager: A :class:`cliff.commandmanager.CommandManager`
- instance.
- :param stdin: Standard input stream
- :param stdout: Standard output stream
- """
-
- use_rawinput = True
- doc_header = "Shell commands (type help <topic>):"
- app_cmd_header = "Application commands (type help <topic>):"
-
- def __init__(self, parent_app, command_manager, stdin, stdout):
- self.parent_app = parent_app
- self.prompt = '(%s) ' % parent_app.NAME
- self.command_manager = command_manager
- cmd2.Cmd.__init__(self, 'tab', stdin=stdin, stdout=stdout)
-
- def default(self, line):
- # Tie in the the default command processor to
- # dispatch commands known to the command manager.
- # We send the message through our parent app,
- # since it already has the logic for executing
- # the subcommand.
- line_parts = shlex.split(line.parsed.raw)
- self.parent_app.run_subcommand(line_parts)
-
- def completedefault(self, text, line, begidx, endidx):
- # Tab-completion for commands known to the command manager.
- # Does not handle options on the commands.
- if not text:
- completions = sorted(n for n, v in self.command_manager)
- else:
- completions = sorted(n for n, v in self.command_manager
- if n.startswith(text)
- )
- return completions
-
- def help_help(self):
- # Use the command manager to get instructions for "help"
- self.default('help help')
-
- def do_help(self, arg):
- if arg:
- # Check if the arg is a builtin command or something
- # coming from the command manager
- arg_parts = shlex.split(arg)
- method_name = '_'.join(
- itertools.chain(
- ['do'],
- itertools.takewhile(lambda x: not x.startswith('-'),
- arg_parts)
- )
- )
- # Have the command manager version of the help
- # command produce the help text since cmd and
- # cmd2 do not provide help for "help"
- if hasattr(self, method_name):
- return cmd2.Cmd.do_help(self, arg)
- # Dispatch to the underlying help command,
- # which knows how to provide help for extension
- # commands.
- self.default('help ' + arg)
- else:
- cmd2.Cmd.do_help(self, arg)
- cmd_names = [n for n, v in self.command_manager]
- self.print_topics(self.app_cmd_header, cmd_names, 15, 80)
- return
-
- def get_names(self):
- # Override the base class version to filter out
- # things that look like they should be hidden
- # from the user.
- return [n
- for n in cmd2.Cmd.get_names(self)
- if not n.startswith('do__')
- ]
-
- def precmd(self, statement):
- # Pre-process the parsed command in case it looks like one of
- # our subcommands, since cmd2 does not handle multi-part
- # command names by default.
- line_parts = shlex.split(statement.parsed.raw)
- try:
- the_cmd = self.command_manager.find_command(line_parts)
- cmd_factory, cmd_name, sub_argv = the_cmd
- except ValueError:
- # Not a plugin command
- pass
- else:
- statement.parsed.command = cmd_name
- statement.parsed.args = ' '.join(sub_argv)
- return statement
diff --git a/cliff/lister.py b/cliff/lister.py
deleted file mode 100644
index 406be97..0000000
--- a/cliff/lister.py
+++ /dev/null
@@ -1,57 +0,0 @@
-"""Application base class for providing a list of data as output.
-"""
-import abc
-import itertools
-import logging
-
-from .display import DisplayCommandBase
-
-
-LOG = logging.getLogger(__name__)
-
-
-class Lister(DisplayCommandBase):
- """Command base class for providing a list of data as output.
- """
- __metaclass__ = abc.ABCMeta
-
- @property
- def formatter_namespace(self):
- return 'cliff.formatter.list'
-
- @property
- def formatter_default(self):
- return 'table'
-
- @abc.abstractmethod
- def take_action(self, parsed_args):
- """Return a tuple containing the column names and an iterable
- containing the data to be listed.
- """
-
- def produce_output(self, parsed_args, column_names, data):
- if not parsed_args.columns:
- columns_to_include = column_names
- data_gen = data
- else:
- columns_to_include = [c for c in column_names
- if c in parsed_args.columns
- ]
- if not columns_to_include:
- raise ValueError('No recognized column names in %s' %
- str(parsed_args.columns))
- # Set up argument to compress()
- selector = [(c in columns_to_include)
- for c in column_names]
- # Generator expression to only return the parts of a row
- # of data that the user has expressed interest in
- # seeing. We have to convert the compress() output to a
- # list so the table formatter can ask for its length.
- data_gen = (list(itertools.compress(row, selector))
- for row in data)
- self.formatter.emit_list(columns_to_include,
- data_gen,
- self.app.stdout,
- parsed_args,
- )
- return 0
diff --git a/cliff/show.py b/cliff/show.py
deleted file mode 100644
index 39e94b1..0000000
--- a/cliff/show.py
+++ /dev/null
@@ -1,46 +0,0 @@
-"""Application base class for displaying data about a single object.
-"""
-import abc
-import itertools
-import logging
-
-from .display import DisplayCommandBase
-
-
-LOG = logging.getLogger(__name__)
-
-
-class ShowOne(DisplayCommandBase):
- """Command base class for displaying data about a single object.
- """
- __metaclass__ = abc.ABCMeta
-
- @property
- def formatter_namespace(self):
- return 'cliff.formatter.show'
-
- @property
- def formatter_default(self):
- return 'table'
-
- @abc.abstractmethod
- def take_action(self, parsed_args):
- """Return a two-part tuple with a tuple of column names
- and a tuple of values.
- """
-
- def produce_output(self, parsed_args, column_names, data):
- if not parsed_args.columns:
- columns_to_include = column_names
- else:
- columns_to_include = [c for c in column_names
- if c in parsed_args.columns]
- # Set up argument to compress()
- selector = [(c in columns_to_include)
- for c in column_names]
- data = list(itertools.compress(data, selector))
- self.formatter.emit_one(columns_to_include,
- data,
- self.app.stdout,
- parsed_args)
- return 0
diff --git a/cliff/__init__.py b/clifftablib/__init__.py
index e69de29..e69de29 100644
--- a/cliff/__init__.py
+++ b/clifftablib/__init__.py
diff --git a/cliff/formatters/tablibformatters.py b/clifftablib/formatters.py
index 7409a70..d7e5ca0 100644
--- a/cliff/formatters/tablibformatters.py
+++ b/clifftablib/formatters.py
@@ -1,7 +1,7 @@
"""Output formatters using tablib.
"""
-from .base import ListFormatter, SingleFormatter
+from cliff.formatters.base import ListFormatter, SingleFormatter
import tablib
diff --git a/docs/requirements.txt b/docs/requirements.txt
deleted file mode 100644
index 6f01983..0000000
--- a/docs/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-httplib2==0.7.4
-prettytable==0.5
diff --git a/docs/source/classes.rst b/docs/source/classes.rst
deleted file mode 100644
index 14e80e9..0000000
--- a/docs/source/classes.rst
+++ /dev/null
@@ -1,63 +0,0 @@
-===============
- Cliff Classes
-===============
-
-Application
-===========
-
-App
----
-
-.. autoclass:: cliff.app.App
- :members:
-
-InteractiveApp
---------------
-
-.. autoclass:: cliff.interactive.InteractiveApp
- :members:
-
-CommandManager
---------------
-
-.. autoclass:: cliff.commandmanager.CommandManager
- :members:
-
-Command
--------
-
-.. autoclass:: cliff.command.Command
- :members:
-
-ShowOne
--------
-
-.. autoclass:: cliff.show.ShowOne
- :members:
-
-Lister
-------
-
-.. autoclass:: cliff.lister.Lister
- :members:
-
-Formatting Output
-=================
-
-Formatter
----------
-
-.. autoclass:: cliff.formatters.base.Formatter
- :members:
-
-ListFormatter
--------------
-
-.. autoclass:: cliff.formatters.base.ListFormatter
- :members:
-
-SingleFormatter
----------------
-
-.. autoclass:: cliff.formatters.base.SingleFormatter
- :members:
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 9e4b98e..e0bf72a 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -11,6 +11,7 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
+import datetime
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
@@ -40,8 +41,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
-project = u'cliff'
-copyright = u'2012, Doug Hellmann'
+project = u'cliff-tablib'
+copyright = u'%s, Doug Hellmann' % datetime.date.today().year
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -164,7 +165,7 @@ html_static_path = ['_static']
#html_file_suffix = None
# Output file base name for HTML help builder.
-htmlhelp_basename = 'cliffdoc'
+htmlhelp_basename = 'cliff-tablibdoc'
# -- Options for LaTeX output --------------------------------------------------
@@ -183,7 +184,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
- ('index', 'cliff.tex', u'cliff Documentation',
+ ('index', 'cliff-tablib.tex', u'cliff-tablibDocumentation',
u'Doug Hellmann', 'manual'),
]
@@ -213,7 +214,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'cliff', u'cliff Documentation',
+ ('index', 'cliff-tablib', u'cliff-tablib Documentation',
[u'Doug Hellmann'], 1)
]
@@ -227,8 +228,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'cliff', u'cliff Documentation',
- u'Doug Hellmann', 'cliff', 'One line description of project.',
+ ('index', 'cliff-tablib', u'cliff-tablib Documentation',
+ u'Doug Hellmann', 'cliff-tablib', 'One line description of project.',
'Miscellaneous'),
]
diff --git a/docs/source/demoapp.rst b/docs/source/demoapp.rst
deleted file mode 100644
index 66e66cb..0000000
--- a/docs/source/demoapp.rst
+++ /dev/null
@@ -1,258 +0,0 @@
-========================
- 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
-
-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:`initialize_app` method of :class:`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 :func:`prepare_to_run_command` method of :class:`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
-:func:`initialize_app`.
-
-The :func:`clean_up` method of :class:`DemoApp` is invoked after a
-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 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
-
-.. _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
-
-.. _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
---------
-
-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/developers.rst b/docs/source/developers.rst
index 46b9808..539dfb1 100644
--- a/docs/source/developers.rst
+++ b/docs/source/developers.rst
@@ -2,23 +2,17 @@
For Developers
================
-If you would like to contribute to cliff directly, these instructions
-should help you get started. Patches, bug reports, and feature
-requests are all welcome through the `GitHub project
-<https://github.com/dreamhost/cliff>`_. Contributions in the form of
-patches or pull requests are easier to integrate and will receive
-priority attention.
-
-.. 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.
+If you would like to contribute to cliff-tablib directly, these
+instructions should help you get started. Patches, bug reports, and
+feature requests are all welcome through the `GitHub project
+<https://github.com/dreamhost/cliff-tablib>`_. Contributions in the
+form of patches or pull requests are easier to integrate and will
+receive priority attention.
Building Documentation
======================
-The documentation for cliff is written in reStructuredText and
+The documentation for cliff-tablib is written in reStructuredText and
converted to HTML using Sphinx. The build itself is driven by make.
You will need the following packages in order to build the docs:
@@ -36,12 +30,12 @@ documentation::
loading pickled environment... done
building [html]: targets for 1 source files that are out of date
updating environment: 1 added, 1 changed, 0 removed
- reading sources... [100%] index
+ reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
done
preparing documents... done
- writing output... [100%] index
+ writing output... [100%] index
writing additional files... genindex search
copying static files... done
dumping search index... done
@@ -49,17 +43,15 @@ documentation::
build succeeded, 2 warnings.
Build finished. The HTML pages are in build/html.
-
+
The output version of the documentation ends up in
``./docs/build/html`` inside your sandbox.
Running Tests
=============
-.. image:: https://secure.travis-ci.org/dhellmann/cliff.png?branch=master
-
-The test suite for clif uses tox_, which must be installed separately
-(``pip install tox``).
+The test suite for cliff-tablib uses tox_, which must be installed
+separately (``pip install tox``).
To run the tests under Python 2.7 and 3.2, run ``tox`` from the top
level directory of the git repository.
@@ -73,5 +65,3 @@ Add new tests by modifying an existing file or creating new script in
the ``tests`` directory.
.. _tox: http://codespeak.net/tox
-
-.. _developer-templates:
diff --git a/docs/source/history.rst b/docs/source/history.rst
index e391488..e8a39d1 100644
--- a/docs/source/history.rst
+++ b/docs/source/history.rst
@@ -4,59 +4,6 @@
1.0
- - Add trailing newlines after output from tablib-based formatters
- (JSON, YAML, and HTML). Contributed by Matt Joyce.
- - Some :pep:`8` fixes.
- - Refactor the API in :class:`Command` to add :func:`take_action`
- and make :func:`run` a concrete method. Existing users should only
- need to rename :func:`run()` to :func:`take_action()` since the
- function signatures have not changed.
- - In :class:`Lister` and :class:`ShowOne` use :func:`take_action`
- instead of :func:`get_data`.
+ - Initial public release, created by pulling the tablib integration
+ out of cliff and repackaging it.
-0.7
-
- - Clean up interactive mode flag settting.
- - Add support for Python 2.6, contributed by heavenshell.
- - Fix multi-word commands in interactive mode.
-
-0.6
-
- - Pass the non-global argument list to :func:`initialize_app` to be
- used in initialization work.
-
-0.5.1
-
- - Remove pinned version requirement for PrettyTable until the
- OpenStack clients catch up to the API change.
-
-0.5
-
- - Asking for help about a command by prefix lists all matching
- commands.
- - Add formatters for HTML, JSON, and YAML.
-
-0.4
-
- - Add shell formatter for single objects.
- - Add interactive mode.
- - Expand documentation.
-
-0.3
-
- - Add ShowOne base class for commands that show details about single
- objects.
- - Fix a problem with Lister when there is no data to be printed.
-
-0.2
-
- - Incorporate changes from dtroyer to replace use of optparse in App
- with argparse.
- - Added "help" subcommand to replace ``--help`` option handling in
- subcommands.
-
-0.1
-
- - Initial public release.
- - Included App, CommandManager, Lister, csv and table formatters, a
- demo application, and basic documentation.
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 0a13664..bf0ae2b 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,10 +1,11 @@
-=======================================================
- cliff -- Command Line Interface Formulation Framework
-=======================================================
+=============================================
+ cliff-tablib -- tablib Formatters for cliff
+=============================================
-cliff is a framework for building command line programs. It uses
-plugins to define sub-commands, output formatters, and other
-extensions.
+cliff-tablib includes formatters to be used in applications based on
+the cliff_ framework.
+
+.. _cliff: http://pypi.python.org/pypi/cliff
Contents:
@@ -12,11 +13,8 @@ Contents:
:maxdepth: 2
introduction
- demoapp
list_commands
show_commands
- interactive_mode
- classes
install
developers
history
diff --git a/docs/source/install.rst b/docs/source/install.rst
index 1bd67d0..e985a4b 100644
--- a/docs/source/install.rst
+++ b/docs/source/install.rst
@@ -5,33 +5,33 @@
Python Versions
===============
-cliff is being developed under Python 2.7 and tested with Python 3.2.
+cliff-tablib is being developed under Python 2.7 and tested with Python 3.2.
.. _install-basic:
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_::
+cliff-tablib 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_::
- $ pip install cliff
+ $ pip install cliff-tablib
or::
- $ sudo pip install cliff
+ $ sudo pip install cliff-tablib
.. _pip: http://pypi.python.org/pypi/pip
Source Code
===========
-The source is hosted on github: https://github.com/dreamhost/cliff
+The source is hosted on github: https://github.com/dreamhost/cliff-tablib
Reporting Bugs
==============
Please report bugs through the github project:
-https://github.com/dreamhost/cliff/issues
+https://github.com/dreamhost/cliff-tablib/issues
diff --git a/docs/source/interactive_mode.rst b/docs/source/interactive_mode.rst
deleted file mode 100644
index 519e89a..0000000
--- a/docs/source/interactive_mode.rst
+++ /dev/null
@@ -1,94 +0,0 @@
-==================
- 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.
-
-.. _cmd2: http://packages.python.org/cmd2/index.html
-
-.. todo:: Add details about configuring and interacting with the shell (copy from cmd2 docs)
-
-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 this 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)
diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst
index d9312b9..def96eb 100644
--- a/docs/source/introduction.rst
+++ b/docs/source/introduction.rst
@@ -4,61 +4,5 @@
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 four 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:`take_action` method that does the appropriate work.
-
-The Interactive Application
----------------------------
-
-The main program uses an :class:`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
-:class:`InteractiveApp` without subclassing it.
-
-.. _setuptools entry points: http://packages.python.org/distribute/setuptools.html
-
-.. _argparse: http://docs.python.org/library/argparse.html
+argument parsing and then invokes a sub-command to do the work. This
+package adds JSON, YAML, and HTML output formatters to those commands.
diff --git a/docs/source/list_commands.rst b/docs/source/list_commands.rst
index f76188b..62fbebb 100644
--- a/docs/source/list_commands.rst
+++ b/docs/source/list_commands.rst
@@ -1,50 +1,11 @@
-===============
- List Commands
-===============
+========================
+ List Output Formatters
+========================
-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 allow :func:`take_action` to return data to be
-formatted using a user-selectable formatter. Subclasses should provide
-a :func:`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
-:ref:`the files command in the demoapp <demoapp-list>` 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.
-
-::
-
- (.venv)$ cliffdemo files -f csv
- "Name","Size"
- "build",136
- "cliffdemo.log",2690
- "Makefile",5569
- "source",408
+cliff-tablib delivers several new output formatters for list commands.
html
-----
+====
The ``html`` formatter uses tablib_ to produce HTML output as a table.
@@ -69,39 +30,19 @@ The ``html`` formatter uses tablib_ to produce HTML output as a table.
</table>
json
-----
+====
The ``json`` formatter uses tablib_ to produce JSON output.
::
-
+
(.venv)$ cliffdemo files -f json
[{"Name": "build", "Size": 136}, {"Name": "cliffdemo.log", "Size":
3461}, {"Name": "Makefile", "Size": 5569}, {"Name":
"requirements.txt", "Size": 33}, {"Name": "source", "Size": 782}]
-table
------
-
-The ``table`` formatter uses PrettyTable_ to produce output formatted
-for human consumption.
-
-.. _PrettyTable: http://code.google.com/p/prettytable/
-
-::
-
- (.venv)$ cliffdemo files
- +---------------+------+
- | Name | Size |
- +---------------+------+
- | build | 136 |
- | cliffdemo.log | 2546 |
- | Makefile | 5569 |
- | source | 408 |
- +---------------+------+
-
yaml
-----
+====
The ``yaml`` formatter uses tablib_ to produce YAML output as a
sequence of mappings.
@@ -115,14 +56,4 @@ sequence of mappings.
- {Name: requirements.txt, Size: 33}
- {Name: source, Size: 816}
-
-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.
-
-
.. _tablib: https://github.com/kennethreitz/tablib
diff --git a/docs/source/show_commands.rst b/docs/source/show_commands.rst
index e848960..661acec 100644
--- a/docs/source/show_commands.rst
+++ b/docs/source/show_commands.rst
@@ -1,27 +1,6 @@
-===============
- 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 allow :func:`take_action` to return data to be
-formatted using a user-selectable formatter. Subclasses should provide
-a :func:`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 :ref:`the file command in the demoapp
-<demoapp-show>` for details.
-
-Show Output Formatters
-======================
+========================
+ Show Output Formatters
+========================
cliff is delivered with output formatters for show
commands. :class:`ShowOne` adds a command line switch to let the user
@@ -65,47 +44,6 @@ The ``json`` formatter uses tablib_ to produce JSON output.
"Value": 6373}, {"Field": "UID", "Value": 527}, {"Field": "GID",
"Value": 501}, {"Field": "Modified Time", "Value": 1336353173.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
-
-table
------
-
-The ``table`` 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 |
- +---------------+--------------+
-
yaml
----
@@ -121,13 +59,5 @@ sequence of mappings.
- {Field: GID, Value: 501}
- {Field: Modified Time, Value: 1336353173.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.
-
.. _tablib: https://github.com/kennethreitz/tablib
diff --git a/setup.py b/setup.py
index be87252..744342a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,5 @@
#!/usr/bin/env python
-PROJECT = 'cliff'
-
# Change docs/source/conf.py too!
VERSION = '1.0'
@@ -21,15 +19,9 @@ try:
except IOError:
long_description = ''
-install_requires = ['distribute',
- 'PrettyTable',
- 'cmd2',
+install_requires = ['cliff',
'tablib',
]
-try:
- import argparse
-except ImportError:
- install_requires.append('argparse')
##############################################################################
@@ -128,17 +120,16 @@ def find_package_data(
setup(
- name=PROJECT,
+ name='cliff-tablib',
version=VERSION,
- description='Command Line Interface Formulation Framework',
+ description='tablib formatters for cliff',
long_description=long_description,
author='Doug Hellmann',
author_email='doug.hellmann@gmail.com',
- url='https://github.com/dreamhost/cliff',
- download_url='https://github.com/dreamhost/cliff/tarball/master',
+ url='https://github.com/dreamhost/cliff-tablib',
classifiers=['Development Status :: 3 - Alpha',
'License :: OSI Approved :: Apache Software License',
@@ -155,7 +146,7 @@ setup(
scripts=[],
- provides=['cliff',
+ provides=['clifftablib',
],
install_requires=install_requires,
@@ -165,25 +156,21 @@ setup(
# Scan the input for package information
# to grab any data files (text, images, etc.)
# associated with sub-packages.
- package_data=find_package_data(PROJECT,
- package=PROJECT,
+ package_data=find_package_data('clifftablib',
+ package='clifftablib',
only_in_packages=False,
),
entry_points={
'cliff.formatter.list': [
- 'table = cliff.formatters.table:TableFormatter',
- 'csv = cliff.formatters.commaseparated:CSVLister',
- 'yaml = cliff.formatters.tablibformatters:YamlFormatter',
- 'html = cliff.formatters.tablibformatters:HtmlFormatter',
- 'json = cliff.formatters.tablibformatters:JsonFormatter',
+ 'yaml = clifftablib.formatters:YamlFormatter',
+ 'html = clifftablib.formatters:HtmlFormatter',
+ 'json = clifftablib.formatters:JsonFormatter',
],
'cliff.formatter.show': [
- 'table = cliff.formatters.table:TableFormatter',
- 'shell = cliff.formatters.shell:ShellFormatter',
- 'yaml = cliff.formatters.tablibformatters:YamlFormatter',
- 'html = cliff.formatters.tablibformatters:HtmlFormatter',
- 'json = cliff.formatters.tablibformatters:JsonFormatter',
+ 'yaml = clifftablib.formatters:YamlFormatter',
+ 'html = clifftablib.formatters:HtmlFormatter',
+ 'json = clifftablib.formatters:JsonFormatter',
],
},
diff --git a/test-requirements.txt b/test-requirements.txt
index 9a04558..9ce317a 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,7 +2,6 @@ nose
mock
coverage
pep8
-cmd2
+cliff
distribute
-PrettyTable
tablib
diff --git a/tests/test_app.py b/tests/test_app.py
deleted file mode 100644
index 8bff071..0000000
--- a/tests/test_app.py
+++ /dev/null
@@ -1,170 +0,0 @@
-from cliff.app import App
-from cliff.command import Command
-from cliff.commandmanager import CommandManager
-
-import mock
-
-
-def make_app():
- cmd_mgr = CommandManager('cliff.tests')
-
- # Register a command that succeeds
- command = mock.MagicMock(spec=Command)
- command_inst = mock.MagicMock(spec=Command)
- command_inst.run.return_value = 0
- command.return_value = command_inst
- cmd_mgr.add_command('mock', command)
-
- # Register a command that fails
- err_command = mock.Mock(name='err_command', spec=Command)
- err_command_inst = mock.Mock(spec=Command)
- err_command_inst.run = mock.Mock(side_effect=RuntimeError('test exception'))
- err_command.return_value = err_command_inst
- cmd_mgr.add_command('error', err_command)
-
- app = App('testing interactive mode',
- '1',
- cmd_mgr,
- stderr=mock.Mock(), # suppress warning messages
- )
- return app, command
-
-
-def test_no_args_triggers_interactive_mode():
- app, command = make_app()
- app.interact = mock.MagicMock(name='inspect')
- app.run([])
- app.interact.assert_called_once_with()
-
-
-def test_interactive_mode_cmdloop():
- app, command = make_app()
- app.interactive_app_factory = mock.MagicMock(name='interactive_app_factory')
- app.run([])
- app.interactive_app_factory.return_value.cmdloop.assert_called_once_with()
-
-
-def test_initialize_app():
- app, command = make_app()
- app.initialize_app = mock.MagicMock(name='initialize_app')
- app.run(['mock'])
- app.initialize_app.assert_called_once_with(['mock'])
-
-
-def test_prepare_to_run_command():
- app, command = make_app()
- app.prepare_to_run_command = mock.MagicMock(name='prepare_to_run_command')
- app.run(['mock'])
- app.prepare_to_run_command.assert_called_once_with(command())
-
-
-def test_clean_up_success():
- app, command = make_app()
- app.clean_up = mock.MagicMock(name='clean_up')
- app.run(['mock'])
- app.clean_up.assert_called_once_with(command.return_value, 0, None)
-
-
-def test_clean_up_error():
- app, command = make_app()
-
- app.clean_up = mock.MagicMock(name='clean_up')
- app.run(['error'])
-
- app.clean_up.assert_called_once()
- call_args = app.clean_up.call_args_list[0]
- assert call_args == mock.call(mock.ANY, 1, mock.ANY)
- args, kwargs = call_args
- assert isinstance(args[2], RuntimeError)
- assert args[2].args == ('test exception',)
-
-
-def test_clean_up_error_debug():
- app, command = make_app()
-
- app.clean_up = mock.MagicMock(name='clean_up')
- try:
- app.run(['--debug', 'error'])
- except RuntimeError as err:
- assert app.clean_up.call_args_list[0][0][2] is err
- else:
- assert False, 'Should have had an exception'
-
- app.clean_up.assert_called_once()
- call_args = app.clean_up.call_args_list[0]
- assert call_args == mock.call(mock.ANY, 1, mock.ANY)
- args, kwargs = call_args
- assert isinstance(args[2], RuntimeError)
- assert args[2].args == ('test exception',)
-
-
-def test_error_handling_clean_up_raises_exception():
- app, command = make_app()
-
- app.clean_up = mock.MagicMock(
- name='clean_up',
- side_effect=RuntimeError('within clean_up'),
- )
- app.run(['error'])
-
- app.clean_up.assert_called_once()
- call_args = app.clean_up.call_args_list[0]
- assert call_args == mock.call(mock.ANY, 1, mock.ANY)
- args, kwargs = call_args
- assert isinstance(args[2], RuntimeError)
- assert args[2].args == ('test exception',)
-
-
-def test_error_handling_clean_up_raises_exception_debug():
- app, command = make_app()
-
- app.clean_up = mock.MagicMock(
- name='clean_up',
- side_effect=RuntimeError('within clean_up'),
- )
- try:
- app.run(['--debug', 'error'])
- except RuntimeError as err:
- if not hasattr(err, '__context__'):
- # The exception passed to clean_up is not the exception
- # caused *by* clean_up. This test is only valid in python
- # 2 because under v3 the original exception is re-raised
- # with the new one as a __context__ attribute.
- assert app.clean_up.call_args_list[0][0][2] is not err
- else:
- assert False, 'Should have had an exception'
-
- app.clean_up.assert_called_once()
- call_args = app.clean_up.call_args_list[0]
- assert call_args == mock.call(mock.ANY, 1, mock.ANY)
- args, kwargs = call_args
- assert isinstance(args[2], RuntimeError)
- assert args[2].args == ('test exception',)
-
-
-def test_normal_clean_up_raises_exception():
- app, command = make_app()
-
- app.clean_up = mock.MagicMock(
- name='clean_up',
- side_effect=RuntimeError('within clean_up'),
- )
- app.run(['mock'])
-
- app.clean_up.assert_called_once()
- call_args = app.clean_up.call_args_list[0]
- assert call_args == mock.call(mock.ANY, 0, None)
-
-
-def test_normal_clean_up_raises_exception_debug():
- app, command = make_app()
-
- app.clean_up = mock.MagicMock(
- name='clean_up',
- side_effect=RuntimeError('within clean_up'),
- )
- app.run(['--debug', 'mock'])
-
- app.clean_up.assert_called_once()
- call_args = app.clean_up.call_args_list[0]
- assert call_args == mock.call(mock.ANY, 0, None)
diff --git a/tests/test_command.py b/tests/test_command.py
deleted file mode 100644
index 39fde51..0000000
--- a/tests/test_command.py
+++ /dev/null
@@ -1,22 +0,0 @@
-
-from cliff.command import Command
-
-
-class TestCommand(Command):
- """Description of command.
- """
-
- def take_action(self, parsed_args):
- return
-
-
-def test_get_description():
- cmd = TestCommand(None, None)
- desc = cmd.get_description()
- assert desc == "Description of command.\n "
-
-
-def test_get_parser():
- cmd = TestCommand(None, None)
- parser = cmd.get_parser('NAME')
- assert parser.prog == 'NAME'
diff --git a/tests/test_commandmanager.py b/tests/test_commandmanager.py
deleted file mode 100644
index 1945f2e..0000000
--- a/tests/test_commandmanager.py
+++ /dev/null
@@ -1,95 +0,0 @@
-
-import mock
-
-from cliff.commandmanager import CommandManager
-
-
-class TestCommand(object):
- @classmethod
- def load(cls):
- return cls
-
- def __init__(self):
- return
-
-
-class TestCommandManager(CommandManager):
- def _load_commands(self):
- self.commands = {
- 'one': TestCommand,
- 'two words': TestCommand,
- 'three word command': TestCommand,
- }
-
-
-def test_lookup_and_find():
- def check(mgr, argv):
- cmd, name, remaining = mgr.find_command(argv)
- assert cmd
- assert name == ' '.join(argv)
- assert not remaining
- mgr = TestCommandManager('test')
- for expected in [['one'],
- ['two', 'words'],
- ['three', 'word', 'command'],
- ]:
- yield check, mgr, expected
- return
-
-
-def test_lookup_with_remainder():
- def check(mgr, argv):
- cmd, name, remaining = mgr.find_command(argv)
- assert cmd
- assert remaining == ['--opt']
- mgr = TestCommandManager('test')
- for expected in [['one', '--opt'],
- ['two', 'words', '--opt'],
- ['three', 'word', 'command', '--opt'],
- ]:
- yield check, mgr, expected
- return
-
-
-def test_find_invalid_command():
- mgr = TestCommandManager('test')
- def check_one(argv):
- try:
- mgr.find_command(argv)
- except ValueError as err:
- assert '-b' in ('%s' % err)
- else:
- assert False, 'expected a failure'
- for argv in [['a', '-b'],
- ['-b'],
- ]:
- yield check_one, argv
-
-
-def test_find_unknown_command():
- mgr = TestCommandManager('test')
- try:
- mgr.find_command(['a', 'b'])
- except ValueError as err:
- assert "['a', 'b']" in ('%s' % err)
- else:
- assert False, 'expected a failure'
-
-
-def test_add_command():
- mgr = TestCommandManager('test')
- mock_cmd = mock.Mock()
- mgr.add_command('mock', mock_cmd)
- found_cmd, name, args = mgr.find_command(['mock'])
- assert found_cmd is mock_cmd
-
-
-def test_load_commands():
- testcmd = mock.Mock(name='testcmd')
- testcmd.name.replace.return_value = 'test'
- mock_pkg_resources = mock.Mock(return_value=[testcmd])
- with mock.patch('pkg_resources.iter_entry_points', mock_pkg_resources) as iter_entry_points:
- mgr = CommandManager('test')
- assert iter_entry_points.called_once_with('test')
- names = [n for n, v in mgr]
- assert names == ['test']
diff --git a/tests/test_help.py b/tests/test_help.py
deleted file mode 100644
index 6d67356..0000000
--- a/tests/test_help.py
+++ /dev/null
@@ -1,116 +0,0 @@
-try:
- from StringIO import StringIO
-except:
- from io import StringIO
-
-import mock
-
-from cliff.app import App
-from cliff.command import Command
-from cliff.commandmanager import CommandManager
-from cliff.help import HelpCommand
-
-
-class TestParser(object):
-
- def print_help(self, stdout):
- stdout.write('TestParser')
-
-
-class TestCommand(Command):
-
- @classmethod
- def load(cls):
- return cls
-
- def get_parser(self, ignore):
- # Make it look like this class is the parser
- # so parse_args() is called.
- return TestParser()
-
- def take_action(self, args):
- return
-
-
-class TestCommandManager(CommandManager):
- def _load_commands(self):
- self.commands = {
- 'one': TestCommand,
- 'two words': TestCommand,
- 'three word command': TestCommand,
- }
-
-
-def test_show_help_for_command():
- # FIXME(dhellmann): Are commands tied too closely to the app? Or
- # do commands know too much about apps by using them to get to the
- # command manager?
- stdout = StringIO()
- app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout)
- app.NAME = 'test'
- help_cmd = HelpCommand(app, mock.Mock())
- parser = help_cmd.get_parser('test')
- parsed_args = parser.parse_args(['one'])
- try:
- help_cmd.run(parsed_args)
- except SystemExit:
- pass
- assert stdout.getvalue() == 'TestParser'
-
-
-def test_list_matching_commands():
- # FIXME(dhellmann): Are commands tied too closely to the app? Or
- # do commands know too much about apps by using them to get to the
- # command manager?
- stdout = StringIO()
- app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout)
- app.NAME = 'test'
- help_cmd = HelpCommand(app, mock.Mock())
- parser = help_cmd.get_parser('test')
- parsed_args = parser.parse_args(['t'])
- try:
- help_cmd.run(parsed_args)
- except SystemExit:
- pass
- help_output = stdout.getvalue()
- assert 'Command "t" matches:' in help_output
- assert 'two' in help_output
- assert 'three' in help_output
-
-
-def test_list_matching_commands_no_match():
- # FIXME(dhellmann): Are commands tied too closely to the app? Or
- # do commands know too much about apps by using them to get to the
- # command manager?
- stdout = StringIO()
- app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout)
- app.NAME = 'test'
- help_cmd = HelpCommand(app, mock.Mock())
- parser = help_cmd.get_parser('test')
- parsed_args = parser.parse_args(['z'])
- try:
- help_cmd.run(parsed_args)
- except SystemExit:
- pass
- except ValueError:
- pass
- else:
- assert False, 'Should have seen a ValueError'
-
-
-def test_show_help_for_help():
- # FIXME(dhellmann): Are commands tied too closely to the app? Or
- # do commands know too much about apps by using them to get to the
- # command manager?
- stdout = StringIO()
- app = App('testing', '1', TestCommandManager('cliff.test'), stdout=stdout)
- app.NAME = 'test'
- help_cmd = HelpCommand(app, mock.Mock())
- parser = help_cmd.get_parser('test')
- parsed_args = parser.parse_args([])
- try:
- help_cmd.run(parsed_args)
- except SystemExit:
- pass
- help_text = stdout.getvalue()
- assert 'usage: test help [-h]' in help_text
diff --git a/tox.ini b/tox.ini
index 89ad368..470439d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,7 +2,7 @@
envlist = py27,py32,pep8
[testenv]
-commands = nosetests -d --with-coverage --cover-inclusive --cover-package cliff []
+commands = nosetests -d --with-coverage --cover-inclusive --cover-package clifftablib []
deps =
nose
mock
@@ -10,4 +10,4 @@ deps =
[testenv:pep8]
deps = pep8
-commands = pep8 --repeat --ignore=E501 --ignore=E123 --show-source cliff
+commands = pep8 --repeat --ignore=E501 --ignore=E123 --show-source clifftablib