/usr/lib/python3/dist-packages/testrepository/commands/__init__.py is in python3-testrepository 0.0.20-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | #
# Copyright (c) 2009, 2010 Testrepository Contributors
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
# license at the users choice. A copy of both licenses are available in the
# project source as Apache-2.0 and BSD. You may not use this file except in
# compliance with one of these two licences.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# license you chose for the specific language governing permissions and
# limitations under that license.
"""'Commands' for testr.
The code in this module contains the Command base class, the run_argv
entry point to run CLI commands.
Actual commands can be found in testrepository.commands.$commandname.
For example, testrepository.commands.init is the init command name, and
testrepository.command.show_stats would be the show-stats command (if one
existed). The Command discovery logic looks for a class in the module with
the same name - e.g. tesrepository.commands.init.init would be the class.
That class must obey the testrepository.commands.Command protocol, but does
not need to be a subclass.
Plugins and extensions wanting to add commands should install them into
testrepository.commands (perhaps by extending the testrepository.commands
__path__ to include a directory containing their commands - no __init__ is
needed in that directory.)
"""
from inspect import getdoc
from optparse import OptionParser
import os
import sys
import subunit
from testtools.compat import _u
from testrepository.repository import file
def _find_command(cmd_name):
orig_cmd_name = cmd_name
cmd_name = cmd_name.replace('-', '_')
classname = "%s" % cmd_name
modname = "testrepository.commands.%s" % cmd_name
try:
_temp = __import__(modname, globals(), locals(), [classname])
except ImportError:
raise KeyError("Could not import command module %s" % modname)
result = getattr(_temp, classname, None)
if result is None:
raise KeyError(
"Malformed command module - no command class %s found in module %s."
% (classname, modname))
if getattr(result, 'name', None) is None:
# Store the name for the common case of name == lookup path.
result.name = orig_cmd_name
return result
def iter_commands():
"""Iterate over all the command classes."""
paths = __path__
names = set()
for path in paths:
# For now, only support regular installs. TODO: support zip, eggs.
for filename in os.listdir(path):
base = os.path.basename(filename)
if base.startswith('.'):
continue
name = base.split('.', 1)[0]
name = name.replace('_', '-')
names.add(name)
names.discard('--init--')
names.discard('--pycache--')
names = sorted(names)
for name in names:
yield _find_command(name)
class Command(object):
"""A command that can be run.
Commands contain non-UI non-domain specific behaviour - they are the
glue between the UI and the object model.
Commands are parameterised with:
:ivar ui: a UI object which is responsible for brokering the command
arguments, input and output. There is no default ui, it must be
passed to the constructor.
:ivar repository_factory: a repository factory which is used to create or
open repositories. The default repository factory is suitable for
use in the command line tool.
Commands declare that they accept/need/emit:
:ivar args: A list of testrepository.arguments.AbstractArgument instances.
AbstractArgument arguments are validated when set_command is called on
the UI layer.
:ivar input_streams: A list of stream specifications. Mandatory streams
are specified by a simple name. Optional streams are specified by
a simple name with a ? ending the name. Optional multiple streams are
specified by a simple name with a * ending the name, and mandatory
multiple streams by ending the name with +. Multiple streams are used
when a command can process more than one stream.
:ivar options: A list of optparse.Option options to accept. These are
merged with global options by the UI layer when set_command is called.
"""
# class defaults to no streams.
input_streams = []
# class defaults to no arguments.
args = []
# class defaults to no options.
options = []
def __init__(self, ui):
"""Create a Command object with ui ui."""
self.ui = ui
self.repository_factory = file.RepositoryFactory()
self._init()
def execute(self):
"""Execute a command.
This interrogates the UI to ensure that arguments and options are
supplied, performs any validation for the same that the command needs
and finally calls run() to perform the command. Most commands should
not need to override this method, and any user wanting to run a
command should call this method.
This is a synchronous method, and basically just a helper. GUI's or
asynchronous programs can choose to not call it and instead should call
lower level API's.
"""
if not self.ui.set_command(self):
return 1
try:
result = self.run()
except Exception:
error_tuple = sys.exc_info()
self.ui.output_error(error_tuple)
return 3
if not result:
return 0
return result
@classmethod
def get_summary(klass):
docs = klass.__doc__.split('\n')
return docs[0]
def _init(self):
"""Per command init call, called into by Command.__init__."""
def run(self):
"""The core logic for this command to be implemented by subclasses."""
raise NotImplementedError(self.run)
def run_argv(argv, stdin, stdout, stderr):
"""Convenience function to run a command with a CLIUI.
:param argv: The argv to run the command with.
:param stdin: The stdin stream for the command.
:param stdout: The stdout stream for the command.
:param stderr: The stderr stream for the command.
:return: An integer exit code for the command.
"""
cmd_name = None
cmd_args = argv[1:]
for arg in argv[1:]:
if not arg.startswith('-'):
cmd_name = arg
break
if cmd_name is None:
cmd_name = 'help'
cmd_args = ['help']
cmd_args.remove(cmd_name)
cmdclass = _find_command(cmd_name)
from testrepository.ui import cli
ui = cli.UI(cmd_args, stdin, stdout, stderr)
cmd = cmdclass(ui)
result = cmd.execute()
if not result:
return 0
return result
def get_command_parser(cmd):
"""Return an OptionParser for cmd.
This populates the parser with the commands options and sets its usage
string based on the arguments and docstring the command has.
Global options are not provided (as they are UI specific).
:return: An OptionParser instance.
"""
parser = OptionParser()
for option in cmd.options:
parser.add_option(option)
usage = _u('%%prog %(cmd)s [options] %(args)s\n\n%(help)s') % {
'args': _u(' ').join(map(lambda x:x.summary(), cmd.args)),
'cmd': getattr(cmd, 'name', cmd),
'help': getdoc(cmd),
}
parser.set_usage(usage)
return parser
|