diff options
57 files changed, 8 insertions, 4314 deletions
diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8a4d01b..0000000 --- a/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -*.py[co] - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -.venv - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox - -#Translations -*.mo - -#Mr Developer -.mr.developer.cfg - -#sample output -*.log -*.log.* diff --git a/.gitreview b/.gitreview deleted file mode 100644 index 212f062..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=stackforge/cliff.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ea35c0e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: python -python: - - 2.7 - - 3.2 - - 3.3 - - pypy -install: pip install -r test-requirements.txt -script: nosetests -d
\ No newline at end of file diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index 3ba612b..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,10 +0,0 @@ -Changes to cliff should be submitted for review via the Gerrit tool, -following the workflow documented at: - - http://wiki.openstack.org/GerritWorkflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/python-cliff diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d645695..0000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bb56f02..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include setup.py -recursive-include docs *.rst *.py *.html *.css *.js *.png *.txt -recursive-include demoapp *.py -recursive-include tests *.py -include tox.ini - diff --git a/Makefile b/Makefile deleted file mode 100644 index 65c420d..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -help: - @echo "release - package and upload a release" - @echo "sdist - package" - @echo "docs - generate HTML documentation" - @echo "clean - remove build artifacts" - -release: docs - rm -rf dist build - python setup.py sdist upload - -sdist: docs - python setup.py sdist - ls -l dist - -clean: - rm -rf dist build *.egg-info - (cd docs && make clean) - -.PHONY: docs -docs: - (cd docs && make clean html) @@ -2,11 +2,15 @@ cliff -- Command Line Interface Formulation Framework ======================================================= +cliff is now part of the OpenStack Oslo program. + cliff is a framework for building command line programs. It uses -setuptools entry points to provide subcommands, output formatters, and +`setuptools entry points`_ to provide subcommands, output formatters, and other extensions. -Documentation -============= +.. _setuptools entry points: http://pythonhosted.org/setuptools/pkg_resources.html#convenience-api -Documentation for cliff is hosted on readthedocs.org at http://readthedocs.org/docs/cliff/en/latest/ +* Free software: Apache license +* Documentation: http://docs.openstack.org/developer/cliff +* Source: http://git.openstack.org/cgit/openstack/cliff +* Bugs: https://bugs.launchpad.net/python-cliff diff --git a/announce.rst b/announce.rst deleted file mode 100644 index 09e57f8..0000000 --- a/announce.rst +++ /dev/null @@ -1,37 +0,0 @@ -======================================================================== - cliff -- Command Line Interface Formulation Framework -- version 1.4.5 -======================================================================== - -.. tags:: python cliff release DreamHost - -cliff is a framework for building command line programs. It uses -setuptools entry points to provide subcommands, output formatters, and -other extensions. - -What's New In This Release? -=========================== - -- Update the pyparsing dependency to a version that supports both - Python 2 and Python 3. -- Add PyPy testing. - -Documentation -============= - -`Documentation for cliff`_ is hosted on `readthedocs.org`_ - -.. _Documentation for cliff: http://readthedocs.org/docs/cliff/en/latest/ - -.. _readthedocs.org: http://readthedocs.org - -Installation -============ - -Use pip:: - - $ pip install cliff - -See `the installation guide`_ for more details. - -.. _the installation guide: http://cliff.readthedocs.org/en/latest/install.html - diff --git a/cliff/__init__.py b/cliff/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/cliff/__init__.py +++ /dev/null diff --git a/cliff/app.py b/cliff/app.py deleted file mode 100644 index 4a93801..0000000 --- a/cliff/app.py +++ /dev/null @@ -1,305 +0,0 @@ -"""Application base class. -""" - -import argparse -import codecs -import locale -import logging -import logging.handlers -import os -import sys - -from .complete import CompleteCommand -from .help import HelpAction, HelpCommand -from .interactive import InteractiveApp - -# Make sure the cliff library has a logging handler -# in case the app developer doesn't set up logging. -# For py26 compat, create a NullHandler - -if hasattr(logging, 'NullHandler'): - NullHandler = logging.NullHandler -else: - class NullHandler(logging.Handler): - def handle(self, record): - pass - - def emit(self, record): - pass - - def createLock(self): - self.lock = None - -logging.getLogger('cliff').addHandler(NullHandler()) - - -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 - DEFAULT_OUTPUT_ENCODING = 'utf-8' - - 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.command_manager.add_command('complete', CompleteCommand) - self._set_streams(stdin, stdout, stderr) - self.interactive_app_factory = interactive_app_factory - self.parser = self.build_option_parser(description, version) - self.interactive_mode = False - self.interpreter = None - - def _set_streams(self, stdin, stdout, stderr): - locale.setlocale(locale.LC_ALL, '') - if sys.version_info[:2] == (2, 6): - # Configure the input and output streams. If a stream is - # provided, it must be configured correctly by the - # caller. If not, make sure the versions of the standard - # streams used by default are wrapped with encodings. This - # works around a problem with Python 2.6 fixed in 2.7 and - # later (http://hg.python.org/cpython/rev/e60ef17561dc/). - lang, encoding = locale.getdefaultlocale() - encoding = (getattr(sys.stdout, 'encoding', None) - or encoding - or self.DEFAULT_OUTPUT_ENCODING - ) - self.stdin = stdin or codecs.getreader(encoding)(sys.stdin) - self.stdout = stdout or codecs.getwriter(encoding)(sys.stdout) - self.stderr = stderr or codecs.getwriter(encoding)(sys.stderr) - else: - self.stdin = stdin or sys.stdin - self.stdout = stdout or sys.stdout - self.stderr = stderr or sys.stderr - - def build_option_parser(self, description, version, - argparse_kwargs=None): - """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 - :param argparse_kwargs: extra keyword argument passed to the - ArgumentParser constructor - :paramtype extra_kwargs: dict - """ - argparse_kwargs = argparse_kwargs or {} - parser = argparse.ArgumentParser( - description=description, - add_help=False, - **argparse_kwargs - ) - 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( - '--log-file', - action='store', - default=None, - help='Specify a file to log output. Disabled by default.', - ) - 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('') - root_logger.setLevel(logging.DEBUG) - - # Set up logging to a file - if self.options.log_file: - file_handler = logging.FileHandler( - filename=self.options.log_file, - ) - formatter = logging.Formatter(self.LOG_FILE_MESSAGE_FORMAT) - file_handler.setFormatter(formatter) - root_logger.addHandler(file_handler) - - # Always 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): - self.interpreter = self.interactive_app_factory(self, - self.command_manager, - self.stdin, - self.stdout, - ) - self.interpreter.cmdloop() - return 0 - - def run_subcommand(self, argv): - try: - subcommand = self.command_manager.find_command(argv) - except ValueError as err: - if self.options.debug: - raise - else: - LOG.error(err) - return 2 - 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 116ae5c..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 2f83eb2..0000000 --- a/cliff/commandmanager.py +++ /dev/null @@ -1,71 +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'``. - :param convert_underscores: Whether cliff should convert underscores to - to spaces in entry_point commands. - """ - def __init__(self, namespace, convert_underscores=True): - self.commands = {} - self.namespace = namespace - self.convert_underscores = convert_underscores - self._load_commands() - - def _load_commands(self): - for ep in pkg_resources.iter_entry_points(self.namespace): - LOG.debug('found command %r', ep.name) - cmd_name = (ep.name.replace('_', ' ') - if self.convert_underscores - else ep.name) - self.commands[cmd_name] = 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/complete.py b/cliff/complete.py deleted file mode 100644 index fa1dd5c..0000000 --- a/cliff/complete.py +++ /dev/null @@ -1,190 +0,0 @@ - -"""Bash completion for the CLI. -""" - -import logging - -import six -import stevedore - -from cliff import command - - -class CompleteDictionary: - """dictionary for bash completion - """ - - def __init__(self): - self._dictionary = {} - - def add_command(self, command, actions): - optstr = ' '.join(opt for action in actions - for opt in action.option_strings) - dicto = self._dictionary - for subcmd in command[:-1]: - dicto = dicto.setdefault(subcmd, {}) - dicto[command[-1]] = optstr - - def get_commands(self): - return ' '.join(k for k in sorted(self._dictionary.keys())) - - def _get_data_recurse(self, dictionary, path): - ray = [] - keys = sorted(dictionary.keys()) - for cmd in keys: - name = path + "_" + cmd if path else cmd - value = dictionary[cmd] - if isinstance(value, six.string_types): - ray.append((name, value)) - else: - cmdlist = ' '.join(sorted(value.keys())) - ray.append((name, cmdlist)) - ray += self._get_data_recurse(value, name) - return ray - - def get_data(self): - return sorted(self._get_data_recurse(self._dictionary, "")) - - -class CompleteShellBase(object): - """base class for bash completion generation - """ - def __init__(self, name, output): - self.name = str(name) - self.output = output - - def write(self, cmdo, data): - self.output.write(self.get_header()) - self.output.write(" cmds='{0}'\n".format(cmdo)) - for datum in data: - self.output.write(' cmds_{0}=\'{1}\'\n'.format(*datum)) - self.output.write(self.get_trailer()) - - -class CompleteNoCode(CompleteShellBase): - """completion with no code - """ - def __init__(self, name, output): - super(CompleteNoCode, self).__init__(name, output) - - def get_header(self): - return '' - - def get_trailer(self): - return '' - - -class CompleteBash(CompleteShellBase): - """completion for bash - """ - def __init__(self, name, output): - super(CompleteBash, self).__init__(name, output) - - def get_header(self): - return ('_' + self.name + """() -{ - local cur prev words - COMPREPLY=() - _get_comp_words_by_ref -n : cur prev words - - # Command data: -""") - - def get_trailer(self): - return (""" - cmd="" - words[0]="" - completed="${cmds}" - for var in "${words[@]:1}" - do - if [[ ${var} == -* ]] ; then - break - fi - if [ -z "${cmd}" ] ; then - proposed="${var}" - else - proposed="${cmd}_${var}" - fi - local i="cmds_${proposed}" - local comp="${!i}" - if [ -z "${comp}" ] ; then - break - fi - if [[ ${comp} == -* ]] ; then - if [[ ${cur} != -* ]] ; then - completed="" - break - fi - fi - cmd="${proposed}" - completed="${comp}" - done - - if [ -z "${completed}" ] ; then - COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) ) - else - COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) ) - fi - return 0 -} -complete -F _""" + self.name + ' ' + self.name + '\n') - - -class CompleteCommand(command.Command): - """print bash completion command - """ - - log = logging.getLogger(__name__ + '.CompleteCommand') - - def __init__(self, app, app_args): - super(CompleteCommand, self).__init__(app, app_args) - self._formatters = stevedore.ExtensionManager( - namespace='cliff.formatter.completion', - ) - - def get_parser(self, prog_name): - parser = super(CompleteCommand, self).get_parser(prog_name) - parser.add_argument( - "--name", - default=None, - metavar='<command_name>', - help="Command name to support with command completion" - ) - parser.add_argument( - "--shell", - default='bash', - metavar='<shell>', - choices=sorted(self._formatters.names()), - help="Shell being used. Use none for data only (default: bash)" - ) - return parser - - def get_actions(self, command): - the_cmd = self.app.command_manager.find_command(command) - cmd_factory, cmd_name, search_args = the_cmd - cmd = cmd_factory(self.app, search_args) - if self.app.interactive_mode: - full_name = (cmd_name) - else: - full_name = (' '.join([self.app.NAME, cmd_name])) - cmd_parser = cmd.get_parser(full_name) - return cmd_parser._get_optional_actions() - - def take_action(self, parsed_args): - self.log.debug('take_action(%s)' % parsed_args) - - name = parsed_args.name or self.app.NAME - try: - shell_factory = self._formatters[parsed_args.shell].plugin - except KeyError: - raise RuntimeError('Unknown shell syntax %r' % parsed_args.shell) - shell = shell_factory(name, self.app.stdout) - - dicto = CompleteDictionary() - for cmd in self.app.command_manager: - command = cmd[0].split() - dicto.add_command(command, self.get_actions(command)) - - shell.write(dicto.get_commands(), dicto.get_data()) - - return 0 diff --git a/cliff/display.py b/cliff/display.py deleted file mode 100644 index edea1f3..0000000 --- a/cliff/display.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Application base class for displaying data. -""" -import abc -import logging - -import stevedore - -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.formatters = 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): - # Here so tests can override - return stevedore.ExtensionManager( - self.formatter_namespace, - invoke_on_load=True, - ) - - 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.names()) - 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 formatter in self.formatters: - formatter.obj.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].obj - 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 1e320f3..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 d1c392b..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 e625a25..0000000 --- a/cliff/formatters/table.py +++ /dev/null @@ -1,64 +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, - print_empty=False, - ) - 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'), - print_empty=False) - 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 9d96111..0000000 --- a/cliff/help.py +++ /dev/null @@ -1,80 +0,0 @@ -import argparse -import sys -import traceback - -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): - 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) - if namespace.debug: - traceback.print_exc(file=app.stdout) - continue - try: - cmd = factory(app, None) - except Exception as err: - app.stdout.write('Could not instantiate %r: %s\n' % (ep, err)) - if namespace.debug: - traceback.print_exc(file=app.stdout) - 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 619c4a2..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(self.parsed('help ' + arg)) - else: - cmd2.Cmd.do_help(self, arg) - cmd_names = sorted([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 1b6c3b6..0000000 --- a/cliff/lister.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Application base class for providing a list of data as output. -""" -import abc - -try: - from itertools import compress -except ImportError: - # for py26 compat - from itertools import izip - - def compress(data, selectors): - return (d for d, s in izip(data, selectors) if s) - -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(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 855b2d2..0000000 --- a/cliff/show.py +++ /dev/null @@ -1,55 +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 - - def dict2columns(self, data): - """Implement the common task of converting a dict-based object - to the two-column output that ShowOne expects. - """ - if not data: - return ({}, {}) - else: - return zip(*sorted(data.items())) diff --git a/cliff/tests/__init__.py b/cliff/tests/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/cliff/tests/__init__.py +++ /dev/null diff --git a/cliff/tests/test_app.py b/cliff/tests/test_app.py deleted file mode 100644 index e3bc12e..0000000 --- a/cliff/tests/test_app.py +++ /dev/null @@ -1,389 +0,0 @@ -# -*- encoding: utf-8 -*- -from argparse import ArgumentError -try: - from StringIO import StringIO -except ImportError: - # Probably python 3, that test won't be run so ignore the error - pass -import sys - -import nose -import mock - -from cliff.app import App -from cliff.command import Command -from cliff.commandmanager import CommandManager - - -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' - ) - assert app.interpreter is None - app.run([]) - assert app.interpreter is not None - 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) - - -def test_build_option_parser_conflicting_option_should_throw(): - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - def build_option_parser(self, description, version): - parser = super(MyApp, self).build_option_parser(description, - version) - parser.add_argument( - '-h', '--help', - default=self, # tricky - help="show this help message and exit", - ) - - # TODO: tests should really use unittest2. - try: - MyApp() - except ArgumentError: - pass - else: - raise Exception('Exception was not thrown') - - -def test_option_parser_conflicting_option_custom_arguments_should_not_throw(): - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - def build_option_parser(self, description, version): - argparse_kwargs = {'conflict_handler': 'resolve'} - parser = super(MyApp, self).build_option_parser( - description, - version, - argparse_kwargs=argparse_kwargs) - parser.add_argument( - '-h', '--help', - default=self, # tricky - help="show this help message and exit", - ) - - MyApp() - - -def test_output_encoding_default(): - # The encoding should come from getdefaultlocale() because - # stdout has no encoding set. - if sys.version_info[:2] != (2, 6): - raise nose.SkipTest('only needed for python 2.6') - data = '\xc3\xa9' - u_data = data.decode('utf-8') - - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - stdout = StringIO() - getdefaultlocale = lambda: ('ignored', 'utf-8') - - with mock.patch('sys.stdout', stdout): - with mock.patch('locale.getdefaultlocale', getdefaultlocale): - app = MyApp() - app.stdout.write(u_data) - actual = stdout.getvalue() - assert data == actual - - -def test_output_encoding_cliff_default(): - # The encoding should come from cliff.App.DEFAULT_OUTPUT_ENCODING - # because the other values are missing or None - if sys.version_info[:2] != (2, 6): - raise nose.SkipTest('only needed for python 2.6') - data = '\xc3\xa9' - u_data = data.decode('utf-8') - - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - stdout = StringIO() - getdefaultlocale = lambda: ('ignored', None) - - with mock.patch('sys.stdout', stdout): - with mock.patch('locale.getdefaultlocale', getdefaultlocale): - app = MyApp() - app.stdout.write(u_data) - actual = stdout.getvalue() - assert data == actual - - -def test_output_encoding_sys(): - # The encoding should come from sys.stdout because it is set - # there. - if sys.version_info[:2] != (2, 6): - raise nose.SkipTest('only needed for python 2.6') - data = '\xc3\xa9' - u_data = data.decode('utf-8') - - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - stdout = StringIO() - stdout.encoding = 'utf-8' - getdefaultlocale = lambda: ('ignored', 'utf-16') - - with mock.patch('sys.stdout', stdout): - with mock.patch('locale.getdefaultlocale', getdefaultlocale): - app = MyApp() - app.stdout.write(u_data) - actual = stdout.getvalue() - assert data == actual - - -def test_error_encoding_default(): - # The encoding should come from getdefaultlocale() because - # stdout has no encoding set. - if sys.version_info[:2] != (2, 6): - raise nose.SkipTest('only needed for python 2.6') - data = '\xc3\xa9' - u_data = data.decode('utf-8') - - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - stderr = StringIO() - getdefaultlocale = lambda: ('ignored', 'utf-8') - - with mock.patch('sys.stderr', stderr): - with mock.patch('locale.getdefaultlocale', getdefaultlocale): - app = MyApp() - app.stderr.write(u_data) - actual = stderr.getvalue() - assert data == actual - - -def test_error_encoding_sys(): - # The encoding should come from sys.stdout (not sys.stderr) - # because it is set there. - if sys.version_info[:2] != (2, 6): - raise nose.SkipTest('only needed for python 2.6') - data = '\xc3\xa9' - u_data = data.decode('utf-8') - - class MyApp(App): - def __init__(self): - super(MyApp, self).__init__( - description='testing', - version='0.1', - command_manager=CommandManager('tests'), - ) - - stdout = StringIO() - stdout.encoding = 'utf-8' - stderr = StringIO() - getdefaultlocale = lambda: ('ignored', 'utf-16') - - with mock.patch('sys.stdout', stdout): - with mock.patch('sys.stderr', stderr): - with mock.patch('locale.getdefaultlocale', getdefaultlocale): - app = MyApp() - app.stderr.write(u_data) - actual = stderr.getvalue() - assert data == actual - - -def test_unknown_cmd(): - app, command = make_app() - assert app.run(['hell']) == 2 - - -def test_unknown_cmd_debug(): - app, command = make_app() - try: - app.run(['--debug', 'hell']) == 2 - except ValueError as err: - assert "['hell']" in ('%s' % err) diff --git a/cliff/tests/test_command.py b/cliff/tests/test_command.py deleted file mode 100644 index 39fde51..0000000 --- a/cliff/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/cliff/tests/test_commandmanager.py b/cliff/tests/test_commandmanager.py deleted file mode 100644 index 9a50a5e..0000000 --- a/cliff/tests/test_commandmanager.py +++ /dev/null @@ -1,121 +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'] - - -def test_load_commands_keep_underscores(): - testcmd = mock.Mock() - testcmd.name = 'test_cmd' - 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', convert_underscores=False) - assert iter_entry_points.called_once_with('test') - names = [n for n, v in mgr] - assert names == ['test_cmd'] - - -def test_load_commands_replace_underscores(): - testcmd = mock.Mock() - testcmd.name = 'test_cmd' - 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', convert_underscores=True) - assert iter_entry_points.called_once_with('test') - names = [n for n, v in mgr] - assert names == ['test cmd'] diff --git a/cliff/tests/test_complete.py b/cliff/tests/test_complete.py deleted file mode 100644 index b5a6bbf..0000000 --- a/cliff/tests/test_complete.py +++ /dev/null @@ -1,130 +0,0 @@ -"""Bash completion tests -""" - -import mock - -from cliff.app import App -from cliff.commandmanager import CommandManager -from cliff import complete - - -def test_complete_dictionary(): - sot = complete.CompleteDictionary() - sot.add_command("image delete".split(), - [mock.Mock(option_strings=["1"])]) - sot.add_command("image list".split(), - [mock.Mock(option_strings=["2"])]) - sot.add_command("image create".split(), - [mock.Mock(option_strings=["3"])]) - sot.add_command("volume type create".split(), - [mock.Mock(option_strings=["4"])]) - sot.add_command("volume type delete".split(), - [mock.Mock(option_strings=["5"])]) - assert "image volume" == sot.get_commands() - result = sot.get_data() - assert "image" == result[0][0] - assert "create delete list" == result[0][1] - assert "image_create" == result[1][0] - assert "3" == result[1][1] - assert "image_delete" == result[2][0] - assert "1" == result[2][1] - assert "image_list" == result[3][0] - assert "2" == result[3][1] - - -class FakeStdout: - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result = result + line - return result - - -def given_cmdo_data(): - cmdo = "image server" - data = [("image", "create"), - ("image_create", "--eolus"), - ("server", "meta ssh"), - ("server_meta_delete", "--wilson"), - ("server_ssh", "--sunlight")] - return cmdo, data - - -def then_data(content): - assert " cmds='image server'\n" in content - assert " cmds_image='create'\n" in content - assert " cmds_image_create='--eolus'\n" in content - assert " cmds_server='meta ssh'\n" in content - assert " cmds_server_meta_delete='--wilson'\n" in content - assert " cmds_server_ssh='--sunlight'\n" in content - - -def test_complete_no_code(): - output = FakeStdout() - sot = complete.CompleteNoCode("doesNotMatter", output) - sot.write(*given_cmdo_data()) - then_data(output.content) - - -def test_complete_bash(): - output = FakeStdout() - sot = complete.CompleteBash("openstack", output) - sot.write(*given_cmdo_data()) - then_data(output.content) - assert "_openstack()\n" in output.content[0] - assert "complete -F _openstack openstack\n" in output.content[-1] - - -def test_complete_command_parser(): - sot = complete.CompleteCommand(mock.Mock(), mock.Mock()) - parser = sot.get_parser('nothing') - assert "nothing" == parser.prog - assert "print bash completion command\n " == parser.description - - -def given_complete_command(): - cmd_mgr = CommandManager('cliff.tests') - app = App('testing', '1', cmd_mgr, stdout=FakeStdout()) - sot = complete.CompleteCommand(app, mock.Mock()) - cmd_mgr.add_command('complete', complete.CompleteCommand) - return sot, app, cmd_mgr - - -def then_actions_equal(actions): - optstr = ' '.join(opt for action in actions - for opt in action.option_strings) - assert '-h --help --name --shell' == optstr - - -def test_complete_command_get_actions(): - sot, app, cmd_mgr = given_complete_command() - app.interactive_mode = False - actions = sot.get_actions(["complete"]) - then_actions_equal(actions) - - -def test_complete_command_get_actions_interactive(): - sot, app, cmd_mgr = given_complete_command() - app.interactive_mode = True - actions = sot.get_actions(["complete"]) - then_actions_equal(actions) - - -def test_complete_command_take_action(): - sot, app, cmd_mgr = given_complete_command() - parsed_args = mock.Mock() - parsed_args.name = "test_take" - parsed_args.shell = "bash" - content = app.stdout.content - assert 0 == sot.take_action(parsed_args) - assert "_test_take()\n" in content[0] - assert "complete -F _test_take test_take\n" in content[-1] - assert " cmds='complete help'\n" in content - assert " cmds_complete='-h --help --name --shell'\n" in content - assert " cmds_help='-h --help'\n" in content diff --git a/cliff/tests/test_help.py b/cliff/tests/test_help.py deleted file mode 100644 index bdf9d71..0000000 --- a/cliff/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/cliff/tests/test_lister.py b/cliff/tests/test_lister.py deleted file mode 100644 index 7d1876f..0000000 --- a/cliff/tests/test_lister.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python - -import weakref - -from cliff.lister import Lister - -import mock - - -class FauxFormatter(object): - - def __init__(self): - self.args = [] - self.obj = weakref.proxy(self) - - def emit_list(self, columns, data, stdout, args): - self.args.append((columns, data)) - - -class ExerciseLister(Lister): - - def _load_formatter_plugins(self): - return { - 'test': FauxFormatter(), - } - return - - def take_action(self, parsed_args): - return ( - parsed_args.columns, - [('a', 'A'), ('b', 'B')], - ) - - -# 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 - -def test_formatter_args(): - app = mock.Mock() - test_lister = ExerciseLister(app, []) - - parsed_args = mock.Mock() - parsed_args.columns = ('Col1', 'Col2') - parsed_args.formatter = 'test' - - test_lister.run(parsed_args) - f = test_lister.formatters['test'] - assert len(f.args) == 1 - args = f.args[0] - assert args[0] == list(parsed_args.columns) - data = list(args[1]) - assert data == [['a', 'A'], ['b', 'B']] diff --git a/cliff/tests/test_show.py b/cliff/tests/test_show.py deleted file mode 100644 index 41df5e1..0000000 --- a/cliff/tests/test_show.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python - -from cliff.show import ShowOne - -import mock - - -class FauxFormatter(object): - - def __init__(self): - self.args = [] - - def emit_list(self, columns, data, stdout, args): - self.args.append((columns, data)) - - -class ExerciseShowOne(ShowOne): - - def load_formatter_plugins(self): - self.formatters = { - 'test': FauxFormatter(), - } - return - - def take_action(self, parsed_args): - return ( - parsed_args.columns, - [('a', 'A'), ('b', 'B')], - ) - - -# def test_formatter_args(): -# app = mock.Mock() -# test_lister = ExerciseLister(app, []) - -# parsed_args = mock.Mock() -# parsed_args.columns = ('Col1', 'Col2') -# parsed_args.formatter = 'test' - -# test_lister.run(parsed_args) -# f = test_lister.formatters['test'] -# assert len(f.args) == 1 -# args = f.args[0] -# assert args[0] == list(parsed_args.columns) -# data = list(args[1]) -# assert data == [['a', 'A'], ['b', 'B']] - -def test_dict2columns(): - app = mock.Mock() - test_show = ExerciseShowOne(app, []) - d = {'a': 'A', 'b': 'B', 'c': 'C'} - expected = [('a', 'b', 'c'), ('A', 'B', 'C')] - actual = list(test_show.dict2columns(d)) - assert expected == actual diff --git a/demoapp/README.rst b/demoapp/README.rst deleted file mode 100644 index 24ca9b6..0000000 --- a/demoapp/README.rst +++ /dev/null @@ -1,64 +0,0 @@ -================= - Running demoapp -================= - -Setup ------ - -First, you need to create a virtual environment and activate it. - -:: - - $ pip install virtualenv - $ virtualenv .venv - $ . .venv/bin/activate - (.venv)$ - -Next, install ``cliff`` in the environment. - -:: - - (.venv)$ python setup.py install - -Now, install the demo application into the virtual environment. - -:: - - (.venv)$ cd demoapp - (.venv)$ python setup.py install - -Usage ------ - -With cliff and the demo setup up, you can now play with it. - -To see a list of commands available, run:: - - (.venv)$ cliffdemo --help - -One of the available commands is "simple" and running it - -:: - - (.venv)$ cliffdemo simple - -produces the following - -:: - - sending greeting - hi! - - -To see help for an individual command, include the command name on the -command line:: - - (.venv)$ cliffdemo files --help - -Cleaning Up ------------ - -Finally, when done, deactivate your virtual environment:: - - (.venv)$ deactivate - $ diff --git a/demoapp/cliffdemo/__init__.py b/demoapp/cliffdemo/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/demoapp/cliffdemo/__init__.py +++ /dev/null diff --git a/demoapp/cliffdemo/encoding.py b/demoapp/cliffdemo/encoding.py deleted file mode 100644 index 6c6c751..0000000 --- a/demoapp/cliffdemo/encoding.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- encoding: utf-8 -*- - -import logging - -from cliff.lister import Lister - - -class Encoding(Lister): - """Show some unicode text - """ - - log = logging.getLogger(__name__) - - def take_action(self, parsed_args): - messages = [ - u'pi: π', - u'GB18030:鼀丅㐀ٸཌྷᠧꌢ€', - ] - return ( - ('UTF-8', 'Unicode'), - [(repr(t.encode('utf-8')), t) - for t in messages], - ) diff --git a/demoapp/cliffdemo/list.py b/demoapp/cliffdemo/list.py deleted file mode 100644 index 3dcee4a..0000000 --- a/demoapp/cliffdemo/list.py +++ /dev/null @@ -1,18 +0,0 @@ -import logging -import os - -from cliff.lister import Lister - - -class Files(Lister): - """Show a list of files in the current directory. - - The file name and size are printed by default. - """ - - log = logging.getLogger(__name__) - - def take_action(self, parsed_args): - return (('Name', 'Size'), - ((n, os.stat(n).st_size) for n in os.listdir('.')) - ) diff --git a/demoapp/cliffdemo/main.py b/demoapp/cliffdemo/main.py deleted file mode 100644 index 85807c8..0000000 --- a/demoapp/cliffdemo/main.py +++ /dev/null @@ -1,37 +0,0 @@ -import logging -import sys - -from cliff.app import App -from cliff.commandmanager import CommandManager - - -class DemoApp(App): - - log = logging.getLogger(__name__) - - def __init__(self): - super(DemoApp, self).__init__( - description='cliff demo app', - version='0.1', - command_manager=CommandManager('cliff.demo'), - ) - - def initialize_app(self, argv): - self.log.debug('initialize_app') - - def prepare_to_run_command(self, cmd): - self.log.debug('prepare_to_run_command %s', cmd.__class__.__name__) - - def clean_up(self, cmd, result, err): - self.log.debug('clean_up %s', cmd.__class__.__name__) - if err: - self.log.debug('got an error: %s', err) - - -def main(argv=sys.argv[1:]): - myapp = DemoApp() - return myapp.run(argv) - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) diff --git a/demoapp/cliffdemo/show.py b/demoapp/cliffdemo/show.py deleted file mode 100644 index 9351148..0000000 --- a/demoapp/cliffdemo/show.py +++ /dev/null @@ -1,31 +0,0 @@ -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 take_action(self, parsed_args): - stat_data = os.stat(parsed_args.filename) - columns = ('Name', - 'Size', - 'UID', - 'GID', - 'Modified Time', - ) - data = (parsed_args.filename, - stat_data.st_size, - stat_data.st_uid, - stat_data.st_gid, - stat_data.st_mtime, - ) - return (columns, data) diff --git a/demoapp/cliffdemo/simple.py b/demoapp/cliffdemo/simple.py deleted file mode 100644 index 3800514..0000000 --- a/demoapp/cliffdemo/simple.py +++ /dev/null @@ -1,24 +0,0 @@ -import logging - -from cliff.command import Command - - -class Simple(Command): - "A simple command that prints a message." - - log = logging.getLogger(__name__) - - def take_action(self, parsed_args): - self.log.info('sending greeting') - self.log.debug('debugging') - self.app.stdout.write('hi!\n') - - -class Error(Command): - "Always raises an error" - - log = logging.getLogger(__name__) - - def take_action(self, parsed_args): - self.log.info('causing error') - raise RuntimeError('this is the expected exception') diff --git a/demoapp/setup.py b/demoapp/setup.py deleted file mode 100644 index 4bef32e..0000000 --- a/demoapp/setup.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python - -PROJECT = 'cliffdemo' - -# Change docs/sphinx/conf.py too! -VERSION = '0.1' - -from setuptools import setup, find_packages - -try: - long_description = open('README.rst', 'rt').read() -except IOError: - long_description = '' - -setup( - name=PROJECT, - version=VERSION, - - description='Demo app for cliff', - long_description=long_description, - - author='Doug Hellmann', - author_email='doug.hellmann@gmail.com', - - url='https://github.com/dreamhost/cliff', - download_url='https://github.com/dreamhost/cliff/tarball/master', - - classifiers=['Development Status :: 3 - Alpha', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - platforms=['Any'], - - scripts=[], - - provides=[], - install_requires=['cliff'], - - namespace_packages=[], - packages=find_packages(), - include_package_data=True, - - entry_points={ - 'console_scripts': [ - 'cliffdemo = cliffdemo.main:main' - ], - 'cliff.demo': [ - 'simple = cliffdemo.simple:Simple', - 'two_part = cliffdemo.simple:Simple', - 'error = cliffdemo.simple:Error', - 'list files = cliffdemo.list:Files', - 'files = cliffdemo.list:Files', - 'file = cliffdemo.show:File', - 'show file = cliffdemo.show:File', - 'unicode = cliffdemo.encoding:Encoding', - ], - }, - - zip_safe=False, -) diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 0127250..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make <target>' where <target> is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/cliff.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/cliff.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/cliff" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/cliff" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." 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/complete.rst b/docs/source/complete.rst deleted file mode 100644 index 2a08098..0000000 --- a/docs/source/complete.rst +++ /dev/null @@ -1,45 +0,0 @@ -==================== - Command Completion -==================== - -A generic command completion command is available to generate a -bash-completion script. Currently, the command will generate a script -for bash versions 3 or 4. There is also a mode that generates only -data that can be used in your own script. The command completion script -is generated based on the commands and options that you have specified -in cliff. - -Usage -===== - -In order for your command to support command completions, you need to -add the `cliff.complete.CompleteCommand` class to your command manager. - -:: - - self.command_manager.add_command('complete', cliff.complete.CompleteCommand) - -When you run the command, it will generate a bash-completion script: - -:: - - (.venv)$ mycmd complete - _mycmd() - { - local cur prev words - COMPREPLY=() - _get_comp_words_by_ref -n : cur prev words - - # Command data: - cmds='agent aggregate backup' - cmds_agent='--name' - ... - if [ -z "${completed}" ] ; then - COMPREPLY=( $( compgen -f -- "$cur" ) $( compgen -d -- "$cur" ) ) - else - COMPREPLY=( $(compgen -W "${completed}" -- ${cur}) ) - fi - return 0 - } - complete -F _mycmd mycmd - diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 131a9f9..0000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,251 +0,0 @@ -# -*- coding: utf-8 -*- -# -# cliff documentation build configuration file, created by -# sphinx-quickstart on Wed Apr 25 11:14:29 2012. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import datetime -import subprocess - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'cliff' -copyright = u'2012-%s, Doug Hellmann' % datetime.datetime.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 -# built documents. -# -# The short X.Y version. -version = subprocess.check_output([ - 'sh', '-c', - 'cd ../..; python setup.py --version', -]) -version = version.strip() -# The full version, including alpha/beta/rc tags. -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'cliffdoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - #'preamble': '', -} - -# 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', - u'Doug Hellmann', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'cliff', u'cliff Documentation', - [u'Doug Hellmann'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (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.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' 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 deleted file mode 100644 index ad02ee4..0000000 --- a/docs/source/developers.rst +++ /dev/null @@ -1,83 +0,0 @@ -================ - For Developers -================ - -If you would like to contribute to cliff directly, these instructions -should help you get started. Bug reports, and feature requests are -all welcome through the `Launchpad project`_. - -.. _Launchpad project: https://launchpad.net/python-cliff - -Changes to cliff should be submitted for review via the Gerrit tool, -following the workflow documented at -http://wiki.openstack.org/GerritWorkflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed under the `Launchpad project`_. - - -.. note:: - - Before contributing new features to clif core, please consider - whether they should be implemented as an extension instead. The - architecture is highly pluggable precisely to keep the core small. - -Building Documentation -====================== - -The documentation for cliff 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: - -- Sphinx -- docutils - -Once all of the tools are installed into a virtualenv using -pip, run ``make docs`` to generate the HTML version of the -documentation:: - - $ make docs - (cd docs && make clean html) - sphinx-build -b html -d build/doctrees source build/html - Running Sphinx v1.1.3 - 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 - looking for now-outdated files... none found - pickling environment... done - done - preparing documents... done - writing output... [100%] index - writing additional files... genindex search - copying static files... done - dumping search index... done - dumping object inventory... done - 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 -============= - -The test suite for clif uses tox_, which must be installed separately -(``pip install tox``). - -To run the tests under Python 2.7 and 3.3 as well as PyPy, run ``tox`` -from the top level directory of the git repository. - -To run tests under a single version of Python, specify the appropriate -environment when running tox:: - - $ tox -e py27 - -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 deleted file mode 100644 index e62752c..0000000 --- a/docs/source/history.rst +++ /dev/null @@ -1,163 +0,0 @@ -================= - Release History -================= - -dev - -- Fix the arguments passed to commands when they are instantiated to - pull out help. See https://github.com/dreamhost/cliff/issues/52 for - details. -- Add bash command completion. (Contributed by Terry Howe) -- Use stevedore to load formatter plugins. - -1.4.5 - -- Update the pyparsing dependency to a version that supports both - Python 2 and Python 3. -- Add PyPy testing. - -1.4.4 - -- Provide better error handling for unknown commands run from the - command line. (Contributed by Christophe Chauvet.) - -1.4.3 - -- More stdout encoding issues with Python 2.6. - -1.4.2 - -- Fix an issue with unicode output under Python 2.6. See - https://github.com/dreamhost/cliff/pull/40 for details. - -1.4.1 - -- Add ``dict2columns`` method to ``ShowOne``. (Contributed by Dean - Troyer) -- Pin the requirement for cmd2 more tightly. - -1.4 - -- Store a reference to the InteractiveApp on the App while in - interactive mode to allow commands to update the interactive - state. (Contributed by Tomaz Muraus) -- Remove reliance on distribute, now that it has merged with - setuptools 0.7. Providing an explicit dependency on setuptools - breaks updates in some cases, so just fail if it isn't there. - -1.3.3 - - - Restore compatibility with prettytable < 0.7.2 by forcing no - output if there is no data (instead of printing an empty - table). Contributed by Dirk Mueller. - - Update to allow cmd2 version 0.6.5.1. Contributed by Dirk Mueller. - -1.3.2 - - - Add ``convert_underscores`` parameter to ``CommandManager`` ``__init__`` - method to allow underscores to be used in command names. This optional - argument is defaulted to True to maintain current behavior. - (contributed by Joe Server) - - Use flake8_ for style checking. - - Relax version requirement for PrettyTable dependency to allow - point releases of 0.7. - -.. _flake8: https://pypi.python.org/pypi/flake8 - -1.3.1 - - - Sort list of commands in interactive help mode. (contributed by - Ilya Shakhat) - - Fix a dependency issue with PyParsing until the cmd2 package can - release an update setting the version of its dependency based on - the Python version. - -1.3 - - - Allow user to pass ``argparse_kwargs`` argument to the - ``build_option_parser`` method. This argument can contain extra - keyword arguments which are passed to the ``ArgumentParser`` constructor. - (contributed by Tomaz Muraus) - - Updated documentation to include dependency on distribute. - -1.2.1 - - - Fix problem with documentation packaging. - - Fix problem with missing ``izip`` import in ``lister.py``. - -1.2 - - - Fix problem with interactive mode ``help`` command. - - Disable logging by default but add a ``--log-file`` option to - re-enable it at runtime. - - Add support for python 2.6. (contributed by Mark McClain for - OpenStack Quantum) - -1.1.2 - - - Fix a packaging problem introduced in version 1.1. - -1.1 - - - Move tablib support (JSON, YAML, and HTML formatters) to a - separate project to comply with Ubuntu packaging requirements. See - https://github.com/dreamhost/cliff-tablib - -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`. - -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 deleted file mode 100644 index b474208..0000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,33 +0,0 @@ -======================================================= - cliff -- Command Line Interface Formulation Framework -======================================================= - -cliff is a framework for building command line programs. It uses -plugins to define sub-commands, output formatters, and other -extensions. - -Contents: - -.. toctree:: - :maxdepth: 2 - - introduction - demoapp - list_commands - show_commands - complete - interactive_mode - classes - install - developers - history - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -.. todolist:: diff --git a/docs/source/install.rst b/docs/source/install.rst deleted file mode 100644 index ad9a211..0000000 --- a/docs/source/install.rst +++ /dev/null @@ -1,51 +0,0 @@ -============== - Installation -============== - -Python Versions -=============== - -cliff is being developed under Python 2.7 and tested with Python 3.2. - -Dependencies -============ - -cliff depends on distribute_, the updated replacement for -setuptools. If you have an older version of setuptools installed, `you -may have trouble installing cliff`_ until you upgrade or set up a -virtualenv_ using distribute instead (by using ``--use-distribute``). - -.. _distribute: http://pypi.python.org/pypi/distribute - -.. _you may have trouble installing cliff: https://bitbucket.org/tarek/distribute/issue/91/install-glitch-when-using-pip-virtualenv - -.. _virtualenv: http://pypi.python.org/pypi/virtualenv - -.. _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_:: - - $ pip install cliff - -or:: - - $ sudo pip install cliff - -.. _pip: http://pypi.python.org/pypi/pip - -Source Code -=========== - -The source is hosted on github: https://github.com/dreamhost/cliff - -Reporting Bugs -============== - -Please report bugs through the github project: -https://github.com/dreamhost/cliff/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 deleted file mode 100644 index d9312b9..0000000 --- a/docs/source/introduction.rst +++ /dev/null @@ -1,64 +0,0 @@ -============== - 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 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 diff --git a/docs/source/list_commands.rst b/docs/source/list_commands.rst deleted file mode 100644 index 2be64e7..0000000 --- a/docs/source/list_commands.rst +++ /dev/null @@ -1,83 +0,0 @@ -=============== - 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 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 - -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 | - +---------------+------+ - -Other Formatters ----------------- - -Formatters using tablib_ to produce JSON, YAML, and HTML are available -as part of `cliff-tablib`_. - -.. _cliff-tablib: https://github.com/dreamhost/cliff-tablib - -Creating Your Own Formatter ---------------------------- - -If the standard formatters do not meet your needs, you can bundle -another formatter with your program by subclassing from -: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 deleted file mode 100644 index 87de6ff..0000000 --- a/docs/source/show_commands.rst +++ /dev/null @@ -1,89 +0,0 @@ -=============== - 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 -====================== - -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. - -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 | - +---------------+--------------+ - -Other Formatters ----------------- - -Formatters using tablib_ to produce JSON, YAML, and HTML are available -as part of `cliff-tablib`_. - -.. _cliff-tablib: https://github.com/dreamhost/cliff-tablib - -Creating Your Own Formatter ---------------------------- - -If the standard formatters do not meet your needs, you can bundle -another formatter with your program by subclassing from -: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 deleted file mode 100644 index 7a16b08..0000000 --- a/setup.py +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env python - -PROJECT = 'cliff' -VERSION = '1.4.5' - -from setuptools import setup, find_packages - -from distutils.util import convert_path -from fnmatch import fnmatchcase -import os -import sys - -try: - long_description = open('README.rst', 'rt').read() -except IOError: - long_description = '' - -install_requires = [ - 'PrettyTable>=0.6,<0.8', - 'cmd2>=0.6.7', - 'pyparsing>=2.0.1', - 'six', - 'stevedore', -] - -try: - import argparse # noqa -except ImportError: - install_requires.append('argparse') - - -############################################################################## -# find_package_data is an Ian Bicking creation. - -# Provided as an attribute, so you can append to these instead -# of replicating them: -standard_exclude = ('*.py', '*.pyc', '*~', '.*', '*.bak', '*.swp*') -standard_exclude_directories = ('.*', 'CVS', '_darcs', './build', - './dist', 'EGG-INFO', '*.egg-info') - - -def find_package_data(where='.', package='', - exclude=standard_exclude, - exclude_directories=standard_exclude_directories, - only_in_packages=True, - show_ignored=False): - """ - Return a dictionary suitable for use in ``package_data`` - in a distutils ``setup.py`` file. - - The dictionary looks like:: - - {'package': [files]} - - Where ``files`` is a list of all the files in that package that - don't match anything in ``exclude``. - - If ``only_in_packages`` is true, then top-level directories that - are not packages won't be included (but directories under packages - will). - - Directories matching any pattern in ``exclude_directories`` will - be ignored; by default directories with leading ``.``, ``CVS``, - and ``_darcs`` will be ignored. - - If ``show_ignored`` is true, then all the files that aren't - included in package data are shown on stderr (for debugging - purposes). - - Note patterns use wildcards, or can be exact paths (including - leading ``./``), and all searching is case-insensitive. - - This function is by Ian Bicking. - """ - - out = {} - stack = [(convert_path(where), '', package, only_in_packages)] - while stack: - where, prefix, package, only_in_packages = stack.pop(0) - for name in os.listdir(where): - fn = os.path.join(where, name) - if os.path.isdir(fn): - bad_name = False - for pattern in exclude_directories: - if (fnmatchcase(name, pattern) - or fn.lower() == pattern.lower()): - bad_name = True - if show_ignored: - print >> sys.stderr, ( - "Directory %s ignored by pattern %s" - % (fn, pattern)) - break - if bad_name: - continue - if os.path.isfile(os.path.join(fn, '__init__.py')): - if not package: - new_package = name - else: - new_package = package + '.' + name - stack.append((fn, '', new_package, False)) - else: - stack.append((fn, - prefix + name + '/', - package, - only_in_packages)) - elif package or not only_in_packages: - # is a file - bad_name = False - for pattern in exclude: - if (fnmatchcase(name, pattern) - or fn.lower() == pattern.lower()): - bad_name = True - if show_ignored: - print >> sys.stderr, ( - "File %s ignored by pattern %s" - % (fn, pattern)) - break - if bad_name: - continue - out.setdefault(package, []).append(prefix + name) - return out -############################################################################## - - -setup( - name=PROJECT, - version=VERSION, - - description='Command Line Interface Formulation Framework', - long_description=long_description, - - author='Doug Hellmann', - author_email='doug.hellmann@gmail.com', - - url='https://launchpad.net/python-cliff', - - classifiers=['Development Status :: 5 - Production/Stable', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Intended Audience :: Developers', - 'Environment :: Console', - ], - - platforms=['Any'], - - scripts=[], - - provides=['cliff', - ], - install_requires=install_requires, - - namespace_packages=[], - packages=find_packages(), - include_package_data=True, - # 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, - only_in_packages=False, - ), - - entry_points={ - 'cliff.formatter.list': [ - 'table = cliff.formatters.table:TableFormatter', - 'csv = cliff.formatters.commaseparated:CSVLister', - ], - 'cliff.formatter.show': [ - 'table = cliff.formatters.table:TableFormatter', - 'shell = cliff.formatters.shell:ShellFormatter', - ], - 'cliff.formatter.completion': [ - 'bash = cliff.complete:CompleteBash', - 'none = cliff.complete:CompleteNoCode', - ], - }, - - zip_safe=False, -) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 5b46989..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -nose -mock -coverage -pep8 -cmd2 -distribute -PrettyTable diff --git a/tox.ini b/tox.ini deleted file mode 100644 index bf5163c..0000000 --- a/tox.ini +++ /dev/null @@ -1,17 +0,0 @@ -[tox] -envlist = py26,py27,py32,py33,pypy,pep8 - -[testenv] -distribute = False -commands = nosetests -d --with-coverage --cover-inclusive --cover-package cliff [] -deps = - nose - mock - coverage - -[testenv:pep8] -deps = flake8 -commands = flake8 cliff docs/source/conf.py setup.py - -[testenv:py26] -basepython=python2.6 |