code cleanup for argparse support (addresses #33)
* remove unused code from CliCommand class * improve parameter handling in BackendTo constructor * return entity in BackendEntityHandler's create method * add a default value for country in ClientCli * pass dictionary from parsed arguments to ClientHandler's create method * correctly handle special chars in ClientCli output
This commit is contained in:
parent
b8139e91f2
commit
45b2865e8e
4 changed files with 33 additions and 174 deletions
|
@ -58,6 +58,7 @@ class BackendEntityHandler(object):
|
|||
sess.rollback()
|
||||
self.logger.exception("Exception in create.")
|
||||
raise
|
||||
return entity
|
||||
|
||||
def fetchall(self, **kwargs):
|
||||
"""Fetches all entities of the managed entity type."""
|
||||
|
|
|
@ -26,13 +26,14 @@ class BackendTo(object):
|
|||
"""Backend transfer object class."""
|
||||
|
||||
def __init__(self, config, **kwargs):
|
||||
cols = object_mapper(self).local_table.columns.keys()
|
||||
for (key, value) in kwargs.items():
|
||||
if key in cols and value is not None:
|
||||
self.__setattr__(key, unicode(value, 'utf8'))
|
||||
|
||||
def __repr__(self, **kwargs):
|
||||
if 'verbose' in kwargs and kwargs['verbose']:
|
||||
cols = [col for col in \
|
||||
object_mapper(self).local_table.columns.keys()]
|
||||
cols = object_mapper(self).local_table.columns.keys()
|
||||
format = "%(class)s:"
|
||||
format = format + ", ".join([col + "=%(" + col + ")s" for col in \
|
||||
cols])
|
||||
|
|
|
@ -25,181 +25,38 @@ from gnuviechadmin.exceptions import GnuviechadminError
|
|||
|
||||
|
||||
class CliCommand(object):
|
||||
"""Base class for command line interface.
|
||||
"""
|
||||
Base class for command line interface.
|
||||
|
||||
A specific implementation class must define the fields name,
|
||||
description and _optionmap.
|
||||
|
||||
The field name is the name of the subcommand.
|
||||
|
||||
The field description is a short, one line description of the
|
||||
command.
|
||||
|
||||
The field _optionmap is a map which maps the subcommand names to
|
||||
lists of tuples. Each tuple consists of four elements. The first
|
||||
element is a list of command line arguments, short arguments start
|
||||
with dash, long arguments with a double dash. The second element
|
||||
is the name of the field in the data map of the command, it will
|
||||
directly be sent to the underlying entity. The third field is a
|
||||
description for the group of command line options in field
|
||||
one. The fourth field is True for mandatory fields and False
|
||||
otherwise.
|
||||
A specific implementation class must implement the static
|
||||
setup_arparser method and the _execute method.
|
||||
"""
|
||||
|
||||
def _usage(self):
|
||||
"""This method shows usage information. The implementation
|
||||
relies on the information in the fields name, description and
|
||||
_optionmap in the implementation classes."""
|
||||
return """GNUViechAdmin command line interface
|
||||
@staticmethod
|
||||
def setup_argparser(subparsers):
|
||||
"""
|
||||
This static method is used to initialize the command line
|
||||
argument parser.
|
||||
|
||||
Subcommand: %(command)s
|
||||
|
||||
%(description)s
|
||||
|
||||
Usage:
|
||||
|
||||
%(called)s %(command)s -h|--help
|
||||
|
||||
gives this usage information.
|
||||
|
||||
Common options:
|
||||
|
||||
%(option)s
|
||||
%(mandatory)s %(optiondesc)s
|
||||
""" % {'called': sys.argv[0],
|
||||
'command': self.name,
|
||||
'description': self.description,
|
||||
'option': '-v, --verbose',
|
||||
'optiondesc': 'verbose operation',
|
||||
'mandatory': " "}
|
||||
for commandname in self._optionmap.keys():
|
||||
cmdl = "%(called)s %(command)s %(subcommand)s [-v|--verbose]" % {
|
||||
'called': sys.argv[0],
|
||||
'command': self.name,
|
||||
'subcommand': commandname}
|
||||
desc = """
|
||||
%s
|
||||
""" % (self._optionmap[commandname][0])
|
||||
for (options, field, optiondesc, mandatory) in \
|
||||
self._optionmap[commandname][1]:
|
||||
cmd = " "
|
||||
pairs = []
|
||||
for option in options:
|
||||
if field:
|
||||
if option.startswith("--"):
|
||||
pairs.append("%s=<%s>" % (option, field))
|
||||
else:
|
||||
pairs.append("%s <%s>" % (option, field))
|
||||
else:
|
||||
pairs.append(option)
|
||||
if not mandatory:
|
||||
cmd = cmd + "["
|
||||
cmd = cmd + "|".join(pairs)
|
||||
if not mandatory:
|
||||
cmd = cmd + "]"
|
||||
descmap = {
|
||||
'option': ", ".join(pairs),
|
||||
'optiondesc': optiondesc,
|
||||
'mandatory': ' '}
|
||||
if mandatory:
|
||||
descmap['mandatory'] = '*'
|
||||
desc = desc + """ %(option)s
|
||||
%(mandatory)s %(optiondesc)s
|
||||
""" % descmap
|
||||
if (len(cmdl) + len(cmd)) > 79:
|
||||
print cmdl
|
||||
cmdl = cmd
|
||||
else:
|
||||
cmdl = cmdl + cmd
|
||||
print cmdl
|
||||
print desc
|
||||
print "Mandatory options are marked with *"
|
||||
|
||||
def _subcommands(self):
|
||||
"""Convenience method for retrieving the subcommand names from
|
||||
the _optionmap field."""
|
||||
return self._optionmap.keys()
|
||||
|
||||
def _longopts(self, subcommand):
|
||||
"""This method retrieves available long options in a format
|
||||
valid for getopt.gnu_getopt(...) from the _optionmap field."""
|
||||
longopts = []
|
||||
for cur in [(option[0], option[1]) for option in \
|
||||
self._optionmap[subcommand][1]]:
|
||||
for command in cur[0]:
|
||||
if command.startswith("--"):
|
||||
if cur[1]:
|
||||
longopts.append(command[2:] + "=")
|
||||
else:
|
||||
longopts.append(command[2:])
|
||||
return longopts
|
||||
|
||||
def _shortopts(self, subcommand):
|
||||
"""This method retrieves available short options in a format
|
||||
valid for getopt.gnu_getopt(...) from the _optionmap field."""
|
||||
shortopts = ""
|
||||
for cur in [(option[0], option[1]) for option in \
|
||||
self._optionmap[subcommand][1]]:
|
||||
for command in cur[0]:
|
||||
if not command.startswith("--"):
|
||||
if cur[1]:
|
||||
shortopts = shortopts + command[1:] + ":"
|
||||
else:
|
||||
shortopts = shortopts + command[1:]
|
||||
return shortopts
|
||||
|
||||
def _checkrequired(self, subcommand):
|
||||
"""Checks whether the required fields of the given subcommand
|
||||
are set."""
|
||||
reqcheck = [True, []]
|
||||
for req in [option for option in \
|
||||
self._optionmap[subcommand][1] if option[3]]:
|
||||
if not req[1] in self._data:
|
||||
reqcheck[0] = False
|
||||
reqcheck[1].append(""" %s
|
||||
* %s""" % (", ".join(req[0]), req[2]))
|
||||
return reqcheck
|
||||
|
||||
def _handleoption(self, subcommand, o, a):
|
||||
"""Handles a command line option by assigning it to the
|
||||
matching key as defined in the _optionmap property of the
|
||||
implementation class."""
|
||||
optionmap = [(option[0], option[1]) for option in \
|
||||
self._optionmap[subcommand][1]]
|
||||
if optionmap:
|
||||
for (options, datakey) in optionmap:
|
||||
if o in options:
|
||||
self._data[datakey] = a
|
||||
|
||||
def _execute(self, subcommand):
|
||||
"""This method is called when the subcommand of the command is
|
||||
executed."""
|
||||
The method has to be overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _parseopts(self, subcommand, args):
|
||||
"""This method parses the options given on the command line."""
|
||||
longopts = ["help", "verbose"]
|
||||
longopts.extend(self._longopts(subcommand))
|
||||
try:
|
||||
opts, args = getopt.gnu_getopt(
|
||||
args,
|
||||
"hv" + self._shortopts(subcommand),
|
||||
longopts)
|
||||
except getopt.GetoptError:
|
||||
self._usage()
|
||||
sys.exit(2)
|
||||
self._verbose = False
|
||||
for o, a in opts:
|
||||
if o in ("-v", "--verbose"):
|
||||
self._verbose = True
|
||||
if o in ("-h", "--help"):
|
||||
self._usage()
|
||||
sys.exit()
|
||||
self._handleoption(subcommand, o, a)
|
||||
|
||||
def _execute(self):
|
||||
"""
|
||||
This method is called when the subcommand of the command is
|
||||
executed.
|
||||
|
||||
The method has to be overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __init__(self, args, config):
|
||||
"""This initializes the command with the given command line
|
||||
arguments and executes it."""
|
||||
"""
|
||||
This initializes the command with the given command line
|
||||
arguments and executes it.
|
||||
"""
|
||||
self.config = config
|
||||
self.args = args
|
||||
self.logger = logging.getLogger("%s.%s" % (
|
||||
|
|
|
@ -60,7 +60,7 @@ class ClientCli(CliCommand.CliCommand):
|
|||
help = _("the city of the client's address"))
|
||||
cmdparser.add_argument(
|
||||
'--country',
|
||||
help = _("the client's country"))
|
||||
help = _("the client's country"), default='de')
|
||||
cmdparser.add_argument(
|
||||
'-e', '--email', required = True,
|
||||
help = _("the client's email address"))
|
||||
|
@ -91,9 +91,9 @@ class ClientCli(CliCommand.CliCommand):
|
|||
ch = ClientHandler(self.config, self.args.verbose)
|
||||
if self.args.subcommand == "create":
|
||||
try:
|
||||
myclient = ch.create(**self.args)
|
||||
myclient = ch.create(**self.args.__dict__)
|
||||
if self.args.verbose:
|
||||
print myclient
|
||||
print unicode(myclient).encode('utf8')
|
||||
except CreationFailedError, cfe:
|
||||
self._usage()
|
||||
print cfe
|
||||
|
@ -101,7 +101,7 @@ class ClientCli(CliCommand.CliCommand):
|
|||
elif self.args.subcommand == "list":
|
||||
clients = ch.fetchall()
|
||||
for client in clients:
|
||||
print client
|
||||
print unicode(client).encode('utf8')
|
||||
elif self.args.subcommand == "delete":
|
||||
ch.delete(self.args.clientid)
|
||||
|
||||
|
|
Loading…
Reference in a new issue