Blacken code base

Change-Id: I85e02280517bd045936381a984fe2c1f708456a8
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
Stephen Finucane 2024-01-25 15:53:40 +00:00
parent bc56deae91
commit 088b1e200f
51 changed files with 835 additions and 701 deletions

@ -4,7 +4,7 @@ default_language_version:
python: python3 python: python3
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v4.5.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: mixed-line-ending - id: mixed-line-ending
@ -18,16 +18,16 @@ repos:
files: .*\.(yaml|yml)$ files: .*\.(yaml|yml)$
exclude: '^zuul.d/.*$' exclude: '^zuul.d/.*$'
- repo: https://github.com/PyCQA/bandit - repo: https://github.com/PyCQA/bandit
rev: 1.7.5 rev: 1.7.7
hooks: hooks:
- id: bandit - id: bandit
args: ['-c', 'bandit.yaml'] args: ['-c', 'bandit.yaml']
# - repo: https://github.com/psf/black - repo: https://github.com/psf/black
# rev: 23.3.0 rev: 24.2.0
# hooks: hooks:
# - id: black - id: black
# args: ['-S', '-l', '79'] args: ['-S', '-l', '79']
- repo: https://github.com/pycqa/flake8 - repo: https://github.com/pycqa/flake8
rev: 6.1.0 rev: 7.0.0
hooks: hooks:
- id: flake8 - id: flake8

