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()
|
sess.rollback()
|
||||||
self.logger.exception("Exception in create.")
|
self.logger.exception("Exception in create.")
|
||||||
raise
|
raise
|
||||||
|
return entity
|
||||||
|
|
||||||
def fetchall(self, **kwargs):
|
def fetchall(self, **kwargs):
|
||||||
"""Fetches all entities of the managed entity type."""
|
"""Fetches all entities of the managed entity type."""
|
||||||
|
|
|
@ -26,13 +26,14 @@ class BackendTo(object):
|
||||||
"""Backend transfer object class."""
|
"""Backend transfer object class."""
|
||||||
|
|
||||||
def __init__(self, config, **kwargs):
|
def __init__(self, config, **kwargs):
|
||||||
|
cols = object_mapper(self).local_table.columns.keys()
|
||||||
for (key, value) in kwargs.items():
|
for (key, value) in kwargs.items():
|
||||||
|
if key in cols and value is not None:
|
||||||
self.__setattr__(key, unicode(value, 'utf8'))
|
self.__setattr__(key, unicode(value, 'utf8'))
|
||||||
|
|
||||||
def __repr__(self, **kwargs):
|
def __repr__(self, **kwargs):
|
||||||
if 'verbose' in kwargs and kwargs['verbose']:
|
if 'verbose' in kwargs and kwargs['verbose']:
|
||||||
cols = [col for col in \
|
cols = object_mapper(self).local_table.columns.keys()
|
||||||
object_mapper(self).local_table.columns.keys()]
|
|
||||||
format = "%(class)s:"
|
format = "%(class)s:"
|
||||||
format = format + ", ".join([col + "=%(" + col + ")s" for col in \
|
format = format + ", ".join([col + "=%(" + col + ")s" for col in \
|
||||||
cols])
|
cols])
|
||||||
|
|
|
@ -25,181 +25,38 @@ from gnuviechadmin.exceptions import GnuviechadminError
|
||||||
|
|
||||||
|
|
||||||
class CliCommand(object):
|
class CliCommand(object):
|
||||||
"""Base class for command line interface.
|
"""
|
||||||
|
Base class for command line interface.
|
||||||
|
|
||||||
A specific implementation class must define the fields name,
|
A specific implementation class must implement the static
|
||||||
description and _optionmap.
|
setup_arparser method and the _execute method.
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _usage(self):
|
@staticmethod
|
||||||
"""This method shows usage information. The implementation
|
def setup_argparser(subparsers):
|
||||||
relies on the information in the fields name, description and
|
"""
|
||||||
_optionmap in the implementation classes."""
|
This static method is used to initialize the command line
|
||||||
return """GNUViechAdmin command line interface
|
argument parser.
|
||||||
|
|
||||||
Subcommand: %(command)s
|
The method has to be overridden by subclasses.
|
||||||
|
"""
|
||||||
%(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."""
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def _parseopts(self, subcommand, args):
|
|
||||||
"""This method parses the options given on the command line."""
|
def _execute(self):
|
||||||
longopts = ["help", "verbose"]
|
"""
|
||||||
longopts.extend(self._longopts(subcommand))
|
This method is called when the subcommand of the command is
|
||||||
try:
|
executed.
|
||||||
opts, args = getopt.gnu_getopt(
|
|
||||||
args,
|
The method has to be overridden by subclasses.
|
||||||
"hv" + self._shortopts(subcommand),
|
"""
|
||||||
longopts)
|
raise NotImplementedError
|
||||||
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 __init__(self, args, config):
|
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.config = config
|
||||||
self.args = args
|
self.args = args
|
||||||
self.logger = logging.getLogger("%s.%s" % (
|
self.logger = logging.getLogger("%s.%s" % (
|
||||||
|
|
|
@ -60,7 +60,7 @@ class ClientCli(CliCommand.CliCommand):
|
||||||
help = _("the city of the client's address"))
|
help = _("the city of the client's address"))
|
||||||
cmdparser.add_argument(
|
cmdparser.add_argument(
|
||||||
'--country',
|
'--country',
|
||||||
help = _("the client's country"))
|
help = _("the client's country"), default='de')
|
||||||
cmdparser.add_argument(
|
cmdparser.add_argument(
|
||||||
'-e', '--email', required = True,
|
'-e', '--email', required = True,
|
||||||
help = _("the client's email address"))
|
help = _("the client's email address"))
|
||||||
|
@ -91,9 +91,9 @@ class ClientCli(CliCommand.CliCommand):
|
||||||
ch = ClientHandler(self.config, self.args.verbose)
|
ch = ClientHandler(self.config, self.args.verbose)
|
||||||
if self.args.subcommand == "create":
|
if self.args.subcommand == "create":
|
||||||
try:
|
try:
|
||||||
myclient = ch.create(**self.args)
|
myclient = ch.create(**self.args.__dict__)
|
||||||
if self.args.verbose:
|
if self.args.verbose:
|
||||||
print myclient
|
print unicode(myclient).encode('utf8')
|
||||||
except CreationFailedError, cfe:
|
except CreationFailedError, cfe:
|
||||||
self._usage()
|
self._usage()
|
||||||
print cfe
|
print cfe
|
||||||
|
@ -101,7 +101,7 @@ class ClientCli(CliCommand.CliCommand):
|
||||||
elif self.args.subcommand == "list":
|
elif self.args.subcommand == "list":
|
||||||
clients = ch.fetchall()
|
clients = ch.fetchall()
|
||||||
for client in clients:
|
for client in clients:
|
||||||
print client
|
print unicode(client).encode('utf8')
|
||||||
elif self.args.subcommand == "delete":
|
elif self.args.subcommand == "delete":
|
||||||
ch.delete(self.args.clientid)
|
ch.delete(self.args.clientid)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue