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