@ -19,7 +19,6 @@ from autopage import argparse
class _ArgumentContainerMixIn(object): class _ArgumentContainerMixIn(object):
# NOTE(dhellmann): We have to override the methods for creating # NOTE(dhellmann): We have to override the methods for creating
# groups to return our objects that know how to deal with the # groups to return our objects that know how to deal with the
# special conflict handler. # special conflict handler.
@ -44,13 +43,15 @@ class _ArgumentContainerMixIn(object):
class ArgumentParser(_ArgumentContainerMixIn, argparse.ArgumentParser): class ArgumentParser(_ArgumentContainerMixIn, argparse.ArgumentParser):
pass pass
def _handle_conflict_ignore(container, option_string_actions, def _handle_conflict_ignore(
new_action, conflicting_actions): container,
option_string_actions,
new_action,
conflicting_actions,
):
# Remember the option strings the new action starts with so we can # Remember the option strings the new action starts with so we can
# restore them as part of error reporting if we need to. # restore them as part of error reporting if we need to.
original_option_strings = new_action.option_strings original_option_strings = new_action.option_strings
@ -58,13 +59,14 @@ def _handle_conflict_ignore(container, option_string_actions,
# Remove all of the conflicting option strings from the new action # Remove all of the conflicting option strings from the new action
# and report an error if none are left at the end. # and report an error if none are left at the end.
for option_string, action in conflicting_actions: for option_string, action in conflicting_actions:
# remove the conflicting option from the new action # remove the conflicting option from the new action
new_action.option_strings.remove(option_string) new_action.option_strings.remove(option_string)
warnings.warn( warnings.warn(
('Ignoring option string {} for new action ' (
'because it conflicts with an existing option.').format( 'Ignoring option string {} for new action '
option_string)) 'because it conflicts with an existing option.'
).format(option_string)
)
# if the option now has no option string, remove it from the # if the option now has no option string, remove it from the
# container holding it # container holding it
@ -72,8 +74,10 @@ def _handle_conflict_ignore(container, option_string_actions,
new_action.option_strings = original_option_strings new_action.option_strings = original_option_strings
raise argparse.ArgumentError( raise argparse.ArgumentError(
new_action, new_action,
('Cannot resolve conflicting option string, ' (
'all names conflict.'), 'Cannot resolve conflicting option string, '
'all names conflict.'
),
) )
@ -81,8 +85,9 @@ class _ArgumentGroup(_ArgumentContainerMixIn, orig_argparse._ArgumentGroup):
pass pass
class _MutuallyExclusiveGroup(_ArgumentContainerMixIn, class _MutuallyExclusiveGroup(
orig_argparse._MutuallyExclusiveGroup): _ArgumentContainerMixIn, orig_argparse._MutuallyExclusiveGroup
):
pass pass

@ -63,17 +63,24 @@ class App(object):
LOG = logging.getLogger(NAME) LOG = logging.getLogger(NAME)
CONSOLE_MESSAGE_FORMAT = '%(message)s' CONSOLE_MESSAGE_FORMAT = '%(message)s'
LOG_FILE_MESSAGE_FORMAT = \ LOG_FILE_MESSAGE_FORMAT = (
'[%(asctime)s] %(levelname)-8s %(name)s %(message)s' '[%(asctime)s] %(levelname)-8s %(name)s %(message)s'
)
DEFAULT_VERBOSE_LEVEL = 1 DEFAULT_VERBOSE_LEVEL = 1
DEFAULT_OUTPUT_ENCODING = 'utf-8' DEFAULT_OUTPUT_ENCODING = 'utf-8'
def __init__(self, description, version, command_manager, def __init__(
stdin=None, stdout=None, stderr=None, self,
interactive_app_factory=None, description,
deferred_help=False): version,
"""Initialize the application. command_manager,
""" stdin=None,
stdout=None,
stderr=None,
interactive_app_factory=None,
deferred_help=False,
):
"""Initialize the application."""
self.command_manager = command_manager self.command_manager = command_manager
self.command_manager.add_command('help', help.HelpCommand) self.command_manager.add_command('help', help.HelpCommand)
self.command_manager.add_command('complete', complete.CompleteCommand) self.command_manager.add_command('complete', complete.CompleteCommand)
@ -120,8 +127,7 @@ class App(object):
self.stdout = stdout or sys.stdout self.stdout = stdout or sys.stdout
self.stderr = stderr or sys.stderr self.stderr = stderr or sys.stderr
def build_option_parser(self, description, version, def build_option_parser(self, description, version, argparse_kwargs=None):
argparse_kwargs=None):
"""Return an argparse option parser for this application. """Return an argparse option parser for this application.
Subclasses may override this method to extend Subclasses may override this method to extend
@ -137,9 +143,7 @@ class App(object):
""" """
argparse_kwargs = argparse_kwargs or {} argparse_kwargs = argparse_kwargs or {}
parser = _argparse.ArgumentParser( parser = _argparse.ArgumentParser(
description=description, description=description, add_help=False, **argparse_kwargs
add_help=False,
**argparse_kwargs
) )
parser.add_argument( parser.add_argument(
'--version', '--version',
@ -148,14 +152,16 @@ class App(object):
) )
verbose_group = parser.add_mutually_exclusive_group() verbose_group = parser.add_mutually_exclusive_group()
verbose_group.add_argument( verbose_group.add_argument(
'-v', '--verbose', '-v',
'--verbose',
action='count', action='count',
dest='verbose_level', dest='verbose_level',
default=self.DEFAULT_VERBOSE_LEVEL, default=self.DEFAULT_VERBOSE_LEVEL,
help='Increase verbosity of output. Can be repeated.', help='Increase verbosity of output. Can be repeated.',
) )
verbose_group.add_argument( verbose_group.add_argument(
'-q', '--quiet', '-q',
'--quiet',
action='store_const', action='store_const',
dest='verbose_level', dest='verbose_level',
const=0, const=0,
@ -169,14 +175,16 @@ class App(object):
) )
if self.deferred_help: if self.deferred_help:
parser.add_argument( parser.add_argument(
'-h', '--help', '-h',
'--help',
dest='deferred_help', dest='deferred_help',
action='store_true', action='store_true',
help="Show help message and exit.", help="Show help message and exit.",
) )
else: else:
parser.add_argument( parser.add_argument(
'-h', '--help', '-h',
'--help',
action=help.HelpAction, action=help.HelpAction,
nargs=0, nargs=0,
default=self, # tricky default=self, # tricky
@ -191,8 +199,7 @@ class App(object):
return parser return parser
def configure_logging(self): def configure_logging(self):
"""Create logging handlers for any log output. """Create logging handlers for any log output."""
"""
root_logger = logging.getLogger('') root_logger = logging.getLogger('')
root_logger.setLevel(logging.DEBUG) root_logger.setLevel(logging.DEBUG)
@ -207,10 +214,11 @@ class App(object):
# Always send higher-level messages to the console via stderr # Always send higher-level messages to the console via stderr
console = logging.StreamHandler(self.stderr) console = logging.StreamHandler(self.stderr)
console_level = {0: logging.WARNING, console_level = {
1: logging.INFO, 0: logging.WARNING,
2: logging.DEBUG, 1: logging.INFO,
}.get(self.options.verbose_level, logging.DEBUG) 2: logging.DEBUG,
}.get(self.options.verbose_level, logging.DEBUG)
console.setLevel(console_level) console.setLevel(console_level)
formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT) formatter = logging.Formatter(self.CONSOLE_MESSAGE_FORMAT)
console.setFormatter(formatter) console.setFormatter(formatter)
@ -320,16 +328,16 @@ class App(object):
if self.interactive_app_factory is None: if self.interactive_app_factory is None:
self.interactive_app_factory = InteractiveApp self.interactive_app_factory = InteractiveApp
self.interpreter = self.interactive_app_factory(self, self.interpreter = self.interactive_app_factory(
self.command_manager, self,
self.stdin, self.command_manager,
self.stdout, self.stdin,
) self.stdout,
)
return self.interpreter.cmdloop() return self.interpreter.cmdloop()
def get_fuzzy_matches(self, cmd): def get_fuzzy_matches(self, cmd):
"""return fuzzy matches of unknown command """return fuzzy matches of unknown command"""
"""
sep = '_' sep = '_'
if self.command_manager.convert_underscores: if self.command_manager.convert_underscores:
@ -343,8 +351,12 @@ class App(object):
dist.append((0, candidate)) dist.append((0, candidate))
continue continue
# Levenshtein distance # Levenshtein distance
dist.append((utils.damerau_levenshtein(cmd, prefix, utils.COST)+1, dist.append(
candidate)) (
utils.damerau_levenshtein(cmd, prefix, utils.COST) + 1,
candidate,
)
)
matches = [] matches = []
match_distance = 0 match_distance = 0
@ -371,10 +383,17 @@ class App(object):
article = 'a' article = 'a'
if self.NAME[0] in 'aeiou': if self.NAME[0] in 'aeiou':
article = 'an' article = 'an'
self.stdout.write('%s: \'%s\' is not %s %s command. ' self.stdout.write(
'See \'%s --help\'.\n' '%s: \'%s\' is not %s %s command. '
% (self.NAME, ' '.join(argv), article, 'See \'%s --help\'.\n'
self.NAME, self.NAME)) % (
self.NAME,
' '.join(argv),
article,
self.NAME,
self.NAME,
)
)
self.stdout.write('Did you mean one of these?\n') self.stdout.write('Did you mean one of these?\n')
for match in fuzzy_matches: for match in fuzzy_matches:
self.stdout.write(' %s\n' % match) self.stdout.write(' %s\n' % match)
@ -393,10 +412,11 @@ class App(object):
err = None err = None
try: try:
self.prepare_to_run_command(cmd) self.prepare_to_run_command(cmd)
full_name = (cmd_name full_name = (
if self.interactive_mode cmd_name
else ' '.join([self.NAME, cmd_name]) if self.interactive_mode
) else ' '.join([self.NAME, cmd_name])
)
cmd_parser = cmd.get_parser(full_name) cmd_parser = cmd.get_parser(full_name)
try: try:
parsed_args = cmd_parser.parse_args(sub_argv) parsed_args = cmd_parser.parse_args(sub_argv)
@ -404,6 +424,7 @@ class App(object):
if self.interactive_mode: if self.interactive_mode:
# Defer importing cmd2 as it is a slow import # Defer importing cmd2 as it is a slow import
import cmd2 import cmd2
raise cmd2.exceptions.Cmd2ArgparseError from ex raise cmd2.exceptions.Cmd2ArgparseError from ex
else: else:
raise ex raise ex

@ -17,7 +17,6 @@ import abc
class FormattableColumn(object, metaclass=abc.ABCMeta): class FormattableColumn(object, metaclass=abc.ABCMeta):
def __init__(self, value): def __init__(self, value):
self._value = value self._value = value
@ -27,9 +26,7 @@ class FormattableColumn(object, metaclass=abc.ABCMeta):
) )
def __lt__(self, other): def __lt__(self, other):
return ( return self.__class__ == other.__class__ and self._value < other._value
self.__class__ == other.__class__ and self._value < other._value
)
def __str__(self): def __str__(self):
return self.human_readable() return self.human_readable()

@ -39,9 +39,7 @@ def _get_distributions_by_modules():
if _dists_by_mods is None: if _dists_by_mods is None:
# There can be multiple distribution in the case of namespace packages # There can be multiple distribution in the case of namespace packages
# so we'll just grab the first one # so we'll just grab the first one
_dists_by_mods = { _dists_by_mods = {k: v[0] for k, v in packages_distributions().items()}
k: v[0] for k, v in packages_distributions().items()
}
return _dists_by_mods return _dists_by_mods
@ -85,7 +83,7 @@ class Command(object, metaclass=abc.ABCMeta):
if self.app and self.cmd_name: if self.app and self.cmd_name:
namespace = '{}.{}'.format( namespace = '{}.{}'.format(
self.app.command_manager.namespace, self.app.command_manager.namespace,
self.cmd_name.replace(' ', '_') self.cmd_name.replace(' ', '_'),
) )
self._hooks = extension.ExtensionManager( self._hooks = extension.ExtensionManager(
namespace=namespace, namespace=namespace,
@ -132,21 +130,19 @@ class Command(object, metaclass=abc.ABCMeta):
) )
parts.extend(hook_epilogs) parts.extend(hook_epilogs)
app_dist_name = getattr( app_dist_name = getattr(
self, 'app_dist_name', _get_distribution_for_module( self,
inspect.getmodule(self.app) 'app_dist_name',
) _get_distribution_for_module(inspect.getmodule(self.app)),
) )
dist_name = _get_distribution_for_module(inspect.getmodule(self)) dist_name = _get_distribution_for_module(inspect.getmodule(self))
if dist_name and dist_name != app_dist_name: if dist_name and dist_name != app_dist_name:
parts.append( parts.append(
'This command is provided by the %s plugin.' % 'This command is provided by the %s plugin.' % (dist_name,)
(dist_name,)
) )
return '\n\n'.join(parts) return '\n\n'.join(parts)
def get_parser(self, prog_name): def get_parser(self, prog_name):
"""Return an :class:`argparse.ArgumentParser`. """Return an :class:`argparse.ArgumentParser`."""
"""
parser = _argparse.ArgumentParser( parser = _argparse.ArgumentParser(
description=self.get_description(), description=self.get_description(),
epilog=self.get_epilog(), epilog=self.get_epilog(),

@ -35,8 +35,7 @@ def _get_commands_by_partial_name(args, commands):
class EntryPointWrapper(object): class EntryPointWrapper(object):
"""Wrap up a command class already imported to make it look like a plugin. """Wrap up a command class already imported to make it look like a plugin."""
"""
def __init__(self, name, command_class): def __init__(self, name, command_class):
self.name = name self.name = name
@ -54,6 +53,7 @@ class CommandManager(object):
:param convert_underscores: Whether cliff should convert underscores to :param convert_underscores: Whether cliff should convert underscores to
spaces in entry_point commands. spaces in entry_point commands.
""" """
def __init__(self, namespace, convert_underscores=True): def __init__(self, namespace, convert_underscores=True):
self.commands = {} self.commands = {}
self._legacy = {} self._legacy = {}
@ -72,9 +72,11 @@ class CommandManager(object):
self.group_list.append(namespace) self.group_list.append(namespace)
for ep in stevedore.ExtensionManager(namespace): for ep in stevedore.ExtensionManager(namespace):
LOG.debug('found command %r', ep.name) LOG.debug('found command %r', ep.name)
cmd_name = (ep.name.replace('_', ' ') cmd_name = (
if self.convert_underscores ep.name.replace('_', ' ')
else ep.name) if self.convert_underscores
else ep.name
)
self.commands[cmd_name] = ep.entry_point self.commands[cmd_name] = ep.entry_point
return return
@ -114,7 +116,8 @@ class CommandManager(object):
found = name found = name
else: else:
candidates = _get_commands_by_partial_name( candidates = _get_commands_by_partial_name(
argv[:i], self.commands) argv[:i], self.commands
)
if len(candidates) == 1: if len(candidates) == 1:
found = candidates[0] found = candidates[0]
if found: if found:
@ -131,8 +134,7 @@ class CommandManager(object):
cmd_factory = cmd_ep.load() cmd_factory = cmd_ep.load()
return (cmd_factory, return_name, search_args) return (cmd_factory, return_name, search_args)
else: else:
raise ValueError('Unknown command %r' % raise ValueError('Unknown command %r' % (argv,))
(argv,))
def _get_last_possible_command_index(self, argv): def _get_last_possible_command_index(self, argv):
"""Returns the index after the last argument """Returns the index after the last argument

@ -21,15 +21,15 @@ from cliff import command
class CompleteDictionary: class CompleteDictionary:
"""dictionary for bash completion """dictionary for bash completion"""
"""
def __init__(self): def __init__(self):
self._dictionary = {} self._dictionary = {}
def add_command(self, command, actions): def add_command(self, command, actions):
optstr = ' '.join(opt for action in actions optstr = ' '.join(
for opt in action.option_strings) opt for action in actions for opt in action.option_strings
)
dicto = self._dictionary dicto = self._dictionary
last_cmd = command[-1] last_cmd = command[-1]
for subcmd in command[:-1]: for subcmd in command[:-1]:
@ -69,8 +69,8 @@ class CompleteDictionary:
class CompleteShellBase(object): class CompleteShellBase(object):
"""base class for bash completion generation """base class for bash completion generation"""
"""
def __init__(self, name, output): def __init__(self, name, output):
self.name = str(name) self.name = str(name)
self.output = output self.output = output
@ -89,8 +89,8 @@ class CompleteShellBase(object):
class CompleteNoCode(CompleteShellBase): class CompleteNoCode(CompleteShellBase):
"""completion with no code """completion with no code"""
"""
def __init__(self, name, output): def __init__(self, name, output):
super(CompleteNoCode, self).__init__(name, output) super(CompleteNoCode, self).__init__(name, output)
@ -102,23 +102,28 @@ class CompleteNoCode(CompleteShellBase):
class CompleteBash(CompleteShellBase): class CompleteBash(CompleteShellBase):
"""completion for bash """completion for bash"""
"""
def __init__(self, name, output): def __init__(self, name, output):
super(CompleteBash, self).__init__(name, output) super(CompleteBash, self).__init__(name, output)
def get_header(self): def get_header(self):
return ('_' + self.escaped_name + """() return (
'_'
+ self.escaped_name
+ """()
{ {
local cur prev words local cur prev words
COMPREPLY=() COMPREPLY=()
_get_comp_words_by_ref -n : cur prev words _get_comp_words_by_ref -n : cur prev words
# Command data: # Command data:
""") """
)
def get_trailer(self): def get_trailer(self):
return (""" return (
"""
dash=- dash=-
underscore=_ underscore=_
cmd="" cmd=""
@ -157,12 +162,16 @@ class CompleteBash(CompleteShellBase):
fi fi
return 0 return 0
} }
complete -F _""" + self.escaped_name + ' ' + self.name + '\n') complete -F _"""
+ self.escaped_name
+ ' '
+ self.name
+ '\n'
)
class CompleteCommand(command.Command): class CompleteCommand(command.Command):
"""print bash completion command """print bash completion command"""
"""
log = logging.getLogger(__name__ + '.CompleteCommand') log = logging.getLogger(__name__ + '.CompleteCommand')
@ -178,14 +187,14 @@ class CompleteCommand(command.Command):
"--name", "--name",
default=None, default=None,
metavar='<command_name>', metavar='<command_name>',
help="Command name to support with command completion" help="Command name to support with command completion",
) )
parser.add_argument( parser.add_argument(
"--shell", "--shell",
default='bash', default='bash',
metavar='<shell>', metavar='<shell>',
choices=sorted(self._formatters.names()), choices=sorted(self._formatters.names()),
help="Shell being used. Use none for data only (default: bash)" help="Shell being used. Use none for data only (default: bash)",
) )
return parser return parser
@ -194,9 +203,9 @@ class CompleteCommand(command.Command):
cmd_factory, cmd_name, search_args = the_cmd cmd_factory, cmd_name, search_args = the_cmd
cmd = cmd_factory(self.app, search_args) cmd = cmd_factory(self.app, search_args)
if self.app.interactive_mode: if self.app.interactive_mode:
full_name = (cmd_name) full_name = cmd_name
else: else:
full_name = (' '.join([self.app.NAME, cmd_name])) full_name = ' '.join([self.app.NAME, cmd_name])
cmd_parser = cmd.get_parser(full_name) cmd_parser = cmd.get_parser(full_name)
return cmd_parser._get_optional_actions() return cmd_parser._get_optional_actions()

@ -21,12 +21,12 @@ from . import command
class DisplayCommandBase(command.Command, metaclass=abc.ABCMeta): class DisplayCommandBase(command.Command, metaclass=abc.ABCMeta):
"""Command base class for displaying data about a single object. """Command base class for displaying data about a single object."""
"""
def __init__(self, app, app_args, cmd_name=None): def __init__(self, app, app_args, cmd_name=None):
super(DisplayCommandBase, self).__init__(app, app_args, super(DisplayCommandBase, self).__init__(
cmd_name=cmd_name) app, app_args, cmd_name=cmd_name
)
self._formatter_plugins = self._load_formatter_plugins() self._formatter_plugins = self._load_formatter_plugins()
@property @property
@ -58,7 +58,8 @@ class DisplayCommandBase(command.Command, metaclass=abc.ABCMeta):
if formatter_default not in formatter_choices: if formatter_default not in formatter_choices:
formatter_default = formatter_choices[0] formatter_default = formatter_choices[0]
formatter_group.add_argument( formatter_group.add_argument(
'-f', '--format', '-f',
'--format',
dest='formatter', dest='formatter',
action='store', action='store',
choices=formatter_choices, choices=formatter_choices,
@ -66,13 +67,14 @@ class DisplayCommandBase(command.Command, metaclass=abc.ABCMeta):
help='the output format, defaults to %s' % formatter_default, help='the output format, defaults to %s' % formatter_default,
) )
formatter_group.add_argument( formatter_group.add_argument(
'-c', '--column', '-c',
'--column',
action='append', action='append',
default=[], default=[],
dest='columns', dest='columns',
metavar='COLUMN', metavar='COLUMN',
help='specify the column(s) to include, can be ' help='specify the column(s) to include, can be '
'repeated to show multiple columns', 'repeated to show multiple columns',
) )
for formatter in self._formatter_plugins: for formatter in self._formatter_plugins:
formatter.obj.add_argument_group(parser) formatter.obj.add_argument_group(parser)
@ -99,24 +101,27 @@ class DisplayCommandBase(command.Command, metaclass=abc.ABCMeta):
columns_to_include = column_names columns_to_include = column_names
selector = None selector = None
else: else:
columns_to_include = [c for c in column_names columns_to_include = [
if c in parsed_args.columns] c for c in column_names if c in parsed_args.columns
]
if not columns_to_include: if not columns_to_include:
raise ValueError('No recognized column names in %s. ' raise ValueError(
'Recognized columns are %s.' % 'No recognized column names in %s. '
(str(parsed_args.columns), str(column_names))) 'Recognized columns are %s.'
% (str(parsed_args.columns), str(column_names))
)
# Set up argument to compress() # Set up argument to compress()
selector = [(c in columns_to_include) selector = [(c in columns_to_include) for c in column_names]
for c in column_names]
return columns_to_include, selector return columns_to_include, selector
def run(self, parsed_args): def run(self, parsed_args):
parsed_args = self._run_before_hooks(parsed_args) parsed_args = self._run_before_hooks(parsed_args)
self.formatter = self._formatter_plugins[parsed_args.formatter].obj self.formatter = self._formatter_plugins[parsed_args.formatter].obj
column_names, data = self.take_action(parsed_args) column_names, data = self.take_action(parsed_args)
column_names, data = self._run_after_hooks(parsed_args, column_names, data = self._run_after_hooks(
(column_names, data)) parsed_args, (column_names, data)
)
self.produce_output(parsed_args, column_names, data) self.produce_output(parsed_args, column_names, data)
return 0 return 0

@ -17,7 +17,6 @@ import abc
class Formatter(object, metaclass=abc.ABCMeta): class Formatter(object, metaclass=abc.ABCMeta):
@abc.abstractmethod @abc.abstractmethod
def add_argument_group(self, parser): def add_argument_group(self, parser):
"""Add any options to the argument parser. """Add any options to the argument parser.
@ -27,8 +26,7 @@ class Formatter(object, metaclass=abc.ABCMeta):
class ListFormatter(Formatter, metaclass=abc.ABCMeta): class ListFormatter(Formatter, metaclass=abc.ABCMeta):
"""Base class for formatters that know how to deal with multiple objects. """Base class for formatters that know how to deal with multiple objects."""
"""
@abc.abstractmethod @abc.abstractmethod
def emit_list(self, column_names, data, stdout, parsed_args): def emit_list(self, column_names, data, stdout, parsed_args):
@ -50,8 +48,7 @@ class ListFormatter(Formatter, metaclass=abc.ABCMeta):
class SingleFormatter(Formatter, metaclass=abc.ABCMeta): class SingleFormatter(Formatter, metaclass=abc.ABCMeta):
"""Base class for formatters that work with single objects. """Base class for formatters that work with single objects."""
"""
@abc.abstractmethod @abc.abstractmethod
def emit_one(self, column_names, data, stdout, parsed_args): def emit_one(self, column_names, data, stdout, parsed_args):

@ -21,7 +21,6 @@ from cliff import columns
class CSVLister(ListFormatter): class CSVLister(ListFormatter):
QUOTE_MODES = { QUOTE_MODES = {
'all': csv.QUOTE_ALL, 'all': csv.QUOTE_ALL,
'minimal': csv.QUOTE_MINIMAL, 'minimal': csv.QUOTE_MINIMAL,
@ -50,9 +49,13 @@ class CSVLister(ListFormatter):
writer.writerow(column_names) writer.writerow(column_names)
for row in data: for row in data:
writer.writerow( writer.writerow(
[(str(c.machine_readable()) [
if isinstance(c, columns.FormattableColumn) (
else c) str(c.machine_readable())
for c in row] if isinstance(c, columns.FormattableColumn)
else c
)
for c in row
]
) )
return return

@ -20,24 +20,27 @@ from cliff import columns
class JSONFormatter(base.ListFormatter, base.SingleFormatter): class JSONFormatter(base.ListFormatter, base.SingleFormatter):
def add_argument_group(self, parser): def add_argument_group(self, parser):
group = parser.add_argument_group(title='json formatter') group = parser.add_argument_group(title='json formatter')
group.add_argument( group.add_argument(
'--noindent', '--noindent',
action='store_true', action='store_true',
dest='noindent', dest='noindent',
help='whether to disable indenting the JSON' help='whether to disable indenting the JSON',
) )
def emit_list(self, column_names, data, stdout, parsed_args): def emit_list(self, column_names, data, stdout, parsed_args):
items = [] items = []
for item in data: for item in data:
items.append( items.append(
{n: (i.machine_readable() {
if isinstance(i, columns.FormattableColumn) n: (
else i) i.machine_readable()
for n, i in zip(column_names, item)} if isinstance(i, columns.FormattableColumn)
else i
)
for n, i in zip(column_names, item)
}
) )
indent = None if parsed_args.noindent else 2 indent = None if parsed_args.noindent else 2
json.dump(items, stdout, indent=indent) json.dump(items, stdout, indent=indent)
@ -45,9 +48,11 @@ class JSONFormatter(base.ListFormatter, base.SingleFormatter):
def emit_one(self, column_names, data, stdout, parsed_args): def emit_one(self, column_names, data, stdout, parsed_args):
one = { one = {
n: (i.machine_readable() n: (
i.machine_readable()
if isinstance(i, columns.FormattableColumn) if isinstance(i, columns.FormattableColumn)
else i) else i
)
for n, i in zip(column_names, data) for n, i in zip(column_names, data)
} }
indent = None if parsed_args.noindent else 2 indent = None if parsed_args.noindent else 2

@ -20,7 +20,6 @@ import argparse
class ShellFormatter(base.SingleFormatter): class ShellFormatter(base.SingleFormatter):
def add_argument_group(self, parser): def add_argument_group(self, parser):
group = parser.add_argument_group( group = parser.add_argument_group(
title='shell formatter', title='shell formatter',
@ -43,15 +42,15 @@ class ShellFormatter(base.SingleFormatter):
) )
def emit_one(self, column_names, data, stdout, parsed_args): def emit_one(self, column_names, data, stdout, parsed_args):
variable_names = [c.lower().replace(' ', '_') variable_names = [c.lower().replace(' ', '_') for c in column_names]
for c in column_names
]
desired_columns = parsed_args.variables desired_columns = parsed_args.variables
for name, value in zip(variable_names, data): for name, value in zip(variable_names, data):
if name in desired_columns or not desired_columns: if name in desired_columns or not desired_columns:
value = (str(value.machine_readable()) value = (
if isinstance(value, columns.FormattableColumn) str(value.machine_readable())
else value) if isinstance(value, columns.FormattableColumn)
else value
)
if isinstance(value, str): if isinstance(value, str):
value = value.replace('"', '\\"') value = value.replace('"', '\\"')
if isinstance(name, str): if isinstance(name, str):

@ -42,7 +42,6 @@ def _do_fit(fit_width):
class TableFormatter(base.ListFormatter, base.SingleFormatter): class TableFormatter(base.ListFormatter, base.SingleFormatter):
ALIGNMENTS = { ALIGNMENTS = {
int: 'r', int: 'r',
str: 'l', str: 'l',
@ -56,18 +55,22 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
metavar='<integer>', metavar='<integer>',
default=int(os.environ.get('CLIFF_MAX_TERM_WIDTH', 0)), default=int(os.environ.get('CLIFF_MAX_TERM_WIDTH', 0)),
type=int, type=int,
help=('Maximum display width, <1 to disable. You can also ' help=(
'use the CLIFF_MAX_TERM_WIDTH environment variable, ' 'Maximum display width, <1 to disable. You can also '
'but the parameter takes precedence.'), 'use the CLIFF_MAX_TERM_WIDTH environment variable, '
'but the parameter takes precedence.'
),
) )
group.add_argument( group.add_argument(
'--fit-width', '--fit-width',
action='store_true', action='store_true',
default=bool(int(os.environ.get('CLIFF_FIT_WIDTH', 0))), default=bool(int(os.environ.get('CLIFF_FIT_WIDTH', 0))),
help=('Fit the table to the display width. ' help=(
'Implied if --max-width greater than 0. ' 'Fit the table to the display width. '
'Set the environment variable CLIFF_FIT_WIDTH=1 ' 'Implied if --max-width greater than 0. '
'to always enable'), 'Set the environment variable CLIFF_FIT_WIDTH=1 '
'to always enable'
),
) )
group.add_argument( group.add_argument(
'--print-empty', '--print-empty',
@ -109,8 +112,8 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
# preference to wrapping columns smaller than 8 characters. # preference to wrapping columns smaller than 8 characters.
min_width = 8 min_width = 8
self._assign_max_widths( self._assign_max_widths(
x, int(parsed_args.max_width), min_width, x, int(parsed_args.max_width), min_width, parsed_args.fit_width
parsed_args.fit_width) )
formatted = x.get_string() formatted = x.get_string()
stdout.write(formatted) stdout.write(formatted)
@ -118,8 +121,9 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
return return
def emit_one(self, column_names, data, stdout, parsed_args): def emit_one(self, column_names, data, stdout, parsed_args):
x = prettytable.PrettyTable(field_names=('Field', 'Value'), x = prettytable.PrettyTable(
print_empty=False) field_names=('Field', 'Value'), print_empty=False
)
x.padding_width = 1 x.padding_width = 1
# Align all columns left because the values are # Align all columns left because the values are
# not all the same type. # not all the same type.
@ -134,8 +138,8 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
# the Field column readable. # the Field column readable.
min_width = 16 min_width = 16
self._assign_max_widths( self._assign_max_widths(
x, int(parsed_args.max_width), min_width, x, int(parsed_args.max_width), min_width, parsed_args.fit_width
parsed_args.fit_width) )
formatted = x.get_string() formatted = x.get_string()
stdout.write(formatted) stdout.write(formatted)
@ -144,7 +148,6 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
@staticmethod @staticmethod
def _field_widths(field_names, first_line): def _field_widths(field_names, first_line):
# use the first line +----+-------+ to infer column widths # use the first line +----+-------+ to infer column widths
# accounting for padding and dividers # accounting for padding and dividers
widths = [max(0, len(i) - 2) for i in first_line.split('+')[1:-1]] widths = [max(0, len(i) - 2) for i in first_line.split('+')[1:-1]]
@ -164,8 +167,9 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
return usable_total_width, optimal_width return usable_total_width, optimal_width
@staticmethod @staticmethod
def _build_shrink_fields(usable_total_width, optimal_width, def _build_shrink_fields(
field_widths, field_names): usable_total_width, optimal_width, field_widths, field_names
):
shrink_fields = [] shrink_fields = []
shrink_remaining = usable_total_width shrink_remaining = usable_total_width
for field in field_names: for field in field_names:
@ -204,12 +208,14 @@ class TableFormatter(base.ListFormatter, base.SingleFormatter):
return return
usable_total_width, optimal_width = TableFormatter._width_info( usable_total_width, optimal_width = TableFormatter._width_info(
term_width, field_count) term_width, field_count
)
field_widths = TableFormatter._field_widths(x.field_names, first_line) field_widths = TableFormatter._field_widths(x.field_names, first_line)
shrink_fields, shrink_remaining = TableFormatter._build_shrink_fields( shrink_fields, shrink_remaining = TableFormatter._build_shrink_fields(
usable_total_width, optimal_width, field_widths, x.field_names) usable_total_width, optimal_width, field_widths, x.field_names
)
shrink_to = shrink_remaining // len(shrink_fields) shrink_to = shrink_remaining // len(shrink_fields)
# make all shrinkable fields size shrink_to apart from the last one # make all shrinkable fields size shrink_to apart from the last one

@ -18,7 +18,6 @@ from cliff import columns
class ValueFormatter(base.ListFormatter, base.SingleFormatter): class ValueFormatter(base.ListFormatter, base.SingleFormatter):
def add_argument_group(self, parser): def add_argument_group(self, parser):
pass pass
@ -26,17 +25,25 @@ class ValueFormatter(base.ListFormatter, base.SingleFormatter):
for row in data: for row in data:
stdout.write( stdout.write(
' '.join( ' '.join(
str(c.machine_readable() str(
c.machine_readable()
if isinstance(c, columns.FormattableColumn) if isinstance(c, columns.FormattableColumn)
else c) else c
for c in row) + '\n') )
for c in row
)
+ '\n'
)
return return
def emit_one(self, column_names, data, stdout, parsed_args): def emit_one(self, column_names, data, stdout, parsed_args):
for value in data: for value in data:
stdout.write('%s\n' % str( stdout.write(
value.machine_readable() '%s\n'
if isinstance(value, columns.FormattableColumn) % str(
else value) value.machine_readable()
if isinstance(value, columns.FormattableColumn)
else value
)
) )
return return

@ -29,7 +29,6 @@ def _yaml_friendly(value):
class YAMLFormatter(base.ListFormatter, base.SingleFormatter): class YAMLFormatter(base.ListFormatter, base.SingleFormatter):
def add_argument_group(self, parser): def add_argument_group(self, parser):
pass pass

@ -30,13 +30,13 @@ class HelpExit(SystemExit):
class HelpAction(argparse.Action): class HelpAction(argparse.Action):
"""Provide a custom action so the -h and --help options """Provide a custom action so the -h and --help options
to the main app will print a list of the commands. to the main app will print a list of the commands.
The commands are determined by checking the CommandManager The commands are determined by checking the CommandManager
instance, passed in as the "default" value for the action. instance, passed in as the "default" value for the action.
""" """
def __call__(self, parser, namespace, values, option_string=None): def __call__(self, parser, namespace, values, option_string=None):
app = self.default app = self.default
pager = autopage.argparse.help_pager(app.stdout) pager = autopage.argparse.help_pager(app.stdout)
@ -90,15 +90,15 @@ class HelpAction(argparse.Action):
class HelpCommand(command.Command): class HelpCommand(command.Command):
"""print detailed help for another command """print detailed help for another command"""
"""
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(HelpCommand, self).get_parser(prog_name) parser = super(HelpCommand, self).get_parser(prog_name)
parser.add_argument('cmd', parser.add_argument(
nargs='*', 'cmd',
help='name of the command', nargs='*',
) help='name of the command',
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -111,9 +111,11 @@ class HelpCommand(command.Command):
except ValueError: except ValueError:
# Did not find an exact match # Did not find an exact match
cmd = parsed_args.cmd[0] cmd = parsed_args.cmd[0]
fuzzy_matches = [k[0] for k in self.app.command_manager fuzzy_matches = [
if k[0].startswith(cmd) k[0]
] for k in self.app.command_manager
if k[0].startswith(cmd)
]
if not fuzzy_matches: if not fuzzy_matches:
raise raise
self.app.stdout.write('Command "%s" matches:\n' % cmd) self.app.stdout.write('Command "%s" matches:\n' % cmd)
@ -125,15 +127,17 @@ class HelpCommand(command.Command):
if 'cmd_name' in inspect.getfullargspec(cmd_factory.__init__).args: if 'cmd_name' in inspect.getfullargspec(cmd_factory.__init__).args:
kwargs['cmd_name'] = cmd_name kwargs['cmd_name'] = cmd_name
cmd = cmd_factory(self.app, self.app_args, **kwargs) cmd = cmd_factory(self.app, self.app_args, **kwargs)
full_name = (cmd_name full_name = (
if self.app.interactive_mode cmd_name
else ' '.join([self.app.NAME, cmd_name]) if self.app.interactive_mode
) else ' '.join([self.app.NAME, cmd_name])
)
cmd_parser = cmd.get_parser(full_name) cmd_parser = cmd.get_parser(full_name)
pager = autopage.argparse.help_pager(self.app.stdout) pager = autopage.argparse.help_pager(self.app.stdout)
with pager as out: with pager as out:
autopage.argparse.use_color_for_parser(cmd_parser, autopage.argparse.use_color_for_parser(
pager.to_terminal()) cmd_parser, pager.to_terminal()
)
cmd_parser.print_help(out) cmd_parser.print_help(out)
else: else:
action = HelpAction(None, None, default=self.app) action = HelpAction(None, None, default=self.app)

@ -42,8 +42,9 @@ class InteractiveApp(cmd2.Cmd):
doc_header = "Shell commands (type help <topic>):" doc_header = "Shell commands (type help <topic>):"
app_cmd_header = "Application commands (type help <topic>):" app_cmd_header = "Application commands (type help <topic>):"
def __init__(self, parent_app, command_manager, stdin, stdout, def __init__(
errexit=False): self, parent_app, command_manager, stdin, stdout, errexit=False
):
self.parent_app = parent_app self.parent_app = parent_app
if not hasattr(sys.stdin, 'isatty') or sys.stdin.isatty(): if not hasattr(sys.stdin, 'isatty') or sys.stdin.isatty():
self.prompt = '(%s) ' % parent_app.NAME self.prompt = '(%s) ' % parent_app.NAME
@ -117,8 +118,9 @@ class InteractiveApp(cmd2.Cmd):
method_name = '_'.join( method_name = '_'.join(
itertools.chain( itertools.chain(
['do'], ['do'],
itertools.takewhile(lambda x: not x.startswith('-'), itertools.takewhile(
arg_parts) lambda x: not x.startswith('-'), arg_parts
),
) )
) )
# Have the command manager version of the help # Have the command manager version of the help
@ -160,10 +162,9 @@ class InteractiveApp(cmd2.Cmd):
# Override the base class version to filter out # Override the base class version to filter out
# things that look like they should be hidden # things that look like they should be hidden
# from the user. # from the user.
return [n return [
for n in cmd2.Cmd.get_names(self) n for n in cmd2.Cmd.get_names(self) if not n.startswith('do__')
if not n.startswith('do__') ]
]
def precmd(self, statement): def precmd(self, statement):
"""Hook method executed just before the command is executed by """Hook method executed just before the command is executed by

@ -83,7 +83,8 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta):
def produce_output(self, parsed_args, column_names, data): def produce_output(self, parsed_args, column_names, data):
if parsed_args.sort_columns and self.need_sort_by_cliff: if parsed_args.sort_columns and self.need_sort_by_cliff:
indexes = [ indexes = [
column_names.index(c) for c in parsed_args.sort_columns column_names.index(c)
for c in parsed_args.sort_columns
if c in column_names if c in column_names
] ]
reverse = parsed_args.sort_direction == 'desc' reverse = parsed_args.sort_direction == 'desc'
@ -97,7 +98,8 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta):
# returns from the 'is None' check on the two values are # returns from the 'is None' check on the two values are
# the same, i.e. both None or both not-None # the same, i.e. both None or both not-None
data = sorted( data = sorted(
data, key=lambda k: (k[index] is None, k[index]), data,
key=lambda k: (k[index] is None, k[index]),
reverse=reverse, reverse=reverse,
) )
except TypeError: except TypeError:
@ -108,7 +110,8 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta):
) )
columns_to_include, selector = self._generate_columns_and_selector( columns_to_include, selector = self._generate_columns_and_selector(
parsed_args, column_names, parsed_args,
column_names,
) )
if selector: if selector:
# Generator expression to only return the parts of a row # Generator expression to only return the parts of a row
@ -120,7 +123,10 @@ class Lister(display.DisplayCommandBase, metaclass=abc.ABCMeta):
) )
self.formatter.emit_list( self.formatter.emit_list(
columns_to_include, data, self.app.stdout, parsed_args, columns_to_include,
data,
self.app.stdout,
parsed_args,
) )
return 0 return 0

