# -*- coding: UTF-8 -*- # # Copyright (C) 2007 by Jan Dittberner. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, # USA. # # Version: $Id$ import getopt, sys, logging from gnuviechadmin.exceptions import GnuviechadminError class CliCommand: """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. """ 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.""" print """GNUViechAdmin command line interface 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.""" 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 __init__(self, args): """This initializes the command with the given command line arguments and executes it.""" self.logger = logging.getLogger("%s.%s" % ( self.__class__.__module__, self.__class__.__name__)) self._data = {} if len(args) > 0: if args[0] in self._subcommands(): self._parseopts(args[0], args[1:]) reqcheck = self._checkrequired(args[0]) if reqcheck[0]: try: self._execute(args[0]) except GnuviechadminError, e: print e else: self._usage() print """ the following required arguments are missing: """ print "\n".join(reqcheck[1]) else: self._usage() print "invalid sub command" else: self._usage()