summaryrefslogtreecommitdiffstats
path: root/Tools/pybench/CommandLine.py
diff options
context:
space:
mode:
authorThomas Wouters <thomas@python.org>2006-04-21 10:40:58 (GMT)
committerThomas Wouters <thomas@python.org>2006-04-21 10:40:58 (GMT)
commit49fd7fa4431da299196d74087df4a04f99f9c46f (patch)
tree35ace5fe78d3d52c7a9ab356ab9f6dbf8d4b71f4 /Tools/pybench/CommandLine.py
parent9ada3d6e29d5165dadacbe6be07bcd35cfbef59d (diff)
downloadcpython-49fd7fa4431da299196d74087df4a04f99f9c46f.zip
cpython-49fd7fa4431da299196d74087df4a04f99f9c46f.tar.gz
cpython-49fd7fa4431da299196d74087df4a04f99f9c46f.tar.bz2
Merge p3yk branch with the trunk up to revision 45595. This breaks a fair
number of tests, all because of the codecs/_multibytecodecs issue described here (it's not a Py3K issue, just something Py3K discovers): http://mail.python.org/pipermail/python-dev/2006-April/064051.html Hye-Shik Chang promised to look for a fix, so no need to fix it here. The tests that are expected to break are: test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecs test_multibytecodec This merge fixes an actual test failure (test_weakref) in this branch, though, so I believe merging is the right thing to do anyway.
Diffstat (limited to 'Tools/pybench/CommandLine.py')
-rw-r--r--Tools/pybench/CommandLine.py634
1 files changed, 634 insertions, 0 deletions
diff --git a/Tools/pybench/CommandLine.py b/Tools/pybench/CommandLine.py
new file mode 100644
index 0000000..fb7e07b
--- /dev/null
+++ b/Tools/pybench/CommandLine.py
@@ -0,0 +1,634 @@
+""" 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__ = """\
+Copyright (c), 1997-2006, Marc-Andre Lemburg (mal@lemburg.com)
+Copyright (c), 2000-2006, eGenix.com Software GmbH (info@egenix.com)
+See the documentation for further information on copyrights,
+or contact the author. All Rights Reserved.
+"""
+
+__version__ = '1.2'
+
+import sys, getopt, string, glob, os, re, exceptions, traceback
+
+### 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 fileopen(name, mode='wb', encoding=None):
+
+ """ Open a file using mode.
+
+ Default mode is 'wb' meaning to open the file for writing in
+ binary mode. If encoding is given, I/O to and from the file is
+ transparently encoded using the given encoding.
+
+ Files opened for writing are chmod()ed to 0600.
+
+ """
+ if name == 'stdout':
+ return sys.stdout
+ elif name == 'stderr':
+ return sys.stderr
+ elif name == 'stdin':
+ return sys.stdin
+ else:
+ if encoding is not None:
+ import codecs
+ f = codecs.open(name, mode, encoding)
+ else:
+ f = open(name, mode)
+ if 'w' in mode:
+ os.chmod(name, 0600)
+ return f
+
+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
+
+def abspath(path,
+
+ expandvars=os.path.expandvars,expanduser=os.path.expanduser,
+ join=os.path.join,getcwd=os.getcwd):
+
+ """ Return the corresponding absolute path for path.
+
+ path is expanded in the usual shell ways before
+ joining it with the current working directory.
+
+ """
+ try:
+ path = expandvars(path)
+ except AttributeError:
+ pass
+ try:
+ path = expanduser(path)
+ except AttributeError:
+ pass
+ return join(getcwd(), path)
+
+### 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__
+
+ # Apply file globbing ?
+ globbing = 1
+
+ # Generate debug output ?
+ debug = 0
+
+ # Generate verbose output ?
+ verbose = 0
+
+ # Internal errors to catch
+ InternalError = exceptions.Exception
+
+ # 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
+ optionlist = None # List of passed options
+
+ 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'
+ print
+ rc = 1
+
+ except self.InternalError:
+ print
+ print '* Internal Error'
+ if self.debug:
+ print
+ traceback.print_exc(20, sys.stdout)
+ elif self.verbose:
+ print ' %s: %s' % sys.exc_info()[:2]
+ print
+ 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
+ # We don't want to catch internal errors:
+ self.InternalError = None
+
+ 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()