@ -18,8 +18,7 @@ from . import display
class ShowOne(display.DisplayCommandBase, metaclass=abc.ABCMeta): class ShowOne(display.DisplayCommandBase, metaclass=abc.ABCMeta):
"""Command base class for displaying data about a single object. """Command base class for displaying data about a single object."""
"""
@property @property
def formatter_namespace(self): def formatter_namespace(self):
@ -37,13 +36,13 @@ class ShowOne(display.DisplayCommandBase, metaclass=abc.ABCMeta):
def produce_output(self, parsed_args, column_names, data): def produce_output(self, parsed_args, column_names, data):
(columns_to_include, selector) = self._generate_columns_and_selector( (columns_to_include, selector) = self._generate_columns_and_selector(
parsed_args, column_names) parsed_args, column_names
)
if selector: if selector:
data = list(self._compress_iterable(data, selector)) data = list(self._compress_iterable(data, selector))
self.formatter.emit_one(columns_to_include, self.formatter.emit_one(
data, columns_to_include, data, self.app.stdout, parsed_args
self.app.stdout, )
parsed_args)
return 0 return 0
def dict2columns(self, data): def dict2columns(self, data):

@ -46,7 +46,8 @@ def _format_description(parser):
information in their help messages if they so choose. information in their help messages if they so choose.
""" """
for line in statemachine.string2lines( for line in statemachine.string2lines(
parser.description, tab_width=4, convert_whitespace=True): parser.description, tab_width=4, convert_whitespace=True
):
yield line yield line
@ -64,12 +65,15 @@ def _format_usage(parser):
# becomes ['--format <FORMAT>'] and not ['--format', '<FORMAT>']. # becomes ['--format <FORMAT>'] and not ['--format', '<FORMAT>'].
# Yes, they really do use regexes to break apart and rewrap their help # Yes, they really do use regexes to break apart and rewrap their help
# string. Don't ask me why. # string. Don't ask me why.
part_regexp = re.compile(r""" part_regexp = re.compile(
r"""
\(.*?\)+ | \(.*?\)+ |
\[.*?\]+ | \[.*?\]+ |
(?:(?:-\w|--\w+(?:-\w+)*)(?:\s+<?\w[\w-]*>?)?) | (?:(?:-\w|--\w+(?:-\w+)*)(?:\s+<?\w[\w-]*>?)?) |
\S+ \S+
""", re.VERBOSE) """,
re.VERBOSE,
)
opt_usage = fmt._format_actions_usage(optionals, groups) opt_usage = fmt._format_actions_usage(optionals, groups)
pos_usage = fmt._format_actions_usage(positionals, groups) pos_usage = fmt._format_actions_usage(positionals, groups)
@ -91,7 +95,8 @@ def _format_epilog(parser):
information in their help messages if they so choose. information in their help messages if they so choose.
""" """
for line in statemachine.string2lines( for line in statemachine.string2lines(
parser.epilog, tab_width=4, convert_whitespace=True): parser.epilog, tab_width=4, convert_whitespace=True
):
yield line yield line
@ -104,11 +109,13 @@ def _format_positional_action(action):
# the 'option' directive dictates that only option argument names should be # the 'option' directive dictates that only option argument names should be
# surrounded by angle brackets # surrounded by angle brackets
yield '.. option:: {}'.format( yield '.. option:: {}'.format(
(action.metavar or action.dest).strip('<>[]() ')) (action.metavar or action.dest).strip('<>[]() ')
)
if action.help: if action.help:
yield '' yield ''
for line in statemachine.string2lines( for line in statemachine.string2lines(
action.help, tab_width=4, convert_whitespace=True): action.help, tab_width=4, convert_whitespace=True
):
yield _indent(line) yield _indent(line)
@ -123,15 +130,17 @@ def _format_optional_action(action):
# TODO(stephenfin): At some point, we may wish to provide more # TODO(stephenfin): At some point, we may wish to provide more
# information about the options themselves, for example, if nargs is # information about the options themselves, for example, if nargs is
# specified # specified
option_strings = [' '.join( option_strings = [
[x, action.metavar or '<{}>'.format(action.dest.upper())]) ' '.join([x, action.metavar or '<{}>'.format(action.dest.upper())])
for x in action.option_strings] for x in action.option_strings
]
yield '.. option:: {}'.format(', '.join(option_strings)) yield '.. option:: {}'.format(', '.join(option_strings))
if action.help: if action.help:
yield '' yield ''
for line in statemachine.string2lines( for line in statemachine.string2lines(
action.help, tab_width=4, convert_whitespace=True): action.help, tab_width=4, convert_whitespace=True
):
yield _indent(line) yield _indent(line)
@ -220,8 +229,9 @@ class AutoprogramCliffDirective(rst.Directive):
def _get_ignored_opts(self): def _get_ignored_opts(self):
global_ignored = self.env.config.autoprogram_cliff_ignored global_ignored = self.env.config.autoprogram_cliff_ignored
local_ignored = self.options.get('ignored', '') local_ignored = self.options.get('ignored', '')
local_ignored = [x.strip() for x in local_ignored.split(',') local_ignored = [
if x.strip()] x.strip() for x in local_ignored.split(',') if x.strip()
]
return list(set(global_ignored + local_ignored)) return list(set(global_ignored + local_ignored))
def _drop_ignored_options(self, parser, ignored_opts): def _drop_ignored_options(self, parser, ignored_opts):
@ -256,9 +266,10 @@ class AutoprogramCliffDirective(rst.Directive):
# find_command expects the value of argv so split to emulate that # find_command expects the value of argv so split to emulate that
return manager.find_command(command_name.split())[0] return manager.find_command(command_name.split())[0]
except ValueError: except ValueError:
raise self.error('"{}" is not a valid command in the "{}" ' raise self.error(
'namespace'.format( '"{}" is not a valid command in the "{}" '
command_name, manager.namespace)) 'namespace'.format(command_name, manager.namespace)
)
def _load_commands(self): def _load_commands(self):
# TODO(sfinucan): We should probably add this wildcarding functionality # TODO(sfinucan): We should probably add this wildcarding functionality
@ -267,8 +278,11 @@ class AutoprogramCliffDirective(rst.Directive):
command_pattern = self.options.get('command') command_pattern = self.options.get('command')
manager = commandmanager.CommandManager(self.arguments[0]) manager = commandmanager.CommandManager(self.arguments[0])
if command_pattern: if command_pattern:
commands = [x for x in manager.commands commands = [
if fnmatch.fnmatch(x, command_pattern)] x
for x in manager.commands
if fnmatch.fnmatch(x, command_pattern)
]
else: else:
commands = manager.commands.keys() commands = manager.commands.keys()
@ -276,12 +290,15 @@ class AutoprogramCliffDirective(rst.Directive):
msg = 'No commands found in the "{}" namespace' msg = 'No commands found in the "{}" namespace'
if command_pattern: if command_pattern:
msg += ' using the "{}" command name/pattern' msg += ' using the "{}" command name/pattern'
msg += ('. Are you sure this is correct and the application being ' msg += (
'documented is installed?') '. Are you sure this is correct and the application being '
'documented is installed?'
)
raise self.warning(msg.format(self.arguments[0], command_pattern)) raise self.warning(msg.format(self.arguments[0], command_pattern))
return dict((name, self._load_command(manager, name)) return dict(
for name in commands) (name, self._load_command(manager, name)) for name in commands
)
def _generate_app_node(self, app, application_name): def _generate_app_node(self, app, application_name):
ignored_opts = self._get_ignored_opts() ignored_opts = self._get_ignored_opts()
@ -303,8 +320,9 @@ class AutoprogramCliffDirective(rst.Directive):
# return [section.children] # return [section.children]
return section.children return section.children
def _generate_nodes_per_command(self, title, command_name, command_class, def _generate_nodes_per_command(
ignored_opts): self, title, command_name, command_class, ignored_opts
):
"""Generate the relevant Sphinx nodes. """Generate the relevant Sphinx nodes.
This doesn't bother using raw docutils nodes as they simply don't offer This doesn't bother using raw docutils nodes as they simply don't offer
@ -324,7 +342,8 @@ class AutoprogramCliffDirective(rst.Directive):
command = command_class(None, None) command = command_class(None, None)
if not getattr(command, 'app_dist_name', None): if not getattr(command, 'app_dist_name', None):
command.app_dist_name = ( command.app_dist_name = (
self.env.config.autoprogram_cliff_app_dist_name) self.env.config.autoprogram_cliff_app_dist_name
)
parser = command.get_parser(command_name) parser = command.get_parser(command_name)
ignored_opts = ignored_opts or [] ignored_opts = ignored_opts or []
@ -334,7 +353,8 @@ class AutoprogramCliffDirective(rst.Directive):
'', '',
nodes.title(text=title), nodes.title(text=title),
ids=[nodes.make_id(title)], ids=[nodes.make_id(title)],
names=[nodes.fully_normalize_name(title)]) names=[nodes.fully_normalize_name(title)],
)
source_name = '<{}>'.format(command.__class__.__name__) source_name = '<{}>'.format(command.__class__.__name__)
result = statemachine.ViewList() result = statemachine.ViewList()
@ -355,16 +375,21 @@ class AutoprogramCliffDirective(rst.Directive):
if application_name: if application_name:
command_name = ' '.join([application_name, command_name]) command_name = ' '.join([application_name, command_name])
output.extend(self._generate_nodes_per_command( output.extend(
title, command_name, command_class, ignored_opts)) self._generate_nodes_per_command(
title, command_name, command_class, ignored_opts
)
)
return output return output
def run(self): def run(self):
self.env = self.state.document.settings.env self.env = self.state.document.settings.env
application_name = (self.options.get('application') application_name = (
or self.env.config.autoprogram_cliff_application) self.options.get('application')
or self.env.config.autoprogram_cliff_application
)
app = self._load_app() app = self._load_app()
if app: if app:

@ -18,7 +18,6 @@ import fixtures
class TestBase(testtools.TestCase): class TestBase(testtools.TestCase):
def setUp(self): def setUp(self):
super(TestBase, self).setUp() super(TestBase, self).setUp()
self._stdout_fixture = fixtures.StringStream('stdout') self._stdout_fixture = fixtures.StringStream('stdout')

@ -19,7 +19,6 @@ from cliff import _argparse
class TestArgparse(unittest.TestCase): class TestArgparse(unittest.TestCase):
def test_argument_parser(self): def test_argument_parser(self):
_argparse.ArgumentParser(conflict_handler='ignore') _argparse.ArgumentParser(conflict_handler='ignore')

