mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	See svn.python.org/external/pybench-2.0 for the original import of that version. Note that platform.py was not copied over from pybench-2.0 since it is already part of Python 2.5.
		
			
				
	
	
		
			634 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			634 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
""" 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 (use --debug to display the traceback)'
 | 
						|
            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()
 |