+++ /dev/null
-""" CommandLine - Get and parse command line options\r
-\r
- NOTE: This still is very much work in progress !!!\r
-\r
- Different version are likely to be incompatible.\r
-\r
- TODO:\r
-\r
- * Incorporate the changes made by (see Inbox)\r
- * Add number range option using srange()\r
-\r
-"""\r
-\r
-__copyright__ = """\\r
-Copyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com)\r
-Copyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com)\r
-See the documentation for further information on copyrights,\r
-or contact the author. All Rights Reserved.\r
-"""\r
-\r
-__version__ = '1.2'\r
-\r
-import sys, getopt, string, glob, os, re, exceptions, traceback\r
-\r
-### Helpers\r
-\r
-def _getopt_flags(options):\r
-\r
- """ Convert the option list to a getopt flag string and long opt\r
- list\r
-\r
- """\r
- s = []\r
- l = []\r
- for o in options:\r
- if o.prefix == '-':\r
- # short option\r
- s.append(o.name)\r
- if o.takes_argument:\r
- s.append(':')\r
- else:\r
- # long option\r
- if o.takes_argument:\r
- l.append(o.name+'=')\r
- else:\r
- l.append(o.name)\r
- return string.join(s,''),l\r
-\r
-def invisible_input(prompt='>>> '):\r
-\r
- """ Get raw input from a terminal without echoing the characters to\r
- the terminal, e.g. for password queries.\r
-\r
- """\r
- import getpass\r
- entry = getpass.getpass(prompt)\r
- if entry is None:\r
- raise KeyboardInterrupt\r
- return entry\r
-\r
-def fileopen(name, mode='wb', encoding=None):\r
-\r
- """ Open a file using mode.\r
-\r
- Default mode is 'wb' meaning to open the file for writing in\r
- binary mode. If encoding is given, I/O to and from the file is\r
- transparently encoded using the given encoding.\r
-\r
- Files opened for writing are chmod()ed to 0600.\r
-\r
- """\r
- if name == 'stdout':\r
- return sys.stdout\r
- elif name == 'stderr':\r
- return sys.stderr\r
- elif name == 'stdin':\r
- return sys.stdin\r
- else:\r
- if encoding is not None:\r
- import codecs\r
- f = codecs.open(name, mode, encoding)\r
- else:\r
- f = open(name, mode)\r
- if 'w' in mode:\r
- os.chmod(name, 0600)\r
- return f\r
-\r
-def option_dict(options):\r
-\r
- """ Return a dictionary mapping option names to Option instances.\r
- """\r
- d = {}\r
- for option in options:\r
- d[option.name] = option\r
- return d\r
-\r
-# Alias\r
-getpasswd = invisible_input\r
-\r
-_integerRE = re.compile('\s*(-?\d+)\s*$')\r
-_integerRangeRE = re.compile('\s*(-?\d+)\s*-\s*(-?\d+)\s*$')\r
-\r
-def srange(s,\r
-\r
- split=string.split,integer=_integerRE,\r
- integerRange=_integerRangeRE):\r
-\r
- """ Converts a textual representation of integer numbers and ranges\r
- to a Python list.\r
-\r
- Supported formats: 2,3,4,2-10,-1 - -3, 5 - -2\r
-\r
- Values are appended to the created list in the order specified\r
- in the string.\r
-\r
- """\r
- l = []\r
- append = l.append\r
- for entry in split(s,','):\r
- m = integer.match(entry)\r
- if m:\r
- append(int(m.groups()[0]))\r
- continue\r
- m = integerRange.match(entry)\r
- if m:\r
- start,end = map(int,m.groups())\r
- l[len(l):] = range(start,end+1)\r
- return l\r
-\r
-def abspath(path,\r
-\r
- expandvars=os.path.expandvars,expanduser=os.path.expanduser,\r
- join=os.path.join,getcwd=os.getcwd):\r
-\r
- """ Return the corresponding absolute path for path.\r
-\r
- path is expanded in the usual shell ways before\r
- joining it with the current working directory.\r
-\r
- """\r
- try:\r
- path = expandvars(path)\r
- except AttributeError:\r
- pass\r
- try:\r
- path = expanduser(path)\r
- except AttributeError:\r
- pass\r
- return join(getcwd(), path)\r
-\r
-### Option classes\r
-\r
-class Option:\r
-\r
- """ Option base class. Takes no argument.\r
-\r
- """\r
- default = None\r
- helptext = ''\r
- prefix = '-'\r
- takes_argument = 0\r
- has_default = 0\r
- tab = 15\r
-\r
- def __init__(self,name,help=None):\r
-\r
- if not name[:1] == '-':\r
- raise TypeError,'option names must start with "-"'\r
- if name[1:2] == '-':\r
- self.prefix = '--'\r
- self.name = name[2:]\r
- else:\r
- self.name = name[1:]\r
- if help:\r
- self.help = help\r
-\r
- def __str__(self):\r
-\r
- o = self\r
- name = o.prefix + o.name\r
- if o.takes_argument:\r
- name = name + ' arg'\r
- if len(name) > self.tab:\r
- name = name + '\n' + ' ' * (self.tab + 1 + len(o.prefix))\r
- else:\r
- name = '%-*s ' % (self.tab, name)\r
- description = o.help\r
- if o.has_default:\r
- description = description + ' (%s)' % o.default\r
- return '%s %s' % (name, description)\r
-\r
-class ArgumentOption(Option):\r
-\r
- """ Option that takes an argument.\r
-\r
- An optional default argument can be given.\r
-\r
- """\r
- def __init__(self,name,help=None,default=None):\r
-\r
- # Basemethod\r
- Option.__init__(self,name,help)\r
-\r
- if default is not None:\r
- self.default = default\r
- self.has_default = 1\r
- self.takes_argument = 1\r
-\r
-class SwitchOption(Option):\r
-\r
- """ Options that can be on or off. Has an optional default value.\r
-\r
- """\r
- def __init__(self,name,help=None,default=None):\r
-\r
- # Basemethod\r
- Option.__init__(self,name,help)\r
-\r
- if default is not None:\r
- self.default = default\r
- self.has_default = 1\r
-\r
-### Application baseclass\r
-\r
-class Application:\r
-\r
- """ Command line application interface with builtin argument\r
- parsing.\r
-\r
- """\r
- # Options the program accepts (Option instances)\r
- options = []\r
-\r
- # Standard settings; these are appended to options in __init__\r
- preset_options = [SwitchOption('-v',\r
- 'generate verbose output'),\r
- SwitchOption('-h',\r
- 'show this help text'),\r
- SwitchOption('--help',\r
- 'show this help text'),\r
- SwitchOption('--debug',\r
- 'enable debugging'),\r
- SwitchOption('--copyright',\r
- 'show copyright'),\r
- SwitchOption('--examples',\r
- 'show examples of usage')]\r
-\r
- # The help layout looks like this:\r
- # [header] - defaults to ''\r
- #\r
- # [synopsis] - formatted as '<self.name> %s' % self.synopsis\r
- #\r
- # options:\r
- # [options] - formatted from self.options\r
- #\r
- # [version] - formatted as 'Version:\n %s' % self.version, if given\r
- #\r
- # [about] - defaults to ''\r
- #\r
- # Note: all fields that do not behave as template are formatted\r
- # using the instances dictionary as substitution namespace,\r
- # e.g. %(name)s will be replaced by the applications name.\r
- #\r
-\r
- # Header (default to program name)\r
- header = ''\r
-\r
- # Name (defaults to program name)\r
- name = ''\r
-\r
- # Synopsis (%(name)s is replaced by the program name)\r
- synopsis = '%(name)s [option] files...'\r
-\r
- # Version (optional)\r
- version = ''\r
-\r
- # General information printed after the possible options (optional)\r
- about = ''\r
-\r
- # Examples of usage to show when the --examples option is given (optional)\r
- examples = ''\r
-\r
- # Copyright to show\r
- copyright = __copyright__\r
-\r
- # Apply file globbing ?\r
- globbing = 1\r
-\r
- # Generate debug output ?\r
- debug = 0\r
-\r
- # Generate verbose output ?\r
- verbose = 0\r
-\r
- # Internal errors to catch\r
- InternalError = exceptions.Exception\r
-\r
- # Instance variables:\r
- values = None # Dictionary of passed options (or default values)\r
- # indexed by the options name, e.g. '-h'\r
- files = None # List of passed filenames\r
- optionlist = None # List of passed options\r
-\r
- def __init__(self,argv=None):\r
-\r
- # Setup application specs\r
- if argv is None:\r
- argv = sys.argv\r
- self.filename = os.path.split(argv[0])[1]\r
- if not self.name:\r
- self.name = os.path.split(self.filename)[1]\r
- else:\r
- self.name = self.name\r
- if not self.header:\r
- self.header = self.name\r
- else:\r
- self.header = self.header\r
-\r
- # Init .arguments list\r
- self.arguments = argv[1:]\r
-\r
- # Setup Option mapping\r
- self.option_map = option_dict(self.options)\r
-\r
- # Append preset options\r
- for option in self.preset_options:\r
- if not self.option_map.has_key(option.name):\r
- self.add_option(option)\r
-\r
- # Init .files list\r
- self.files = []\r
-\r
- # Start Application\r
- try:\r
- # Process startup\r
- rc = self.startup()\r
- if rc is not None:\r
- raise SystemExit,rc\r
-\r
- # Parse command line\r
- rc = self.parse()\r
- if rc is not None:\r
- raise SystemExit,rc\r
-\r
- # Start application\r
- rc = self.main()\r
- if rc is None:\r
- rc = 0\r
-\r
- except SystemExit,rc:\r
- pass\r
-\r
- except KeyboardInterrupt:\r
- print\r
- print '* User Break'\r
- print\r
- rc = 1\r
-\r
- except self.InternalError:\r
- print\r
- print '* Internal Error (use --debug to display the traceback)'\r
- if self.debug:\r
- print\r
- traceback.print_exc(20, sys.stdout)\r
- elif self.verbose:\r
- print ' %s: %s' % sys.exc_info()[:2]\r
- print\r
- rc = 1\r
-\r
- raise SystemExit,rc\r
-\r
- def add_option(self, option):\r
-\r
- """ Add a new Option instance to the Application dynamically.\r
-\r
- Note that this has to be done *before* .parse() is being\r
- executed.\r
-\r
- """\r
- self.options.append(option)\r
- self.option_map[option.name] = option\r
-\r
- def startup(self):\r
-\r
- """ Set user defined instance variables.\r
-\r
- If this method returns anything other than None, the\r
- process is terminated with the return value as exit code.\r
-\r
- """\r
- return None\r
-\r
- def exit(self, rc=0):\r
-\r
- """ Exit the program.\r
-\r
- rc is used as exit code and passed back to the calling\r
- program. It defaults to 0 which usually means: OK.\r
-\r
- """\r
- raise SystemExit, rc\r
-\r
- def parse(self):\r
-\r
- """ Parse the command line and fill in self.values and self.files.\r
-\r
- After having parsed the options, the remaining command line\r
- arguments are interpreted as files and passed to .handle_files()\r
- for processing.\r
-\r
- As final step the option handlers are called in the order\r
- of the options given on the command line.\r
-\r
- """\r
- # Parse arguments\r
- self.values = values = {}\r
- for o in self.options:\r
- if o.has_default:\r
- values[o.prefix+o.name] = o.default\r
- else:\r
- values[o.prefix+o.name] = 0\r
- flags,lflags = _getopt_flags(self.options)\r
- try:\r
- optlist,files = getopt.getopt(self.arguments,flags,lflags)\r
- if self.globbing:\r
- l = []\r
- for f in files:\r
- gf = glob.glob(f)\r
- if not gf:\r
- l.append(f)\r
- else:\r
- l[len(l):] = gf\r
- files = l\r
- self.optionlist = optlist\r
- self.files = files + self.files\r
- except getopt.error,why:\r
- self.help(why)\r
- sys.exit(1)\r
-\r
- # Call file handler\r
- rc = self.handle_files(self.files)\r
- if rc is not None:\r
- sys.exit(rc)\r
-\r
- # Call option handlers\r
- for optionname, value in optlist:\r
-\r
- # Try to convert value to integer\r
- try:\r
- value = string.atoi(value)\r
- except ValueError:\r
- pass\r
-\r
- # Find handler and call it (or count the number of option\r
- # instances on the command line)\r
- handlername = 'handle' + string.replace(optionname, '-', '_')\r
- try:\r
- handler = getattr(self, handlername)\r
- except AttributeError:\r
- if value == '':\r
- # count the number of occurances\r
- if values.has_key(optionname):\r
- values[optionname] = values[optionname] + 1\r
- else:\r
- values[optionname] = 1\r
- else:\r
- values[optionname] = value\r
- else:\r
- rc = handler(value)\r
- if rc is not None:\r
- raise SystemExit, rc\r
-\r
- # Apply final file check (for backward compatibility)\r
- rc = self.check_files(self.files)\r
- if rc is not None:\r
- sys.exit(rc)\r
-\r
- def check_files(self,filelist):\r
-\r
- """ Apply some user defined checks on the files given in filelist.\r
-\r
- This may modify filelist in place. A typical application\r
- is checking that at least n files are given.\r
-\r
- If this method returns anything other than None, the\r
- process is terminated with the return value as exit code.\r
-\r
- """\r
- return None\r
-\r
- def help(self,note=''):\r
-\r
- self.print_header()\r
- if self.synopsis:\r
- print 'Synopsis:'\r
- # To remain backward compatible:\r
- try:\r
- synopsis = self.synopsis % self.name\r
- except (NameError, KeyError, TypeError):\r
- synopsis = self.synopsis % self.__dict__\r
- print ' ' + synopsis\r
- print\r
- self.print_options()\r
- if self.version:\r
- print 'Version:'\r
- print ' %s' % self.version\r
- print\r
- if self.about:\r
- print string.strip(self.about % self.__dict__)\r
- print\r
- if note:\r
- print '-'*72\r
- print 'Note:',note\r
- print\r
-\r
- def notice(self,note):\r
-\r
- print '-'*72\r
- print 'Note:',note\r
- print '-'*72\r
- print\r
-\r
- def print_header(self):\r
-\r
- print '-'*72\r
- print self.header % self.__dict__\r
- print '-'*72\r
- print\r
-\r
- def print_options(self):\r
-\r
- options = self.options\r
- print 'Options and default settings:'\r
- if not options:\r
- print ' None'\r
- return\r
- long = filter(lambda x: x.prefix == '--', options)\r
- short = filter(lambda x: x.prefix == '-', options)\r
- items = short + long\r
- for o in options:\r
- print ' ',o\r
- print\r
-\r
- #\r
- # Example handlers:\r
- #\r
- # If a handler returns anything other than None, processing stops\r
- # and the return value is passed to sys.exit() as argument.\r
- #\r
-\r
- # File handler\r
- def handle_files(self,files):\r
-\r
- """ This may process the files list in place.\r
- """\r
- return None\r
-\r
- # Short option handler\r
- def handle_h(self,arg):\r
-\r
- self.help()\r
- return 0\r
-\r
- def handle_v(self, value):\r
-\r
- """ Turn on verbose output.\r
- """\r
- self.verbose = 1\r
-\r
- # Handlers for long options have two underscores in their name\r
- def handle__help(self,arg):\r
-\r
- self.help()\r
- return 0\r
-\r
- def handle__debug(self,arg):\r
-\r
- self.debug = 1\r
- # We don't want to catch internal errors:\r
- self.InternalError = None\r
-\r
- def handle__copyright(self,arg):\r
-\r
- self.print_header()\r
- print string.strip(self.copyright % self.__dict__)\r
- print\r
- return 0\r
-\r
- def handle__examples(self,arg):\r
-\r
- self.print_header()\r
- if self.examples:\r
- print 'Examples:'\r
- print\r
- print string.strip(self.examples % self.__dict__)\r
- print\r
- else:\r
- print 'No examples available.'\r
- print\r
- return 0\r
-\r
- def main(self):\r
-\r
- """ Override this method as program entry point.\r
-\r
- The return value is passed to sys.exit() as argument. If\r
- it is None, 0 is assumed (meaning OK). Unhandled\r
- exceptions are reported with exit status code 1 (see\r
- __init__ for further details).\r
-\r
- """\r
- return None\r
-\r
-# Alias\r
-CommandLine = Application\r
-\r
-def _test():\r
-\r
- class MyApplication(Application):\r
- header = 'Test Application'\r
- version = __version__\r
- options = [Option('-v','verbose')]\r
-\r
- def handle_v(self,arg):\r
- print 'VERBOSE, Yeah !'\r
-\r
- cmd = MyApplication()\r
- if not cmd.values['-h']:\r
- cmd.help()\r
- print 'files:',cmd.files\r
- print 'Bye...'\r
-\r
-if __name__ == '__main__':\r
- _test()\r