@ -48,32 +48,28 @@ def make_app(**kwargs):
# Register a command that is interrrupted # Register a command that is interrrupted
interrupt_command = mock.Mock(name='interrupt_command', spec=c_cmd.Command) interrupt_command = mock.Mock(name='interrupt_command', spec=c_cmd.Command)
interrupt_command_inst = mock.Mock(spec=c_cmd.Command) interrupt_command_inst = mock.Mock(spec=c_cmd.Command)
interrupt_command_inst.run = mock.Mock( interrupt_command_inst.run = mock.Mock(side_effect=KeyboardInterrupt)
side_effect=KeyboardInterrupt
)
interrupt_command.return_value = interrupt_command_inst interrupt_command.return_value = interrupt_command_inst
cmd_mgr.add_command('interrupt', interrupt_command) cmd_mgr.add_command('interrupt', interrupt_command)
# Register a command that is interrrupted by a broken pipe # Register a command that is interrrupted by a broken pipe
pipeclose_command = mock.Mock(name='pipeclose_command', spec=c_cmd.Command) pipeclose_command = mock.Mock(name='pipeclose_command', spec=c_cmd.Command)
pipeclose_command_inst = mock.Mock(spec=c_cmd.Command) pipeclose_command_inst = mock.Mock(spec=c_cmd.Command)
pipeclose_command_inst.run = mock.Mock( pipeclose_command_inst.run = mock.Mock(side_effect=BrokenPipeError)
side_effect=BrokenPipeError
)
pipeclose_command.return_value = pipeclose_command_inst pipeclose_command.return_value = pipeclose_command_inst
cmd_mgr.add_command('pipe-close', pipeclose_command) cmd_mgr.add_command('pipe-close', pipeclose_command)
app = application.App('testing interactive mode', app = application.App(
'1', 'testing interactive mode',
cmd_mgr, '1',
stderr=mock.Mock(), # suppress warning messages cmd_mgr,
**kwargs stderr=mock.Mock(), # suppress warning messages
) **kwargs
)
return app, command return app, command
class TestInteractiveMode(base.TestBase): class TestInteractiveMode(base.TestBase):
def test_no_args_triggers_interactive_mode(self): def test_no_args_triggers_interactive_mode(self):
app, command = make_app() app, command = make_app()
app.interact = mock.MagicMock(name='inspect') app.interact = mock.MagicMock(name='inspect')
@ -110,7 +106,6 @@ class TestInteractiveMode(base.TestBase):
class TestInitAndCleanup(base.TestBase): class TestInitAndCleanup(base.TestBase):
def test_initialize_app(self): def test_initialize_app(self):
app, command = make_app() app, command = make_app()
app.initialize_app = mock.MagicMock(name='initialize_app') app.initialize_app = mock.MagicMock(name='initialize_app')
@ -266,7 +261,6 @@ class TestInitAndCleanup(base.TestBase):
class TestOptionParser(base.TestBase): class TestOptionParser(base.TestBase):
def test_conflicting_option_should_throw(self): def test_conflicting_option_should_throw(self):
class MyApp(application.App): class MyApp(application.App):
def __init__(self): def __init__(self):
@ -277,10 +271,12 @@ class TestOptionParser(base.TestBase):
) )
def build_option_parser(self, description, version): def build_option_parser(self, description, version):
parser = super(MyApp, self).build_option_parser(description, parser = super(MyApp, self).build_option_parser(
version) description, version
)
parser.add_argument( parser.add_argument(
'-h', '--help', '-h',
'--help',
default=self, # tricky default=self, # tricky
help="Show help message and exit.", help="Show help message and exit.",
) )
@ -302,11 +298,11 @@ class TestOptionParser(base.TestBase):
def build_option_parser(self, description, version): def build_option_parser(self, description, version):
argparse_kwargs = {'conflict_handler': 'resolve'} argparse_kwargs = {'conflict_handler': 'resolve'}
parser = super(MyApp, self).build_option_parser( parser = super(MyApp, self).build_option_parser(
description, description, version, argparse_kwargs=argparse_kwargs
version, )
argparse_kwargs=argparse_kwargs)
parser.add_argument( parser.add_argument(
'-h', '--help', '-h',
'--help',
default=self, # tricky default=self, # tricky
help="Show help message and exit.", help="Show help message and exit.",
) )
@ -339,7 +335,8 @@ class TestOptionParser(base.TestBase):
parser = super(MyApp, self).build_option_parser( parser = super(MyApp, self).build_option_parser(
description, description,
version, version,
argparse_kwargs={'allow_abbrev': False}) argparse_kwargs={'allow_abbrev': False},
)
parser.add_argument('--endpoint') parser.add_argument('--endpoint')
return parser return parser
@ -350,12 +347,12 @@ class TestOptionParser(base.TestBase):
class TestHelpHandling(base.TestBase): class TestHelpHandling(base.TestBase):
def _test_help(self, deferred_help): def _test_help(self, deferred_help):
app, _ = make_app(deferred_help=deferred_help) app, _ = make_app(deferred_help=deferred_help)
with mock.patch.object(app, 'initialize_app') as init: with mock.patch.object(app, 'initialize_app') as init:
with mock.patch('cliff.help.HelpAction.__call__', with mock.patch(
side_effect=SystemExit(0)) as helper: 'cliff.help.HelpAction.__call__', side_effect=SystemExit(0)
) as helper:
self.assertRaises( self.assertRaises(
SystemExit, SystemExit,
app.run, app.run,
@ -372,8 +369,9 @@ class TestHelpHandling(base.TestBase):
def _test_interrupted_help(self, deferred_help): def _test_interrupted_help(self, deferred_help):
app, _ = make_app(deferred_help=deferred_help) app, _ = make_app(deferred_help=deferred_help)
with mock.patch('cliff.help.HelpAction.__call__', with mock.patch(
side_effect=KeyboardInterrupt): 'cliff.help.HelpAction.__call__', side_effect=KeyboardInterrupt
):
result = app.run(['--help']) result = app.run(['--help'])
self.assertEqual(result, 130) self.assertEqual(result, 130)
@ -385,8 +383,9 @@ class TestHelpHandling(base.TestBase):
def _test_pipeclose_help(self, deferred_help): def _test_pipeclose_help(self, deferred_help):
app, _ = make_app(deferred_help=deferred_help) app, _ = make_app(deferred_help=deferred_help)
with mock.patch('cliff.help.HelpAction.__call__', with mock.patch(
side_effect=BrokenPipeError): 'cliff.help.HelpAction.__call__', side_effect=BrokenPipeError
):
app.run(['--help']) app.run(['--help'])
def test_pipeclose_help(self): def test_pipeclose_help(self):
@ -415,7 +414,6 @@ class TestHelpHandling(base.TestBase):
class TestCommandLookup(base.TestBase): class TestCommandLookup(base.TestBase):
def test_unknown_cmd(self): def test_unknown_cmd(self):
app, command = make_app() app, command = make_app()
self.assertEqual(2, app.run(['hell'])) self.assertEqual(2, app.run(['hell']))
@ -429,18 +427,21 @@ class TestCommandLookup(base.TestBase):
def test_list_matching_commands(self): def test_list_matching_commands(self):
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
test_utils.TestCommandManager( 'testing',
test_utils.TEST_NAMESPACE), '1',
stdout=stdout) test_utils.TestCommandManager(test_utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
try: try:
self.assertEqual(2, app.run(['t'])) self.assertEqual(2, app.run(['t']))
except SystemExit: except SystemExit:
pass pass
output = stdout.getvalue() output = stdout.getvalue()
self.assertIn("test: 't' is not a test command. See 'test --help'.", self.assertIn(
output) "test: 't' is not a test command. See 'test --help'.", output
)
self.assertIn('Did you mean one of these?', output) self.assertIn('Did you mean one of these?', output)
self.assertIn('three word command\n two words\n', output) self.assertIn('three word command\n two words\n', output)
@ -484,7 +485,6 @@ class TestCommandLookup(base.TestBase):
class TestVerboseMode(base.TestBase): class TestVerboseMode(base.TestBase):
def test_verbose(self): def test_verbose(self):
app, command = make_app() app, command = make_app()
app.clean_up = mock.MagicMock(name='clean_up') app.clean_up = mock.MagicMock(name='clean_up')
@ -501,7 +501,6 @@ class TestVerboseMode(base.TestBase):
class TestIO(base.TestBase): class TestIO(base.TestBase):
def test_io_streams(self): def test_io_streams(self):
cmd_mgr = commandmanager.CommandManager('cliff.tests') cmd_mgr = commandmanager.CommandManager('cliff.tests')
io = mock.Mock() io = mock.Mock()
@ -516,14 +515,12 @@ class TestIO(base.TestBase):
self.assertIs(sys.stdout, app.stdout) self.assertIs(sys.stdout, app.stdout)
self.assertIs(sys.stderr, app.stderr) self.assertIs(sys.stderr, app.stderr)
app = application.App('with stdout io stream', 1, cmd_mgr, app = application.App('with stdout io stream', 1, cmd_mgr, stdout=io)
stdout=io)
self.assertIs(sys.stdin, app.stdin) self.assertIs(sys.stdin, app.stdin)
self.assertIs(io, app.stdout) self.assertIs(io, app.stdout)
self.assertIs(sys.stderr, app.stderr) self.assertIs(sys.stderr, app.stderr)
app = application.App('with stderr io stream', 1, cmd_mgr, app = application.App('with stderr io stream', 1, cmd_mgr, stderr=io)
stderr=io)
self.assertIs(sys.stdin, app.stdin) self.assertIs(sys.stdin, app.stdin)
self.assertIs(sys.stdout, app.stdout) self.assertIs(sys.stdout, app.stdout)
self.assertIs(io, app.stderr) self.assertIs(io, app.stderr)

@ -16,13 +16,11 @@ from cliff import columns
class FauxColumn(columns.FormattableColumn): class FauxColumn(columns.FormattableColumn):
def human_readable(self): def human_readable(self):
return 'I made this string myself: {}'.format(self._value) return 'I made this string myself: {}'.format(self._value)
class TestColumns(unittest.TestCase): class TestColumns(unittest.TestCase):
def test_machine_readable(self): def test_machine_readable(self):
c = FauxColumn(['list', 'of', 'values']) c = FauxColumn(['list', 'of', 'values'])
self.assertEqual(['list', 'of', 'values'], c.machine_readable()) self.assertEqual(['list', 'of', 'values'], c.machine_readable())

@ -18,30 +18,28 @@ from cliff.tests import base
class TestCommand(command.Command): class TestCommand(command.Command):
"""Description of command. """Description of command."""
"""
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(TestCommand, self).get_parser(prog_name) parser = super(TestCommand, self).get_parser(prog_name)
parser.add_argument( parser.add_argument(
'long_help_argument', 'long_help_argument',
help="Create a NIC on the server.\n" help="Create a NIC on the server.\n"
"Specify option multiple times to create multiple NICs. " "Specify option multiple times to create multiple NICs. "
"Either net-id or port-id must be provided, but not both.\n" "Either net-id or port-id must be provided, but not both.\n"
"net-id: attach NIC to network with this UUID\n" "net-id: attach NIC to network with this UUID\n"
"port-id: attach NIC to port with this UUID\n" "port-id: attach NIC to port with this UUID\n"
"v4-fixed-ip: IPv4 fixed address for NIC (optional)\n" "v4-fixed-ip: IPv4 fixed address for NIC (optional)\n"
"v6-fixed-ip: IPv6 fixed address for NIC (optional)\n" "v6-fixed-ip: IPv6 fixed address for NIC (optional)\n"
"none: (v2.37+) no network is attached\n" "none: (v2.37+) no network is attached\n"
"auto: (v2.37+) the compute service will automatically " "auto: (v2.37+) the compute service will automatically "
"allocate a network.\n" "allocate a network.\n"
"Specifying a --nic of auto or none " "Specifying a --nic of auto or none "
"cannot be used with any other --nic value.", "cannot be used with any other --nic value.",
) )
parser.add_argument( parser.add_argument(
'regular_help_argument', 'regular_help_argument',
help="The quick brown fox jumps " help="The quick brown fox jumps " "over the lazy dog.",
"over the lazy dog.",
) )
parser.add_argument( parser.add_argument(
'-z', '-z',
@ -56,17 +54,15 @@ class TestCommand(command.Command):
class TestCommandNoDocstring(command.Command): class TestCommandNoDocstring(command.Command):
def take_action(self, parsed_args): def take_action(self, parsed_args):
return 42 return 42
class TestDescription(base.TestBase): class TestDescription(base.TestBase):
def test_get_description_docstring(self): def test_get_description_docstring(self):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
desc = cmd.get_description() desc = cmd.get_description()
assert desc == "Description of command.\n " assert desc == "Description of command."
def test_get_description_attribute(self): def test_get_description_attribute(self):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
@ -83,7 +79,6 @@ class TestDescription(base.TestBase):
class TestBasicValues(base.TestBase): class TestBasicValues(base.TestBase):
def test_get_parser(self): def test_get_parser(self):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
parser = cmd.get_parser('NAME') parser = cmd.get_parser('NAME')
@ -118,7 +113,6 @@ expected_help_message = """
class TestHelp(base.TestBase): class TestHelp(base.TestBase):
def test_smart_help_formatter(self): def test_smart_help_formatter(self):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
parser = cmd.get_parser('NAME') parser = cmd.get_parser('NAME')
@ -138,7 +132,6 @@ class TestHelp(base.TestBase):
class TestArgumentParser(base.TestBase): class TestArgumentParser(base.TestBase):
def test_option_name_collision(self): def test_option_name_collision(self):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
parser = cmd.get_parser('NAME') parser = cmd.get_parser('NAME')
@ -165,7 +158,8 @@ class TestArgumentParser(base.TestBase):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
parser = cmd.get_parser('NAME') parser = cmd.get_parser('NAME')
parser.add_argument( parser.add_argument(
'-z', '--zero', '-z',
'--zero',
dest='zero', dest='zero',
default='zero-default', default='zero-default',
) )
@ -183,7 +177,8 @@ class TestArgumentParser(base.TestBase):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
parser = cmd.get_parser('NAME') parser = cmd.get_parser('NAME')
parser.add_argument( parser.add_argument(
'-f', '--foo', '-f',
'--foo',
dest='foo', dest='foo',
default='foo', default='foo',
) )
@ -198,12 +193,14 @@ class TestArgumentParser(base.TestBase):
cmd.conflict_handler = 'resolve' cmd.conflict_handler = 'resolve'
parser = cmd.get_parser('NAME') parser = cmd.get_parser('NAME')
parser.add_argument( parser.add_argument(
'-f', '--foo', '-f',
'--foo',
dest='foo', dest='foo',
default='foo', default='foo',
) )
parser.add_argument( parser.add_argument(
'-f', '--foo', '-f',
'--foo',
dest='foo', dest='foo',
default='bar', default='bar',
) )

@ -41,18 +41,18 @@ def make_app(**kwargs):
err_command.return_value = err_command_inst err_command.return_value = err_command_inst
cmd_mgr.add_command('error', err_command) cmd_mgr.add_command('error', err_command)
app = application.App('testing command hooks', app = application.App(
'1', 'testing command hooks',
cmd_mgr, '1',
stderr=mock.Mock(), # suppress warning messages cmd_mgr,
**kwargs stderr=mock.Mock(), # suppress warning messages
) **kwargs
)
return app return app
class TestCommand(command.Command): class TestCommand(command.Command):
"""Description of command. """Description of command."""
"""
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(TestCommand, self).get_parser(prog_name) parser = super(TestCommand, self).get_parser(prog_name)
@ -63,23 +63,20 @@ class TestCommand(command.Command):
class TestShowCommand(show.ShowOne): class TestShowCommand(show.ShowOne):
"""Description of command. """Description of command."""
"""
def take_action(self, parsed_args): def take_action(self, parsed_args):
return (('Name',), ('value',)) return (('Name',), ('value',))
class TestListerCommand(lister.Lister): class TestListerCommand(lister.Lister):
"""Description of command. """Description of command."""
"""
def take_action(self, parsed_args): def take_action(self, parsed_args):
return (('Name',), [('value',)]) return (('Name',), [('value',)])
class TestHook(hooks.CommandHook): class TestHook(hooks.CommandHook):
_before_called = False _before_called = False
_after_called = False _after_called = False
@ -98,7 +95,6 @@ class TestHook(hooks.CommandHook):
class TestChangeHook(hooks.CommandHook): class TestChangeHook(hooks.CommandHook):
_before_called = False _before_called = False
_after_called = False _after_called = False
@ -121,7 +117,6 @@ class TestChangeHook(hooks.CommandHook):
class TestDisplayChangeHook(hooks.CommandHook): class TestDisplayChangeHook(hooks.CommandHook):
_before_called = False _before_called = False
_after_called = False _after_called = False
@ -144,7 +139,6 @@ class TestDisplayChangeHook(hooks.CommandHook):
class TestListerChangeHook(hooks.CommandHook): class TestListerChangeHook(hooks.CommandHook):
_before_called = False _before_called = False
_after_called = False _after_called = False
@ -167,7 +161,6 @@ class TestListerChangeHook(hooks.CommandHook):
class TestCommandLoadHooks(base.TestBase): class TestCommandLoadHooks(base.TestBase):
def test_no_app_or_name(self): def test_no_app_or_name(self):
cmd = TestCommand(None, None) cmd = TestCommand(None, None)
self.assertEqual([], cmd._hooks) self.assertEqual([], cmd._hooks)
@ -183,18 +176,13 @@ class TestCommandLoadHooks(base.TestBase):
class TestHooks(base.TestBase): class TestHooks(base.TestBase):
def setUp(self): def setUp(self):
super(TestHooks, self).setUp() super(TestHooks, self).setUp()
self.app = make_app() self.app = make_app()
self.cmd = TestCommand(self.app, None, cmd_name='test') self.cmd = TestCommand(self.app, None, cmd_name='test')
self.hook = TestHook(self.cmd) self.hook = TestHook(self.cmd)
self.mgr = extension.ExtensionManager.make_test_instance( self.mgr = extension.ExtensionManager.make_test_instance(
[extension.Extension( [extension.Extension('parser-hook', None, None, self.hook)],
'parser-hook',
None,
None,
self.hook)],
) )
# Replace the auto-loaded hooks with our explicitly created # Replace the auto-loaded hooks with our explicitly created
# manager. # manager.
@ -222,18 +210,13 @@ class TestHooks(base.TestBase):
class TestChangeHooks(base.TestBase): class TestChangeHooks(base.TestBase):
def setUp(self): def setUp(self):
super(TestChangeHooks, self).setUp() super(TestChangeHooks, self).setUp()
self.app = make_app() self.app = make_app()
self.cmd = TestCommand(self.app, None, cmd_name='test') self.cmd = TestCommand(self.app, None, cmd_name='test')
self.hook = TestChangeHook(self.cmd) self.hook = TestChangeHook(self.cmd)
self.mgr = extension.ExtensionManager.make_test_instance( self.mgr = extension.ExtensionManager.make_test_instance(
[extension.Extension( [extension.Extension('parser-hook', None, None, self.hook)],
'parser-hook',
None,
None,
self.hook)],
) )
# Replace the auto-loaded hooks with our explicitly created # Replace the auto-loaded hooks with our explicitly created
# manager. # manager.
@ -267,18 +250,13 @@ class TestChangeHooks(base.TestBase):
class TestShowOneHooks(base.TestBase): class TestShowOneHooks(base.TestBase):
def setUp(self): def setUp(self):
super(TestShowOneHooks, self).setUp() super(TestShowOneHooks, self).setUp()
self.app = make_app() self.app = make_app()
self.cmd = TestShowCommand(self.app, None, cmd_name='test') self.cmd = TestShowCommand(self.app, None, cmd_name='test')
self.hook = TestHook(self.cmd) self.hook = TestHook(self.cmd)
self.mgr = extension.ExtensionManager.make_test_instance( self.mgr = extension.ExtensionManager.make_test_instance(
[extension.Extension( [extension.Extension('parser-hook', None, None, self.hook)],
'parser-hook',
None,
None,
self.hook)],
) )
# Replace the auto-loaded hooks with our explicitly created # Replace the auto-loaded hooks with our explicitly created
# manager. # manager.
@ -309,18 +287,13 @@ class TestShowOneHooks(base.TestBase):
class TestShowOneChangeHooks(base.TestBase): class TestShowOneChangeHooks(base.TestBase):
def setUp(self): def setUp(self):
super(TestShowOneChangeHooks, self).setUp() super(TestShowOneChangeHooks, self).setUp()
self.app = make_app() self.app = make_app()
self.cmd = TestShowCommand(self.app, None, cmd_name='test') self.cmd = TestShowCommand(self.app, None, cmd_name='test')
self.hook = TestDisplayChangeHook(self.cmd) self.hook = TestDisplayChangeHook(self.cmd)
self.mgr = extension.ExtensionManager.make_test_instance( self.mgr = extension.ExtensionManager.make_test_instance(
[extension.Extension( [extension.Extension('parser-hook', None, None, self.hook)],
'parser-hook',
None,
None,
self.hook)],
) )
# Replace the auto-loaded hooks with our explicitly created # Replace the auto-loaded hooks with our explicitly created
# manager. # manager.
@ -354,18 +327,13 @@ class TestShowOneChangeHooks(base.TestBase):
class TestListerHooks(base.TestBase): class TestListerHooks(base.TestBase):
def setUp(self): def setUp(self):
super(TestListerHooks, self).setUp() super(TestListerHooks, self).setUp()
self.app = make_app() self.app = make_app()
self.cmd = TestListerCommand(self.app, None, cmd_name='test') self.cmd = TestListerCommand(self.app, None, cmd_name='test')
self.hook = TestHook(self.cmd) self.hook = TestHook(self.cmd)
self.mgr = extension.ExtensionManager.make_test_instance( self.mgr = extension.ExtensionManager.make_test_instance(
[extension.Extension( [extension.Extension('parser-hook', None, None, self.hook)],
'parser-hook',
None,
None,
self.hook)],
) )
# Replace the auto-loaded hooks with our explicitly created # Replace the auto-loaded hooks with our explicitly created
# manager. # manager.
@ -396,18 +364,13 @@ class TestListerHooks(base.TestBase):
class TestListerChangeHooks(base.TestBase): class TestListerChangeHooks(base.TestBase):
def setUp(self): def setUp(self):
super(TestListerChangeHooks, self).setUp() super(TestListerChangeHooks, self).setUp()
self.app = make_app() self.app = make_app()
self.cmd = TestListerCommand(self.app, None, cmd_name='test') self.cmd = TestListerCommand(self.app, None, cmd_name='test')
self.hook = TestListerChangeHook(self.cmd) self.hook = TestListerChangeHook(self.cmd)
self.mgr = extension.ExtensionManager.make_test_instance( self.mgr = extension.ExtensionManager.make_test_instance(
[extension.Extension( [extension.Extension('parser-hook', None, None, self.hook)],
'parser-hook',
None,
None,
self.hook)],
) )
# Replace the auto-loaded hooks with our explicitly created # Replace the auto-loaded hooks with our explicitly created
# manager. # manager.

@ -23,7 +23,6 @@ load_tests = testscenarios.load_tests_apply_scenarios
class TestLookupAndFind(base.TestBase): class TestLookupAndFind(base.TestBase):
scenarios = [ scenarios = [
('one-word', {'argv': ['one']}), ('one-word', {'argv': ['one']}),
('two-words', {'argv': ['two', 'words']}), ('two-words', {'argv': ['two', 'words']}),
@ -39,7 +38,6 @@ class TestLookupAndFind(base.TestBase):
class TestLookupWithRemainder(base.TestBase): class TestLookupWithRemainder(base.TestBase):
scenarios = [ scenarios = [
('one', {'argv': ['one', '--opt']}), ('one', {'argv': ['one', '--opt']}),
('two', {'argv': ['two', 'words', '--opt']}), ('two', {'argv': ['two', 'words', '--opt']}),
@ -54,7 +52,6 @@ class TestLookupWithRemainder(base.TestBase):
class TestFindInvalidCommand(base.TestBase): class TestFindInvalidCommand(base.TestBase):
scenarios = [ scenarios = [
('no-such-command', {'argv': ['a', '-b']}), ('no-such-command', {'argv': ['a', '-b']}),
('no-command-given', {'argv': ['-b']}), ('no-command-given', {'argv': ['-b']}),
@ -73,7 +70,6 @@ class TestFindInvalidCommand(base.TestBase):
class TestFindUnknownCommand(base.TestBase): class TestFindUnknownCommand(base.TestBase):
def test(self): def test(self):
mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) mgr = utils.TestCommandManager(utils.TEST_NAMESPACE)
try: try:
@ -85,7 +81,6 @@ class TestFindUnknownCommand(base.TestBase):
class TestDynamicCommands(base.TestBase): class TestDynamicCommands(base.TestBase):
def test_add(self): def test_add(self):
mgr = utils.TestCommandManager(utils.TEST_NAMESPACE) mgr = utils.TestCommandManager(utils.TEST_NAMESPACE)
mock_cmd = mock.Mock() mock_cmd = mock.Mock()
@ -112,13 +107,13 @@ class TestDynamicCommands(base.TestBase):
class TestLoad(base.TestBase): class TestLoad(base.TestBase):
def test_load_commands(self): def test_load_commands(self):
testcmd = mock.Mock(name='testcmd') testcmd = mock.Mock(name='testcmd')
testcmd.name.replace.return_value = 'test' testcmd.name.replace.return_value = 'test'
mock_get_group_all = mock.Mock(return_value=[testcmd]) mock_get_group_all = mock.Mock(return_value=[testcmd])
with mock.patch('stevedore.ExtensionManager', with mock.patch(
mock_get_group_all) as mock_manager: 'stevedore.ExtensionManager', mock_get_group_all
) as mock_manager:
mgr = commandmanager.CommandManager('test') mgr = commandmanager.CommandManager('test')
mock_manager.assert_called_once_with('test') mock_manager.assert_called_once_with('test')
names = [n for n, v in mgr] names = [n for n, v in mgr]
@ -128,8 +123,9 @@ class TestLoad(base.TestBase):
testcmd = mock.Mock() testcmd = mock.Mock()
testcmd.name = 'test_cmd' testcmd.name = 'test_cmd'
mock_get_group_all = mock.Mock(return_value=[testcmd]) mock_get_group_all = mock.Mock(return_value=[testcmd])
with mock.patch('stevedore.ExtensionManager', with mock.patch(
mock_get_group_all) as mock_manager: 'stevedore.ExtensionManager', mock_get_group_all
) as mock_manager:
mgr = commandmanager.CommandManager( mgr = commandmanager.CommandManager(
'test', 'test',
convert_underscores=False, convert_underscores=False,
@ -142,8 +138,9 @@ class TestLoad(base.TestBase):
testcmd = mock.Mock() testcmd = mock.Mock()
testcmd.name = 'test_cmd' testcmd.name = 'test_cmd'
mock_get_group_all = mock.Mock(return_value=[testcmd]) mock_get_group_all = mock.Mock(return_value=[testcmd])
with mock.patch('stevedore.ExtensionManager', with mock.patch(
mock_get_group_all) as mock_manager: 'stevedore.ExtensionManager', mock_get_group_all
) as mock_manager:
mgr = commandmanager.CommandManager( mgr = commandmanager.CommandManager(
'test', 'test',
convert_underscores=True, convert_underscores=True,
@ -154,7 +151,6 @@ class TestLoad(base.TestBase):
class FauxCommand(command.Command): class FauxCommand(command.Command):
def take_action(self, parsed_args): def take_action(self, parsed_args):
return 0 return 0
@ -164,7 +160,6 @@ class FauxCommand2(FauxCommand):
class TestLegacyCommand(base.TestBase): class TestLegacyCommand(base.TestBase):
def test_find_legacy(self): def test_find_legacy(self):
mgr = utils.TestCommandManager(None) mgr = utils.TestCommandManager(None)
mgr.add_command('new name', FauxCommand) mgr.add_command('new name', FauxCommand)
@ -202,7 +197,6 @@ class TestLegacyCommand(base.TestBase):
class TestLookupAndFindPartialName(base.TestBase): class TestLookupAndFindPartialName(base.TestBase):
scenarios = [ scenarios = [
('one-word', {'argv': ['o']}), ('one-word', {'argv': ['o']}),
('two-words', {'argv': ['t', 'w']}), ('two-words', {'argv': ['t', 'w']}),
@ -218,45 +212,61 @@ class TestLookupAndFindPartialName(base.TestBase):
class TestGetByPartialName(base.TestBase): class TestGetByPartialName(base.TestBase):
def setUp(self): def setUp(self):
super(TestGetByPartialName, self).setUp() super(TestGetByPartialName, self).setUp()
self.commands = { self.commands = {
'resource provider list': 1, 'resource provider list': 1,
'resource class list': 2, 'resource class list': 2,
'server list': 3, 'server list': 3,
'service list': 4} 'service list': 4,
}
def test_no_candidates(self): def test_no_candidates(self):
self.assertEqual( self.assertEqual(
[], commandmanager._get_commands_by_partial_name( [],
['r', 'p'], self.commands)) commandmanager._get_commands_by_partial_name(
['r', 'p'], self.commands
),
)
self.assertEqual( self.assertEqual(
[], commandmanager._get_commands_by_partial_name( [],
['r', 'p', 'c'], self.commands)) commandmanager._get_commands_by_partial_name(
['r', 'p', 'c'], self.commands
),
)
def test_multiple_candidates(self): def test_multiple_candidates(self):
self.assertEqual( self.assertEqual(
2, len(commandmanager._get_commands_by_partial_name( 2,
['se', 'li'], self.commands))) len(
commandmanager._get_commands_by_partial_name(
['se', 'li'], self.commands
)
),
)
def test_one_candidate(self): def test_one_candidate(self):
self.assertEqual( self.assertEqual(
['resource provider list'], ['resource provider list'],
commandmanager._get_commands_by_partial_name( commandmanager._get_commands_by_partial_name(
['r', 'p', 'l'], self.commands)) ['r', 'p', 'l'], self.commands
),
)
self.assertEqual( self.assertEqual(
['resource provider list'], ['resource provider list'],
commandmanager._get_commands_by_partial_name( commandmanager._get_commands_by_partial_name(
['resource', 'provider', 'list'], self.commands)) ['resource', 'provider', 'list'], self.commands
),
)
self.assertEqual( self.assertEqual(
['server list'], ['server list'],
commandmanager._get_commands_by_partial_name( commandmanager._get_commands_by_partial_name(
['serve', 'l'], self.commands)) ['serve', 'l'], self.commands
),
)
class FakeCommand(object): class FakeCommand(object):
@classmethod @classmethod
def load(cls): def load(cls):
return cls return cls
@ -286,7 +296,6 @@ class FakeCommandManager(commandmanager.CommandManager):
class TestCommandManagerGroups(base.TestBase): class TestCommandManagerGroups(base.TestBase):
def test_add_command_group(self): def test_add_command_group(self):
mgr = FakeCommandManager('test') mgr = FakeCommandManager('test')

@ -22,19 +22,23 @@ from cliff.tests import base
class TestCompletion(base.TestBase): class TestCompletion(base.TestBase):
def test_dictionary(self): def test_dictionary(self):
sot = complete.CompleteDictionary() sot = complete.CompleteDictionary()
sot.add_command("image delete".split(), sot.add_command(
[mock.Mock(option_strings=["1"])]) "image delete".split(), [mock.Mock(option_strings=["1"])]
sot.add_command("image list".split(), )
[mock.Mock(option_strings=["2"])]) sot.add_command(
sot.add_command("image create".split(), "image list".split(), [mock.Mock(option_strings=["2"])]
[mock.Mock(option_strings=["3"])]) )
sot.add_command("volume type create".split(), sot.add_command(
[mock.Mock(option_strings=["4"])]) "image create".split(), [mock.Mock(option_strings=["3"])]
sot.add_command("volume type delete".split(), )
[mock.Mock(option_strings=["5"])]) sot.add_command(
"volume type create".split(), [mock.Mock(option_strings=["4"])]
)
sot.add_command(
"volume type delete".split(), [mock.Mock(option_strings=["5"])]
)
self.assertEqual("image volume", sot.get_commands()) self.assertEqual("image volume", sot.get_commands())
result = sot.get_data() result = sot.get_data()
self.assertEqual("image", result[0][0]) self.assertEqual("image", result[0][0])
@ -48,12 +52,15 @@ class TestCompletion(base.TestBase):
def test_complete_dictionary_subcmd(self): def test_complete_dictionary_subcmd(self):
sot = complete.CompleteDictionary() sot = complete.CompleteDictionary()
sot.add_command("image delete".split(), sot.add_command(
[mock.Mock(option_strings=["1"])]) "image delete".split(), [mock.Mock(option_strings=["1"])]
sot.add_command("image list".split(), )
[mock.Mock(option_strings=["2"])]) sot.add_command(
sot.add_command("image list better".split(), "image list".split(), [mock.Mock(option_strings=["2"])]
[mock.Mock(option_strings=["3"])]) )
sot.add_command(
"image list better".split(), [mock.Mock(option_strings=["3"])]
)
self.assertEqual("image", sot.get_commands()) self.assertEqual("image", sot.get_commands())
result = sot.get_data() result = sot.get_data()
self.assertEqual("image", result[0][0]) self.assertEqual("image", result[0][0])
@ -81,14 +88,15 @@ class FakeStdout:
class TestCompletionAlternatives(base.TestBase): class TestCompletionAlternatives(base.TestBase):
def given_cmdo_data(self): def given_cmdo_data(self):
cmdo = "image server" cmdo = "image server"
data = [("image", "create"), data = [
("image_create", "--eolus"), ("image", "create"),
("server", "meta ssh"), ("image_create", "--eolus"),
("server_meta_delete", "--wilson"), ("server", "meta ssh"),
("server_ssh", "--sunlight")] ("server_meta_delete", "--wilson"),
("server_ssh", "--sunlight"),
]
return cmdo, data return cmdo, data
def then_data(self, content): def then_data(self, content):
@ -117,12 +125,10 @@ class TestCompletionAlternatives(base.TestBase):
sot = complete.CompleteCommand(mock.Mock(), mock.Mock()) sot = complete.CompleteCommand(mock.Mock(), mock.Mock())
parser = sot.get_parser('nothing') parser = sot.get_parser('nothing')
self.assertEqual("nothing", parser.prog) self.assertEqual("nothing", parser.prog)
self.assertEqual("print bash completion command\n ", self.assertEqual("print bash completion command", parser.description)
parser.description)
class TestCompletionAction(base.TestBase): class TestCompletionAction(base.TestBase):
def given_complete_command(self): def given_complete_command(self):
cmd_mgr = commandmanager.CommandManager('cliff.tests') cmd_mgr = commandmanager.CommandManager('cliff.tests')
app = application.App('testing', '1', cmd_mgr, stdout=FakeStdout()) app = application.App('testing', '1', cmd_mgr, stdout=FakeStdout())
@ -131,8 +137,9 @@ class TestCompletionAction(base.TestBase):
return sot, app, cmd_mgr return sot, app, cmd_mgr
def then_actions_equal(self, actions): def then_actions_equal(self, actions):
optstr = ' '.join(opt for action in actions optstr = ' '.join(
for opt in action.option_strings) opt for action in actions for opt in action.option_strings
)
self.assertEqual('-h --help --name --shell', optstr) self.assertEqual('-h --help --name --shell', optstr)
def test_complete_command_get_actions(self): def test_complete_command_get_actions(self):

@ -23,7 +23,6 @@ from cliff.tests import test_columns
class TestCSVFormatter(unittest.TestCase): class TestCSVFormatter(unittest.TestCase):
def test_commaseparated_list_formatter(self): def test_commaseparated_list_formatter(self):
sf = commaseparated.CSVLister() sf = commaseparated.CSVLister()
c = ('a', 'b', 'c') c = ('a', 'b', 'c')

@ -22,17 +22,11 @@ from cliff.tests import test_columns
class TestJSONFormatter(base.TestBase): class TestJSONFormatter(base.TestBase):
def test_one(self): def test_one(self):
sf = json_format.JSONFormatter() sf = json_format.JSONFormatter()
c = ('a', 'b', 'c', 'd') c = ('a', 'b', 'c', 'd')
d = ('A', 'B', 'C', '"escape me"') d = ('A', 'B', 'C', '"escape me"')
expected = { expected = {'a': 'A', 'b': 'B', 'c': 'C', 'd': '"escape me"'}
'a': 'A',
'b': 'B',
'c': 'C',
'd': '"escape me"'
}
args = mock.Mock() args = mock.Mock()
sf.add_argument_group(args) sf.add_argument_group(args)
@ -78,15 +72,11 @@ class TestJSONFormatter(base.TestBase):
def test_list(self): def test_list(self):
sf = json_format.JSONFormatter() sf = json_format.JSONFormatter()
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
d = ( d = (('A1', 'B1', 'C1'), ('A2', 'B2', 'C2'), ('A3', 'B3', 'C3'))
('A1', 'B1', 'C1'),
('A2', 'B2', 'C2'),
('A3', 'B3', 'C3')
)
expected = [ expected = [
{'a': 'A1', 'b': 'B1', 'c': 'C1'}, {'a': 'A1', 'b': 'B1', 'c': 'C1'},
{'a': 'A2', 'b': 'B2', 'c': 'C2'}, {'a': 'A2', 'b': 'B2', 'c': 'C2'},
{'a': 'A3', 'b': 'B3', 'c': 'C3'} {'a': 'A3', 'b': 'B3', 'c': 'C3'},
] ]
args = mock.Mock() args = mock.Mock()
sf.add_argument_group(args) sf.add_argument_group(args)
@ -110,9 +100,7 @@ class TestJSONFormatter(base.TestBase):
def test_formattablecolumn_list(self): def test_formattablecolumn_list(self):
sf = json_format.JSONFormatter() sf = json_format.JSONFormatter()
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
d = ( d = (('A1', 'B1', test_columns.FauxColumn(['the', 'value'])),)
('A1', 'B1', test_columns.FauxColumn(['the', 'value'])),
)
expected = [ expected = [
{'a': 'A1', 'b': 'B1', 'c': ['the', 'value']}, {'a': 'A1', 'b': 'B1', 'c': ['the', 'value']},
] ]

@ -22,7 +22,6 @@ from cliff.tests import test_columns
class TestShellFormatter(base.TestBase): class TestShellFormatter(base.TestBase):
def test(self): def test(self):
sf = shell.ShellFormatter() sf = shell.ShellFormatter()
c = ('a', 'b', 'c', 'd') c = ('a', 'b', 'c', 'd')
@ -54,11 +53,13 @@ class TestShellFormatter(base.TestBase):
sf = shell.ShellFormatter() sf = shell.ShellFormatter()
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
d = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) d = ('A', 'B', test_columns.FauxColumn(['the', 'value']))
expected = '\n'.join([ expected = '\n'.join(
'a="A"', [
'b="B"', 'a="A"',
'c="[\'the\', \'value\']"\n', 'b="B"',
]) 'c="[\'the\', \'value\']"\n',
]
)
output = io.StringIO() output = io.StringIO()
args = mock.Mock() args = mock.Mock()
args.variables = ['a', 'b', 'c'] args.variables = ['a', 'b', 'c']
@ -71,8 +72,9 @@ class TestShellFormatter(base.TestBase):
sf = shell.ShellFormatter() sf = shell.ShellFormatter()
c = ('a', 'b', 'c', 'd', 'e') c = ('a', 'b', 'c', 'd', 'e')
d = (True, False, 100, '"esc"', str('"esc"')) d = (True, False, 100, '"esc"', str('"esc"'))
expected = ('a="True"\nb="False"\nc="100"\n' expected = (
'd="\\"esc\\""\ne="\\"esc\\""\n') 'a="True"\nb="False"\nc="100"\n' 'd="\\"esc\\""\ne="\\"esc\\""\n'
)
output = io.StringIO() output = io.StringIO()
args = mock.Mock() args = mock.Mock()
args.variables = ['a', 'b', 'c', 'd', 'e'] args.variables = ['a', 'b', 'c', 'd', 'e']

@ -66,13 +66,13 @@ def _table_tester_helper(tags, data, extra_args=None):
class TestTableFormatter(base.TestBase): class TestTableFormatter(base.TestBase):
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
def test(self, tw): def test(self, tw):
tw.return_value = 80 tw.return_value = 80
c = ('a', 'b', 'c', 'd') c = ('a', 'b', 'c', 'd')
d = ('A', 'B', 'C', 'test\rcarriage\r\nreturn') d = ('A', 'B', 'C', 'test\rcarriage\r\nreturn')
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+-------+---------------+ +-------+---------------+
| Field | Value | | Field | Value |
+-------+---------------+ +-------+---------------+
@ -82,14 +82,15 @@ class TestTableFormatter(base.TestBase):
| d | test carriage | | d | test carriage |
| | return | | | return |
+-------+---------------+ +-------+---------------+
''') '''
)
self.assertEqual(expected, _table_tester_helper(c, d)) self.assertEqual(expected, _table_tester_helper(c, d))
class TestTerminalWidth(base.TestBase): class TestTerminalWidth(base.TestBase):
# Multi-line output when width is restricted to 42 columns # Multi-line output when width is restricted to 42 columns
expected_ml_val = textwrap.dedent('''\ expected_ml_val = textwrap.dedent(
'''\
+-------+--------------------------------+ +-------+--------------------------------+
| Field | Value | | Field | Value |
+-------+--------------------------------+ +-------+--------------------------------+
@ -100,10 +101,12 @@ class TestTerminalWidth(base.TestBase):
| | dddddddddddddddddddddddddddddd | | | dddddddddddddddddddddddddddddd |
| | ddddddddddddddddd | | | ddddddddddddddddd |
+-------+--------------------------------+ +-------+--------------------------------+
''') '''
)
# Multi-line output when width is restricted to 80 columns # Multi-line output when width is restricted to 80 columns
expected_ml_80_val = textwrap.dedent('''\ expected_ml_80_val = textwrap.dedent(
'''\
+-------+----------------------------------------------------------------------+ +-------+----------------------------------------------------------------------+
| Field | Value | | Field | Value |
+-------+----------------------------------------------------------------------+ +-------+----------------------------------------------------------------------+
@ -113,10 +116,12 @@ class TestTerminalWidth(base.TestBase):
| d | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | | d | dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd |
| | ddddddddd | | | ddddddddd |
+-------+----------------------------------------------------------------------+ +-------+----------------------------------------------------------------------+
''') # noqa '''
) # noqa
# Single-line output, for when no line length restriction apply # Single-line output, for when no line length restriction apply
expected_sl_val = textwrap.dedent('''\ expected_sl_val = textwrap.dedent(
'''\
+-------+-------------------------------------------------------------------------------+ +-------+-------------------------------------------------------------------------------+
| Field | Value | | Field | Value |
+-------+-------------------------------------------------------------------------------+ +-------+-------------------------------------------------------------------------------+
@ -125,7 +130,8 @@ class TestTerminalWidth(base.TestBase):
| c | C | | c | C |
| d | ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd | | d | ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd |
+-------+-------------------------------------------------------------------------------+ +-------+-------------------------------------------------------------------------------+
''') # noqa '''
) # noqa
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
def test_table_formatter_no_cli_param(self, tw): def test_table_formatter_no_cli_param(self, tw):
@ -192,15 +198,16 @@ class TestTerminalWidth(base.TestBase):
class TestMaxWidth(base.TestBase): class TestMaxWidth(base.TestBase):
expected_80 = textwrap.dedent(
expected_80 = textwrap.dedent('''\ '''\
+--------------------------+---------------------------------------------+ +--------------------------+---------------------------------------------+
| Field | Value | | Field | Value |
+--------------------------+---------------------------------------------+ +--------------------------+---------------------------------------------+
| field_name | the value | | field_name | the value |
| a_really_long_field_name | a value significantly longer than the field | | a_really_long_field_name | a value significantly longer than the field |
+--------------------------+---------------------------------------------+ +--------------------------+---------------------------------------------+
''') '''
)
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
def test_80(self, tw): def test_80(self, tw):
@ -215,7 +222,8 @@ class TestMaxWidth(base.TestBase):
tw.return_value = 70 tw.return_value = 70
c = ('field_name', 'a_really_long_field_name') c = ('field_name', 'a_really_long_field_name')
d = ('the value', 'a value significantly longer than the field') d = ('the value', 'a value significantly longer than the field')
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+--------------------------+-----------------------------------------+ +--------------------------+-----------------------------------------+
| Field | Value | | Field | Value |
+--------------------------+-----------------------------------------+ +--------------------------+-----------------------------------------+
@ -223,7 +231,8 @@ class TestMaxWidth(base.TestBase):
| a_really_long_field_name | a value significantly longer than the | | a_really_long_field_name | a value significantly longer than the |
| | field | | | field |
+--------------------------+-----------------------------------------+ +--------------------------+-----------------------------------------+
''') '''
)
self.assertEqual( self.assertEqual(
expected, expected,
_table_tester_helper(c, d, extra_args=['--fit-width']), _table_tester_helper(c, d, extra_args=['--fit-width']),
@ -235,7 +244,8 @@ class TestMaxWidth(base.TestBase):
tw.return_value = 50 tw.return_value = 50
c = ('field_name', 'a_really_long_field_name') c = ('field_name', 'a_really_long_field_name')
d = ('the value', 'a value significantly longer than the field') d = ('the value', 'a value significantly longer than the field')
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+-----------------------+------------------------+ +-----------------------+------------------------+
| Field | Value | | Field | Value |
+-----------------------+------------------------+ +-----------------------+------------------------+
@ -243,7 +253,8 @@ class TestMaxWidth(base.TestBase):
| a_really_long_field_n | a value significantly | | a_really_long_field_n | a value significantly |
| ame | longer than the field | | ame | longer than the field |
+-----------------------+------------------------+ +-----------------------+------------------------+
''') '''
)
self.assertEqual( self.assertEqual(
expected, expected,
_table_tester_helper(c, d, extra_args=['--fit-width']), _table_tester_helper(c, d, extra_args=['--fit-width']),
@ -255,7 +266,8 @@ class TestMaxWidth(base.TestBase):
tw.return_value = 10 tw.return_value = 10
c = ('field_name', 'a_really_long_field_name') c = ('field_name', 'a_really_long_field_name')
d = ('the value', 'a value significantly longer than the field') d = ('the value', 'a value significantly longer than the field')
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+------------------+------------------+ +------------------+------------------+
| Field | Value | | Field | Value |
+------------------+------------------+ +------------------+------------------+
@ -265,7 +277,8 @@ class TestMaxWidth(base.TestBase):
| | longer than the | | | longer than the |
| | field | | | field |
+------------------+------------------+ +------------------+------------------+
''') '''
)
self.assertEqual( self.assertEqual(
expected, expected,
_table_tester_helper(c, d, extra_args=['--fit-width']), _table_tester_helper(c, d, extra_args=['--fit-width']),
@ -273,50 +286,51 @@ class TestMaxWidth(base.TestBase):
class TestListFormatter(base.TestBase): class TestListFormatter(base.TestBase):
_col_names = ('one', 'two', 'three') _col_names = ('one', 'two', 'three')
_col_data = [( _col_data = [('one one one one one', 'two two two two', 'three three')]
'one one one one one',
'two two two two',
'three three')]
_expected_mv = { _expected_mv = {
80: textwrap.dedent('''\ 80: textwrap.dedent(
'''\
+---------------------+-----------------+-------------+ +---------------------+-----------------+-------------+
| one | two | three | | one | two | three |
+---------------------+-----------------+-------------+ +---------------------+-----------------+-------------+
| one one one one one | two two two two | three three | | one one one one one | two two two two | three three |
+---------------------+-----------------+-------------+ +---------------------+-----------------+-------------+
'''), '''
),
50: textwrap.dedent('''\ 50: textwrap.dedent(
'''\
+----------------+-----------------+-------------+ +----------------+-----------------+-------------+
| one | two | three | | one | two | three |
+----------------+-----------------+-------------+ +----------------+-----------------+-------------+
| one one one | two two two two | three three | | one one one | two two two two | three three |
| one one | | | | one one | | |
+----------------+-----------------+-------------+ +----------------+-----------------+-------------+
'''), '''
),
47: textwrap.dedent('''\ 47: textwrap.dedent(
'''\
+---------------+---------------+-------------+ +---------------+---------------+-------------+
| one | two | three | | one | two | three |
+---------------+---------------+-------------+ +---------------+---------------+-------------+
| one one one | two two two | three three | | one one one | two two two | three three |
| one one | two | | | one one | two | |
+---------------+---------------+-------------+ +---------------+---------------+-------------+
'''), '''
),
45: textwrap.dedent('''\ 45: textwrap.dedent(
'''\
+--------------+--------------+-------------+ +--------------+--------------+-------------+
| one | two | three | | one | two | three |
+--------------+--------------+-------------+ +--------------+--------------+-------------+
| one one one | two two two | three three | | one one one | two two two | three three |
| one one | two | | | one one | two | |
+--------------+--------------+-------------+ +--------------+--------------+-------------+
'''), '''
),
40: textwrap.dedent('''\ 40: textwrap.dedent(
'''\
+------------+------------+------------+ +------------+------------+------------+
| one | two | three | | one | two | three |
+------------+------------+------------+ +------------+------------+------------+
@ -324,9 +338,10 @@ class TestListFormatter(base.TestBase):
| one one | two two | three | | one one | two two | three |
| one | | | | one | | |
+------------+------------+------------+ +------------+------------+------------+
'''), '''
),
10: textwrap.dedent('''\ 10: textwrap.dedent(
'''\
+----------+----------+----------+ +----------+----------+----------+
| one | two | three | | one | two | three |
+----------+----------+----------+ +----------+----------+----------+
@ -334,7 +349,8 @@ class TestListFormatter(base.TestBase):
| one one | two two | three | | one one | two two | three |
| one | | | | one | | |
+----------+----------+----------+ +----------+----------+----------+
'''), '''
),
} }
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
@ -344,7 +360,8 @@ class TestListFormatter(base.TestBase):
d1 = ('A', 'B', 'C') d1 = ('A', 'B', 'C')
d2 = ('D', 'E', 'test\rcarriage\r\nreturn') d2 = ('D', 'E', 'test\rcarriage\r\nreturn')
data = [d1, d2] data = [d1, d2]
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+---+---+---------------+ +---+---+---------------+
| a | b | c | | a | b | c |
+---+---+---------------+ +---+---+---------------+
@ -352,7 +369,8 @@ class TestListFormatter(base.TestBase):
| D | E | test carriage | | D | E | test carriage |
| | | return | | | | return |
+---+---+---------------+ +---+---+---------------+
''') '''
)
self.assertEqual(expected, _table_tester_helper(c, data)) self.assertEqual(expected, _table_tester_helper(c, data))
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
@ -360,7 +378,8 @@ class TestListFormatter(base.TestBase):
tw.return_value = 0 tw.return_value = 0
c = ('a', 'b', 'c', 'd') c = ('a', 'b', 'c', 'd')
d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value'])) d = ('A', 'B', 'C', test_columns.FauxColumn(['the', 'value']))
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+-------+---------------------------------------------+ +-------+---------------------------------------------+
| Field | Value | | Field | Value |
+-------+---------------------------------------------+ +-------+---------------------------------------------+
@ -369,7 +388,8 @@ class TestListFormatter(base.TestBase):
| c | C | | c | C |
| d | I made this string myself: ['the', 'value'] | | d | I made this string myself: ['the', 'value'] |
+-------+---------------------------------------------+ +-------+---------------------------------------------+
''') '''
)
self.assertEqual(expected, _table_tester_helper(c, d)) self.assertEqual(expected, _table_tester_helper(c, d))
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
@ -378,13 +398,15 @@ class TestListFormatter(base.TestBase):
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value'])) d1 = ('A', 'B', test_columns.FauxColumn(['the', 'value']))
data = [d1] data = [d1]
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+---+---+---------------------------------------------+ +---+---+---------------------------------------------+
| a | b | c | | a | b | c |
+---+---+---------------------------------------------+ +---+---+---------------------------------------------+
| A | B | I made this string myself: ['the', 'value'] | | A | B | I made this string myself: ['the', 'value'] |
+---+---+---------------------------------------------+ +---+---+---------------------------------------------+
''') '''
)
self.assertEqual(expected, _table_tester_helper(c, data)) self.assertEqual(expected, _table_tester_helper(c, data))
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
@ -400,8 +422,9 @@ class TestListFormatter(base.TestBase):
def test_max_width_50(self, tw): def test_max_width_50(self, tw):
# resize 1 column # resize 1 column
width = tw.return_value = 50 width = tw.return_value = 50
actual = _table_tester_helper(self._col_names, self._col_data, actual = _table_tester_helper(
extra_args=['--fit-width']) self._col_names, self._col_data, extra_args=['--fit-width']
)
self.assertEqual(self._expected_mv[width], actual) self.assertEqual(self._expected_mv[width], actual)
self.assertEqual(width, len(actual.splitlines()[0])) self.assertEqual(width, len(actual.splitlines()[0]))
@ -409,8 +432,9 @@ class TestListFormatter(base.TestBase):
def test_max_width_45(self, tw): def test_max_width_45(self, tw):
# resize 2 columns # resize 2 columns
width = tw.return_value = 45 width = tw.return_value = 45
actual = _table_tester_helper(self._col_names, self._col_data, actual = _table_tester_helper(
extra_args=['--fit-width']) self._col_names, self._col_data, extra_args=['--fit-width']
)
self.assertEqual(self._expected_mv[width], actual) self.assertEqual(self._expected_mv[width], actual)
self.assertEqual(width, len(actual.splitlines()[0])) self.assertEqual(width, len(actual.splitlines()[0]))
@ -418,8 +442,9 @@ class TestListFormatter(base.TestBase):
def test_max_width_40(self, tw): def test_max_width_40(self, tw):
# resize all columns # resize all columns
width = tw.return_value = 40 width = tw.return_value = 40
actual = _table_tester_helper(self._col_names, self._col_data, actual = _table_tester_helper(
extra_args=['--fit-width']) self._col_names, self._col_data, extra_args=['--fit-width']
)
self.assertEqual(self._expected_mv[width], actual) self.assertEqual(self._expected_mv[width], actual)
self.assertEqual(width, len(actual.splitlines()[0])) self.assertEqual(width, len(actual.splitlines()[0]))
@ -427,8 +452,9 @@ class TestListFormatter(base.TestBase):
def test_max_width_10(self, tw): def test_max_width_10(self, tw):
# resize all columns limited by min_width=8 # resize all columns limited by min_width=8
width = tw.return_value = 10 width = tw.return_value = 10
actual = _table_tester_helper(self._col_names, self._col_data, actual = _table_tester_helper(
extra_args=['--fit-width']) self._col_names, self._col_data, extra_args=['--fit-width']
)
self.assertEqual(self._expected_mv[width], actual) self.assertEqual(self._expected_mv[width], actual)
# 3 columns each 8 wide, plus table spacing and borders # 3 columns each 8 wide, plus table spacing and borders
expected_width = 11 * 3 + 1 expected_width = 11 * 3 + 1
@ -550,16 +576,18 @@ class TestListFormatter(base.TestBase):
def test_env_maxwidth_args_big(self): def test_env_maxwidth_args_big(self):
self.assertEqual( self.assertEqual(
self._expected_mv[80], self._expected_mv[80],
_table_tester_helper(self._col_names, self._col_data, _table_tester_helper(
extra_args=args(666)), self._col_names, self._col_data, extra_args=args(666)
),
) )
@mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '42'}) @mock.patch.dict(os.environ, {'CLIFF_MAX_TERM_WIDTH': '42'})
def test_env_maxwidth_args_tiny(self): def test_env_maxwidth_args_tiny(self):
self.assertEqual( self.assertEqual(
self._expected_mv[40], self._expected_mv[40],
_table_tester_helper(self._col_names, self._col_data, _table_tester_helper(
extra_args=args(40)), self._col_names, self._col_data, extra_args=args(40)
),
) )
@mock.patch('cliff.utils.terminal_width') @mock.patch('cliff.utils.terminal_width')
@ -575,46 +603,35 @@ class TestListFormatter(base.TestBase):
tw.return_value = 80 tw.return_value = 80
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
data = [] data = []
expected = textwrap.dedent('''\ expected = textwrap.dedent(
'''\
+---+---+---+ +---+---+---+
| a | b | c | | a | b | c |
+---+---+---+ +---+---+---+
+---+---+---+ +---+---+---+
''') '''
)
self.assertEqual( self.assertEqual(
expected, expected,
_table_tester_helper(c, data, _table_tester_helper(c, data, extra_args=['--print-empty']),
extra_args=['--print-empty']),
) )
class TestFieldWidths(base.TestBase): class TestFieldWidths(base.TestBase):
def test(self): def test(self):
tf = table.TableFormatter tf = table.TableFormatter
self.assertEqual( self.assertEqual(
{ {'a': 1, 'b': 2, 'c': 3, 'd': 10},
'a': 1,
'b': 2,
'c': 3,
'd': 10
},
tf._field_widths( tf._field_widths(
('a', 'b', 'c', 'd'), ('a', 'b', 'c', 'd'), '+---+----+-----+------------+'
'+---+----+-----+------------+'), ),
) )
def test_zero(self): def test_zero(self):
tf = table.TableFormatter tf = table.TableFormatter
self.assertEqual( self.assertEqual(
{ {'a': 0, 'b': 0, 'c': 0},
'a': 0, tf._field_widths(('a', 'b', 'c'), '+--+-++'),
'b': 0,
'c': 0
},
tf._field_widths(
('a', 'b', 'c'),
'+--+-++'),
) )
def test_info(self): def test_info(self):

@ -20,7 +20,6 @@ from cliff.tests import test_columns
class TestValueFormatter(base.TestBase): class TestValueFormatter(base.TestBase):
def test(self): def test(self):
sf = value.ValueFormatter() sf = value.ValueFormatter()
c = ('a', 'b', 'c', 'd') c = ('a', 'b', 'c', 'd')

@ -23,7 +23,6 @@ from cliff.tests import test_columns
class _toDict: class _toDict:
def __init__(self, **kwargs): def __init__(self, **kwargs):
self._data = kwargs self._data = kwargs
@ -40,17 +39,11 @@ class _to_Dict:
class TestYAMLFormatter(base.TestBase): class TestYAMLFormatter(base.TestBase):
def test_format_one(self): def test_format_one(self):
sf = yaml_format.YAMLFormatter() sf = yaml_format.YAMLFormatter()
c = ('a', 'b', 'c', 'd') c = ('a', 'b', 'c', 'd')
d = ('A', 'B', 'C', '"escape me"') d = ('A', 'B', 'C', '"escape me"')
expected = { expected = {'a': 'A', 'b': 'B', 'c': 'C', 'd': '"escape me"'}
'a': 'A',
'b': 'B',
'c': 'C',
'd': '"escape me"'
}
output = StringIO() output = StringIO()
args = mock.Mock() args = mock.Mock()
sf.emit_one(c, d, output, args) sf.emit_one(c, d, output, args)
@ -81,15 +74,11 @@ class TestYAMLFormatter(base.TestBase):
def test_list(self): def test_list(self):
sf = yaml_format.YAMLFormatter() sf = yaml_format.YAMLFormatter()
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
d = ( d = (('A1', 'B1', 'C1'), ('A2', 'B2', 'C2'), ('A3', 'B3', 'C3'))
('A1', 'B1', 'C1'),
('A2', 'B2', 'C2'),
('A3', 'B3', 'C3')
)
expected = [ expected = [
{'a': 'A1', 'b': 'B1', 'c': 'C1'}, {'a': 'A1', 'b': 'B1', 'c': 'C1'},
{'a': 'A2', 'b': 'B2', 'c': 'C2'}, {'a': 'A2', 'b': 'B2', 'c': 'C2'},
{'a': 'A3', 'b': 'B3', 'c': 'C3'} {'a': 'A3', 'b': 'B3', 'c': 'C3'},
] ]
output = StringIO() output = StringIO()
args = mock.Mock() args = mock.Mock()
@ -101,9 +90,7 @@ class TestYAMLFormatter(base.TestBase):
def test_formattablecolumn_list(self): def test_formattablecolumn_list(self):
sf = yaml_format.YAMLFormatter() sf = yaml_format.YAMLFormatter()
c = ('a', 'b', 'c') c = ('a', 'b', 'c')
d = ( d = (('A1', 'B1', test_columns.FauxColumn(['the', 'value'])),)
('A1', 'B1', test_columns.FauxColumn(['the', 'value'])),
)
expected = [ expected = [
{'a': 'A1', 'b': 'B1', 'c': ['the', 'value']}, {'a': 'A1', 'b': 'B1', 'c': ['the', 'value']},
] ]
@ -124,7 +111,7 @@ class TestYAMLFormatter(base.TestBase):
'a': 'A', 'a': 'A',
'b': 'B', 'b': 'B',
'toDict': {"spam": "ham"}, 'toDict': {"spam": "ham"},
'to_dict': {"ham": "eggs"} 'to_dict': {"ham": "eggs"},
} }
output = StringIO() output = StringIO()
args = mock.Mock() args = mock.Mock()
@ -138,12 +125,12 @@ class TestYAMLFormatter(base.TestBase):
d = ( d = (
('A1', _toDict(B=1), _to_Dict(C=1)), ('A1', _toDict(B=1), _to_Dict(C=1)),
('A2', _toDict(B=2), _to_Dict(C=2)), ('A2', _toDict(B=2), _to_Dict(C=2)),
('A3', _toDict(B=3), _to_Dict(C=3)) ('A3', _toDict(B=3), _to_Dict(C=3)),
) )
expected = [ expected = [
{'a': 'A1', 'toDict': {'B': 1}, 'to_dict': {'C': 1}}, {'a': 'A1', 'toDict': {'B': 1}, 'to_dict': {'C': 1}},
{'a': 'A2', 'toDict': {'B': 2}, 'to_dict': {'C': 2}}, {'a': 'A2', 'toDict': {'B': 2}, 'to_dict': {'C': 2}},
{'a': 'A3', 'toDict': {'B': 3}, 'to_dict': {'C': 3}} {'a': 'A3', 'toDict': {'B': 3}, 'to_dict': {'C': 3}},
] ]
output = StringIO() output = StringIO()
args = mock.Mock() args = mock.Mock()

@ -24,15 +24,17 @@ from cliff.tests import utils
class TestHelp(base.TestBase): class TestHelp(base.TestBase):
def test_show_help_for_command(self): def test_show_help_for_command(self):
# FIXME(dhellmann): Are commands tied too closely to the app? Or # 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 # do commands know too much about apps by using them to get to the
# command manager? # command manager?
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
help_cmd = help.HelpCommand(app, mock.Mock()) help_cmd = help.HelpCommand(app, mock.Mock())
parser = help_cmd.get_parser('test') parser = help_cmd.get_parser('test')
@ -48,9 +50,12 @@ class TestHelp(base.TestBase):
# do commands know too much about apps by using them to get to the # do commands know too much about apps by using them to get to the
# command manager? # command manager?
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
help_cmd = help.HelpCommand(app, mock.Mock()) help_cmd = help.HelpCommand(app, mock.Mock())
parser = help_cmd.get_parser('test') parser = help_cmd.get_parser('test')
@ -68,9 +73,12 @@ class TestHelp(base.TestBase):
# do commands know too much about apps by using them to get to the # do commands know too much about apps by using them to get to the
# command manager? # command manager?
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
help_cmd = help.HelpCommand(app, mock.Mock()) help_cmd = help.HelpCommand(app, mock.Mock())
parser = help_cmd.get_parser('test') parser = help_cmd.get_parser('test')
@ -86,9 +94,12 @@ class TestHelp(base.TestBase):
# do commands know too much about apps by using them to get to the # do commands know too much about apps by using them to get to the
# command manager? # command manager?
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
app.options = mock.Mock() app.options = mock.Mock()
help_cmd = help.HelpCommand(app, mock.Mock()) help_cmd = help.HelpCommand(app, mock.Mock())
@ -113,9 +124,12 @@ class TestHelp(base.TestBase):
# do commands know too much about apps by using them to get to the # do commands know too much about apps by using them to get to the
# command manager? # command manager?
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
try: try:
app.run(['--help']) app.run(['--help'])
@ -126,13 +140,19 @@ class TestHelp(base.TestBase):
self.assertIn('three word command', help_output) self.assertIn('three word command', help_output)
self.assertNotIn('old cmd', help_output) self.assertNotIn('old cmd', help_output)
@mock.patch.object(commandmanager.EntryPointWrapper, 'load', @mock.patch.object(
side_effect=Exception('Could not load EntryPoint')) commandmanager.EntryPointWrapper,
'load',
side_effect=Exception('Could not load EntryPoint'),
)
def test_show_help_with_ep_load_fail(self, mock_load): def test_show_help_with_ep_load_fail(self, mock_load):
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
app.options = mock.Mock() app.options = mock.Mock()
app.options.debug = False app.options.debug = False
@ -148,13 +168,19 @@ class TestHelp(base.TestBase):
self.assertIn('Could not load', help_output) self.assertIn('Could not load', help_output)
self.assertNotIn('Exception: Could not load EntryPoint', help_output) self.assertNotIn('Exception: Could not load EntryPoint', help_output)
@mock.patch.object(commandmanager.EntryPointWrapper, 'load', @mock.patch.object(
side_effect=Exception('Could not load EntryPoint')) commandmanager.EntryPointWrapper,
'load',
side_effect=Exception('Could not load EntryPoint'),
)
def test_show_help_print_exc_with_ep_load_fail(self, mock_load): def test_show_help_print_exc_with_ep_load_fail(self, mock_load):
stdout = io.StringIO() stdout = io.StringIO()
app = application.App('testing', '1', app = application.App(
utils.TestCommandManager(utils.TEST_NAMESPACE), 'testing',
stdout=stdout) '1',
utils.TestCommandManager(utils.TEST_NAMESPACE),
stdout=stdout,
)
app.NAME = 'test' app.NAME = 'test'
app.options = mock.Mock() app.options = mock.Mock()
app.options.debug = True app.options.debug = True

@ -23,16 +23,21 @@ class FakeApp(object):
class TestInteractive(base.TestBase): class TestInteractive(base.TestBase):
def make_interactive_app(self, errexit, *command_names): def make_interactive_app(self, errexit, *command_names):
fake_command_manager = [(x, None) for x in command_names] fake_command_manager = [(x, None) for x in command_names]
return InteractiveApp(FakeApp, fake_command_manager, return InteractiveApp(
stdin=None, stdout=None, errexit=errexit) FakeApp,
fake_command_manager,
stdin=None,
stdout=None,
errexit=errexit,
)
def _test_completenames(self, expecteds, prefix): def _test_completenames(self, expecteds, prefix):
app = self.make_interactive_app(False, 'hips', 'hippo', 'nonmatching') app = self.make_interactive_app(False, 'hips', 'hippo', 'nonmatching')
self.assertEqual( self.assertEqual(
set(app.completenames(prefix, '', 0, 1)), set(expecteds)) set(app.completenames(prefix, '', 0, 1)), set(expecteds)
)
def test_cmd2_completenames(self): def test_cmd2_completenames(self):
# cmd2.Cmd define do_help method # cmd2.Cmd define do_help method
@ -56,8 +61,9 @@ class TestInteractive(base.TestBase):
self._test_completenames(['history', 'hips', 'hippo'], 'hi') self._test_completenames(['history', 'hips', 'hippo'], 'hi')
def _test_completedefault(self, expecteds, line, begidx): def _test_completedefault(self, expecteds, line, begidx):
command_names = set(['show file', 'show folder', 'show long', command_names = set(
'list all']) ['show file', 'show folder', 'show long', 'list all']
)
app = self.make_interactive_app(False, *command_names) app = self.make_interactive_app(False, *command_names)
observeds = app.completedefault(None, line, begidx, None) observeds = app.completedefault(None, line, begidx, None)
self.assertEqual(set(expecteds), set(observeds)) self.assertEqual(set(expecteds), set(observeds))

@ -21,7 +21,6 @@ from cliff.tests import base
class FauxFormatter(object): class FauxFormatter(object):
def __init__(self): def __init__(self):
self.args = [] self.args = []
self.obj = weakref.proxy(self) self.obj = weakref.proxy(self)
@ -31,7 +30,6 @@ class FauxFormatter(object):
class ExerciseLister(lister.Lister): class ExerciseLister(lister.Lister):
data = [('a', 'A'), ('b', 'B'), ('c', 'A')] data = [('a', 'A'), ('b', 'B'), ('c', 'A')]
def _load_formatter_plugins(self): def _load_formatter_plugins(self):
@ -44,22 +42,18 @@ class ExerciseLister(lister.Lister):
class ExerciseListerCustomSort(ExerciseLister): class ExerciseListerCustomSort(ExerciseLister):
need_sort_by_cliff = False need_sort_by_cliff = False
class ExerciseListerNullValues(ExerciseLister): class ExerciseListerNullValues(ExerciseLister):
data = ExerciseLister.data + [(None, None)] data = ExerciseLister.data + [(None, None)]
class ExerciseListerDifferentTypes(ExerciseLister): class ExerciseListerDifferentTypes(ExerciseLister):
data = ExerciseLister.data + [(1, 0)] data = ExerciseLister.data + [(1, 0)]
class TestLister(base.TestBase): class TestLister(base.TestBase):
def test_formatter_args(self): def test_formatter_args(self):
app = mock.Mock() app = mock.Mock()
test_lister = ExerciseLister(app, []) test_lister = ExerciseLister(app, [])
@ -148,7 +142,8 @@ class TestLister(base.TestBase):
args = f.args[0] args = f.args[0]
data = list(args[1]) data = list(args[1])
self.assertEqual( self.assertEqual(
[['a', 'A'], ['c', 'A'], ['b', 'B'], [None, None]], data) [['a', 'A'], ['c', 'A'], ['b', 'B'], [None, None]], data
)
def test_sort_by_column_with_different_types(self): def test_sort_by_column_with_different_types(self):
test_lister = ExerciseListerDifferentTypes(mock.Mock(), []) test_lister = ExerciseListerDifferentTypes(mock.Mock(), [])
@ -164,13 +159,16 @@ class TestLister(base.TestBase):
args = f.args[0] args = f.args[0]
data = list(args[1]) data = list(args[1])
# The output should be unchanged # The output should be unchanged
self.assertEqual( self.assertEqual([['a', 'A'], ['b', 'B'], ['c', 'A'], [1, 0]], data)
[['a', 'A'], ['b', 'B'], ['c', 'A'], [1, 0]], data)
# but we should have logged a warning # but we should have logged a warning
mock_log.warning.assert_has_calls([ mock_log.warning.assert_has_calls(
mock.call("Could not sort on field '%s'; unsortable types", col) [
for col in parsed_args.sort_columns mock.call(
]) "Could not sort on field '%s'; unsortable types", col
)
for col in parsed_args.sort_columns
]
)
def test_sort_by_non_displayed_column(self): def test_sort_by_non_displayed_column(self):
test_lister = ExerciseLister(mock.Mock(), []) test_lister = ExerciseLister(mock.Mock(), [])
@ -181,7 +179,8 @@ class TestLister(base.TestBase):
with mock.patch.object(test_lister, 'take_action') as mock_take_action: with mock.patch.object(test_lister, 'take_action') as mock_take_action:
mock_take_action.return_value = ( mock_take_action.return_value = (
('Col1', 'Col2'), [['a', 'A'], ['b', 'B'], ['c', 'A']] ('Col1', 'Col2'),
[['a', 'A'], ['b', 'B'], ['c', 'A']],
) )
test_lister.run(parsed_args) test_lister.run(parsed_args)

@ -21,7 +21,6 @@ from cliff.tests import base
class FauxFormatter(object): class FauxFormatter(object):
def __init__(self): def __init__(self):
self.args = [] self.args = []
self.obj = weakref.proxy(self) self.obj = weakref.proxy(self)
@ -31,7 +30,6 @@ class FauxFormatter(object):
class ExerciseShowOne(show.ShowOne): class ExerciseShowOne(show.ShowOne):
def _load_formatter_plugins(self): def _load_formatter_plugins(self):
return { return {
'test': FauxFormatter(), 'test': FauxFormatter(),
@ -45,7 +43,6 @@ class ExerciseShowOne(show.ShowOne):
class TestShow(base.TestBase): class TestShow(base.TestBase):
def test_formatter_args(self): def test_formatter_args(self):
app = mock.Mock() app = mock.Mock()
test_show = ExerciseShowOne(app, []) test_show = ExerciseShowOne(app, [])

@ -20,7 +20,6 @@ from cliff.tests import base
class TestSphinxExtension(base.TestBase): class TestSphinxExtension(base.TestBase):
def test_empty_help(self): def test_empty_help(self):
"""Handle positional and optional actions without help messages.""" """Handle positional and optional actions without help messages."""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False) parser = argparse.ArgumentParser(prog='hello-world', add_help=False)
@ -28,7 +27,9 @@ class TestSphinxExtension(base.TestBase):
parser.add_argument('--language', dest='lang') parser.add_argument('--language', dest='lang')
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -37,17 +38,23 @@ class TestSphinxExtension(base.TestBase):
.. option:: --language <LANG> .. option:: --language <LANG>
.. option:: name .. option:: name
""").lstrip(), output) """
).lstrip(),
output,
)
def test_nonempty_help(self): def test_nonempty_help(self):
"""Handle positional and optional actions with help messages.""" """Handle positional and optional actions with help messages."""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False) parser = argparse.ArgumentParser(prog='hello-world', add_help=False)
parser.add_argument('name', help='user name') parser.add_argument('name', help='user name')
parser.add_argument('--language', dest='lang', parser.add_argument(
help='greeting language') '--language', dest='lang', help='greeting language'
)
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -60,18 +67,26 @@ class TestSphinxExtension(base.TestBase):
.. option:: name .. option:: name
user name user name
""").lstrip(), output) """
).lstrip(),
output,
)
def test_description_epilog(self): def test_description_epilog(self):
"""Handle a parser description, epilog.""" """Handle a parser description, epilog."""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False, parser = argparse.ArgumentParser(
description='A "Hello, World" app.', prog='hello-world',
epilog='What am I doing down here?') add_help=False,
description='A "Hello, World" app.',
epilog='What am I doing down here?',
)
parser.add_argument('name', action='store') parser.add_argument('name', action='store')
parser.add_argument('--language', dest='lang') parser.add_argument('--language', dest='lang')
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
A "Hello, World" app. A "Hello, World" app.
.. program:: hello-world .. program:: hello-world
@ -84,17 +99,25 @@ class TestSphinxExtension(base.TestBase):
.. option:: name .. option:: name
What am I doing down here? What am I doing down here?
""").lstrip(), output) """
).lstrip(),
output,
)
def test_flag(self): def test_flag(self):
"""Handle a boolean argparse action.""" """Handle a boolean argparse action."""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False) parser = argparse.ArgumentParser(prog='hello-world', add_help=False)
parser.add_argument('name', help='user name') parser.add_argument('name', help='user name')
parser.add_argument('--translate', action='store_true', parser.add_argument(
help='translate to local language') '--translate',
action='store_true',
help='translate to local language',
)
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -107,7 +130,10 @@ class TestSphinxExtension(base.TestBase):
.. option:: name .. option:: name
user name user name
""").lstrip(), output) """
).lstrip(),
output,
)
def test_supressed(self): def test_supressed(self):
"""Handle a supressed action.""" """Handle a supressed action."""
@ -116,7 +142,9 @@ class TestSphinxExtension(base.TestBase):
parser.add_argument('--variable', help=argparse.SUPPRESS) parser.add_argument('--variable', help=argparse.SUPPRESS)
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -126,16 +154,22 @@ class TestSphinxExtension(base.TestBase):
.. option:: name .. option:: name
user name user name
""").lstrip(), output) """
).lstrip(),
output,
)
def test_metavar(self): def test_metavar(self):
"""Handle an option with a metavar.""" """Handle an option with a metavar."""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False) parser = argparse.ArgumentParser(prog='hello-world', add_help=False)
parser.add_argument('names', metavar='<NAME>', nargs='+', parser.add_argument(
help='a user name') 'names', metavar='<NAME>', nargs='+', help='a user name'
)
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -144,29 +178,46 @@ class TestSphinxExtension(base.TestBase):
.. option:: NAME .. option:: NAME
a user name a user name
""").lstrip(), output) """
).lstrip(),
output,
)
def test_multiple_opts(self): def test_multiple_opts(self):
"""Correctly output multiple opts on separate lines.""" """Correctly output multiple opts on separate lines."""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False) parser = argparse.ArgumentParser(prog='hello-world', add_help=False)
parser.add_argument('name', help='user name') parser.add_argument('name', help='user name')
parser.add_argument('--language', dest='lang', parser.add_argument(
help='greeting language') '--language', dest='lang', help='greeting language'
parser.add_argument('--translate', action='store_true', )
help='translate to local language') parser.add_argument(
parser.add_argument('--write-to-var-log-something-or-other', '--translate',
action='store_true', action='store_true',
help='a long opt to force wrapping') help='translate to local language',
parser.add_argument('--required-arg', dest='stuff', required=True, )
help='a required argument') parser.add_argument(
'--write-to-var-log-something-or-other',
action='store_true',
help='a long opt to force wrapping',
)
parser.add_argument(
'--required-arg',
dest='stuff',
required=True,
help='a required argument',
)
style_group = parser.add_mutually_exclusive_group(required=True) style_group = parser.add_mutually_exclusive_group(required=True)
style_group.add_argument('--polite', action='store_true', style_group.add_argument(
help='use a polite greeting') '--polite', action='store_true', help='use a polite greeting'
style_group.add_argument('--profane', action='store_true', )
help='use a less polite greeting') style_group.add_argument(
'--profane', action='store_true', help='use a less polite greeting'
)
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -205,26 +256,36 @@ class TestSphinxExtension(base.TestBase):
.. option:: name .. option:: name
user name user name
""").lstrip(), output) """
).lstrip(),
output,
)
def test_various_option_names_with_hyphen(self): def test_various_option_names_with_hyphen(self):
"""Handle options whose name and/or metavar contain hyphen(s)""" """Handle options whose name and/or metavar contain hyphen(s)"""
parser = argparse.ArgumentParser(prog='hello-world', add_help=False) parser = argparse.ArgumentParser(prog='hello-world', add_help=False)
parser.add_argument('--foo-bar', metavar='<foo-bar>', parser.add_argument(
help='foo bar', required=True) '--foo-bar', metavar='<foo-bar>', help='foo bar', required=True
parser.add_argument('--foo-bar-baz', metavar='<foo-bar-baz>', )
help='foo bar baz', required=True) parser.add_argument(
parser.add_argument('--foo', metavar='<foo>', '--foo-bar-baz',
help='foo', required=True) metavar='<foo-bar-baz>',
parser.add_argument('--alpha', metavar='<A>', help='foo bar baz',
help='alpha') required=True,
parser.add_argument('--alpha-beta', metavar='<A-B>', )
help='alpha beta') parser.add_argument(
parser.add_argument('--alpha-beta-gamma', metavar='<A-B-C>', '--foo', metavar='<foo>', help='foo', required=True
help='alpha beta gamma') )
parser.add_argument('--alpha', metavar='<A>', help='alpha')
parser.add_argument('--alpha-beta', metavar='<A-B>', help='alpha beta')
parser.add_argument(
'--alpha-beta-gamma', metavar='<A-B-C>', help='alpha beta gamma'
)
output = '\n'.join(sphinxext._format_parser(parser)) output = '\n'.join(sphinxext._format_parser(parser))
self.assertEqual(textwrap.dedent(""" self.assertEqual(
textwrap.dedent(
"""
.. program:: hello-world .. program:: hello-world
.. code-block:: shell .. code-block:: shell
@ -259,4 +320,7 @@ class TestSphinxExtension(base.TestBase):
.. option:: --alpha-beta-gamma <A-B-C> .. option:: --alpha-beta-gamma <A-B-C>
alpha beta gamma alpha beta gamma
""").lstrip(), output) """
).lstrip(),
output,
)

@ -20,7 +20,6 @@ from cliff import utils
class TestTerminalWidth(base.TestBase): class TestTerminalWidth(base.TestBase):
def test(self): def test(self):
width = utils.terminal_width() width = utils.terminal_width()
# Results are specific to the execution environment, so only assert # Results are specific to the execution environment, so only assert

@ -17,7 +17,6 @@ TEST_NAMESPACE = 'cliff.test'
class TestParser(object): class TestParser(object):
def print_help(self, stdout): def print_help(self, stdout):
stdout.write('TestParser') stdout.write('TestParser')
@ -35,12 +34,10 @@ class TestCommand(Command):
class TestDeprecatedCommand(TestCommand): class TestDeprecatedCommand(TestCommand):
deprecated = True deprecated = True
class TestCommandManager(CommandManager): class TestCommandManager(CommandManager):
def load_commands(self, namespace): def load_commands(self, namespace):
if namespace == TEST_NAMESPACE: if namespace == TEST_NAMESPACE:
for key in ('one', 'two words', 'three word command'): for key in ('one', 'two words', 'three word command'):

@ -59,7 +59,6 @@ def damerau_levenshtein(s1, s2, cost):
row2[0] = (i + 1) * cost['d'] row2[0] = (i + 1) * cost['d']
for j in range(len2): for j in range(len2):
# substitution # substitution
sub_cost = row1[j] + (s1[i] != s2[j]) * cost['s'] sub_cost = row1[j] + (s1[i] != s2[j]) * cost['s']
@ -70,11 +69,12 @@ def damerau_levenshtein(s1, s2, cost):
del_cost = row1[j + 1] + cost['d'] del_cost = row1[j + 1] + cost['d']
# swap # swap
swp_condition = ((i > 0) and swp_condition = (
(j > 0) and (i > 0)
(s1[i - 1] == s2[j]) and and (j > 0)
(s1[i] == s2[j - 1]) and (s1[i - 1] == s2[j])
) and (s1[i] == s2[j - 1])
)
# min cost # min cost
if swp_condition: if swp_condition:

@ -6,8 +6,7 @@ from cliff.lister import Lister
class Encoding(Lister): class Encoding(Lister):
"""Show some unicode text """Show some unicode text"""
"""
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -18,6 +17,5 @@ class Encoding(Lister):
] ]
return ( return (
('UTF-8', 'Unicode'), ('UTF-8', 'Unicode'),
[(repr(t.encode('utf-8')), t) [(repr(t.encode('utf-8')), t) for t in messages],
for t in messages],
) )

@ -13,6 +13,7 @@ class Files(Lister):
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def take_action(self, parsed_args): def take_action(self, parsed_args):
return (('Name', 'Size'), return (
((n, os.stat(n).st_size) for n in os.listdir('.')) ('Name', 'Size'),
) ((n, os.stat(n).st_size) for n in os.listdir('.')),
)

@ -5,14 +5,13 @@ from cliff.commandmanager import CommandManager
class DemoApp(App): class DemoApp(App):
def __init__(self): def __init__(self):
super(DemoApp, self).__init__( super(DemoApp, self).__init__(
description='cliff demo app', description='cliff demo app',
version='0.1', version='0.1',
command_manager=CommandManager('cliff.demo'), command_manager=CommandManager('cliff.demo'),
deferred_help=True, deferred_help=True,
) )
def initialize_app(self, argv): def initialize_app(self, argv):
self.LOG.debug('initialize_app') self.LOG.debug('initialize_app')

@ -16,16 +16,18 @@ class File(ShowOne):
def take_action(self, parsed_args): def take_action(self, parsed_args):
stat_data = os.stat(parsed_args.filename) stat_data = os.stat(parsed_args.filename)
columns = ('Name', columns = (
'Size', 'Name',
'UID', 'Size',
'GID', 'UID',
'Modified Time', 'GID',
) 'Modified Time',
data = (parsed_args.filename, )
stat_data.st_size, data = (
stat_data.st_uid, parsed_args.filename,
stat_data.st_gid, stat_data.st_size,
stat_data.st_mtime, stat_data.st_uid,
) stat_data.st_gid,
stat_data.st_mtime,
)
return (columns, data) return (columns, data)

@ -16,16 +16,12 @@ except IOError:
setup( setup(
name=PROJECT, name=PROJECT,
version=VERSION, version=VERSION,
description='Demo app for cliff', description='Demo app for cliff',
long_description=long_description, long_description=long_description,
author='Doug Hellmann', author='Doug Hellmann',
author_email='doug.hellmann@gmail.com', author_email='doug.hellmann@gmail.com',
url='https://github.com/openstack/cliff', url='https://github.com/openstack/cliff',
download_url='https://github.com/openstack/cliff/tarball/master', download_url='https://github.com/openstack/cliff/tarball/master',
classifiers=[ classifiers=[
'Development Status :: 3 - Alpha', 'Development Status :: 3 - Alpha',
'License :: OSI Approved :: Apache Software License', 'License :: OSI Approved :: Apache Software License',
@ -35,22 +31,15 @@ setup(
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'Environment :: Console', 'Environment :: Console',
], ],
platforms=['Any'], platforms=['Any'],
scripts=[], scripts=[],
provides=[], provides=[],
install_requires=['cliff'], install_requires=['cliff'],
namespace_packages=[], namespace_packages=[],
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': ['cliffdemo = cliffdemo.main:main'],
'cliffdemo = cliffdemo.main:main'
],
'cliff.demo': [ 'cliff.demo': [
'simple = cliffdemo.simple:Simple', 'simple = cliffdemo.simple:Simple',
'two_part = cliffdemo.simple:Simple', 'two_part = cliffdemo.simple:Simple',
@ -66,6 +55,5 @@ setup(
'sample-hook = cliffdemo.hook:Hook', 'sample-hook = cliffdemo.hook:Hook',
], ],
}, },
zip_safe=False, zip_safe=False,
) )

@ -197,10 +197,8 @@ htmlhelp_basename = 'cliffdoc'
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper', # 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt', # 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
# 'preamble': '', # 'preamble': '',
} }
@ -209,8 +207,7 @@ latex_elements = {
# (source start file, target name, title, author, # (source start file, target name, title, author,
# documentclass [howto/manual]). # documentclass [howto/manual]).
latex_documents = [ latex_documents = [
('index', 'cliff.tex', 'cliff Documentation', ('index', 'cliff.tex', 'cliff Documentation', 'Doug Hellmann', 'manual'),
'Doug Hellmann', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -238,10 +235,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [('index', 'cliff', 'cliff Documentation', ['Doug Hellmann'], 1)]
('index', 'cliff', 'cliff Documentation',
['Doug Hellmann'], 1)
]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
# man_show_urls = False # man_show_urls = False
@ -253,9 +247,15 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
('index', 'cliff', 'cliff Documentation', (
'Doug Hellmann', 'cliff', 'One line description of project.', 'index',
'Miscellaneous'), 'cliff',
'cliff Documentation',
'Doug Hellmann',
'cliff',
'One line description of project.',
'Miscellaneous',
),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.

@ -57,4 +57,3 @@ cliff.demo =
hooked = cliffdemo.hook:Hooked hooked = cliffdemo.hook:Hooked
cliff.demo.hooked = cliff.demo.hooked =
sample-hook = cliffdemo.hook:Hook sample-hook = cliffdemo.hook:Hook

@ -13,10 +13,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools import setuptools
setuptools.setup( setuptools.setup(
setup_requires=['pbr>=2.0.0'], setup_requires=['pbr>=2.0.0'],
install_requires=['setuptools'], install_requires=['setuptools'],
pbr=True) pbr=True,
)

12
tox.ini

@ -59,3 +59,15 @@ commands =
coverage combine coverage combine
coverage html -d cover coverage html -d cover
coverage xml -o cover/coverage.xml coverage xml -o cover/coverage.xml
[flake8]
application-import-names = cliff
# E203 Black will put spaces after colons in list comprehensions
# E501 Black takes care of line length for us
# W503 Is supposed to be off by default but in the latest pycodestyle isn't.
# Also, both openstacksdk and Donald Knuth disagree with the rule. Line
# breaks should occur before the binary operator for readability.
ignore = E203, E501, W503
import-order-style = pep8
show-source = true
exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build