Home | History | Annotate | Download | only in Lib
      1 """Parser for command line options.
      2 
      3 This module helps scripts to parse the command line arguments in
      4 sys.argv.  It supports the same conventions as the Unix getopt()
      5 function (including the special meanings of arguments of the form `-'
      6 and `--').  Long options similar to those supported by GNU software
      7 may be used as well via an optional third argument.  This module
      8 provides two functions and an exception:
      9 
     10 getopt() -- Parse command line options
     11 gnu_getopt() -- Like getopt(), but allow option and non-option arguments
     12 to be intermixed.
     13 GetoptError -- exception (class) raised with 'opt' attribute, which is the
     14 option involved with the exception.
     15 """
     16 
     17 # Long option support added by Lars Wirzenius <liw (at] iki.fi>.
     18 #
     19 # Gerrit Holl <gerrit (at] nl.linux.org> moved the string-based exceptions
     20 # to class-based exceptions.
     21 #
     22 # Peter strand <astrand (at] lysator.liu.se> added gnu_getopt().
     23 #
     24 # TODO for gnu_getopt():
     25 #
     26 # - GNU getopt_long_only mechanism
     27 # - allow the caller to specify ordering
     28 # - RETURN_IN_ORDER option
     29 # - GNU extension with '-' as first character of option string
     30 # - optional arguments, specified by double colons
     31 # - an option string with a W followed by semicolon should
     32 #   treat "-W foo" as "--foo"
     33 
     34 __all__ = ["GetoptError","error","getopt","gnu_getopt"]
     35 
     36 import os
     37 try:
     38     from gettext import gettext as _
     39 except ImportError:
     40     # Bootstrapping Python: gettext's dependencies not built yet
     41     def _(s): return s
     42 
     43 class GetoptError(Exception):
     44     opt = ''
     45     msg = ''
     46     def __init__(self, msg, opt=''):
     47         self.msg = msg
     48         self.opt = opt
     49         Exception.__init__(self, msg, opt)
     50 
     51     def __str__(self):
     52         return self.msg
     53 
     54 error = GetoptError # backward compatibility
     55 
     56 def getopt(args, shortopts, longopts = []):
     57     """getopt(args, options[, long_options]) -> opts, args
     58 
     59     Parses command line options and parameter list.  args is the
     60     argument list to be parsed, without the leading reference to the
     61     running program.  Typically, this means "sys.argv[1:]".  shortopts
     62     is the string of option letters that the script wants to
     63     recognize, with options that require an argument followed by a
     64     colon (i.e., the same format that Unix getopt() uses).  If
     65     specified, longopts is a list of strings with the names of the
     66     long options which should be supported.  The leading '--'
     67     characters should not be included in the option name.  Options
     68     which require an argument should be followed by an equal sign
     69     ('=').
     70 
     71     The return value consists of two elements: the first is a list of
     72     (option, value) pairs; the second is the list of program arguments
     73     left after the option list was stripped (this is a trailing slice
     74     of the first argument).  Each option-and-value pair returned has
     75     the option as its first element, prefixed with a hyphen (e.g.,
     76     '-x'), and the option argument as its second element, or an empty
     77     string if the option has no argument.  The options occur in the
     78     list in the same order in which they were found, thus allowing
     79     multiple occurrences.  Long and short options may be mixed.
     80 
     81     """
     82 
     83     opts = []
     84     if type(longopts) == type(""):
     85         longopts = [longopts]
     86     else:
     87         longopts = list(longopts)
     88     while args and args[0].startswith('-') and args[0] != '-':
     89         if args[0] == '--':
     90             args = args[1:]
     91             break
     92         if args[0].startswith('--'):
     93             opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
     94         else:
     95             opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
     96 
     97     return opts, args
     98 
     99 def gnu_getopt(args, shortopts, longopts = []):
    100     """getopt(args, options[, long_options]) -> opts, args
    101 
    102     This function works like getopt(), except that GNU style scanning
    103     mode is used by default. This means that option and non-option
    104     arguments may be intermixed. The getopt() function stops
    105     processing options as soon as a non-option argument is
    106     encountered.
    107 
    108     If the first character of the option string is `+', or if the
    109     environment variable POSIXLY_CORRECT is set, then option
    110     processing stops as soon as a non-option argument is encountered.
    111 
    112     """
    113 
    114     opts = []
    115     prog_args = []
    116     if isinstance(longopts, str):
    117         longopts = [longopts]
    118     else:
    119         longopts = list(longopts)
    120 
    121     # Allow options after non-option arguments?
    122     if shortopts.startswith('+'):
    123         shortopts = shortopts[1:]
    124         all_options_first = True
    125     elif os.environ.get("POSIXLY_CORRECT"):
    126         all_options_first = True
    127     else:
    128         all_options_first = False
    129 
    130     while args:
    131         if args[0] == '--':
    132             prog_args += args[1:]
    133             break
    134 
    135         if args[0][:2] == '--':
    136             opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
    137         elif args[0][:1] == '-' and args[0] != '-':
    138             opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
    139         else:
    140             if all_options_first:
    141                 prog_args += args
    142                 break
    143             else:
    144                 prog_args.append(args[0])
    145                 args = args[1:]
    146 
    147     return opts, prog_args
    148 
    149 def do_longs(opts, opt, longopts, args):
    150     try:
    151         i = opt.index('=')
    152     except ValueError:
    153         optarg = None
    154     else:
    155         opt, optarg = opt[:i], opt[i+1:]
    156 
    157     has_arg, opt = long_has_args(opt, longopts)
    158     if has_arg:
    159         if optarg is None:
    160             if not args:
    161                 raise GetoptError(_('option --%s requires argument') % opt, opt)
    162             optarg, args = args[0], args[1:]
    163     elif optarg is not None:
    164         raise GetoptError(_('option --%s must not have an argument') % opt, opt)
    165     opts.append(('--' + opt, optarg or ''))
    166     return opts, args
    167 
    168 # Return:
    169 #   has_arg?
    170 #   full option name
    171 def long_has_args(opt, longopts):
    172     possibilities = [o for o in longopts if o.startswith(opt)]
    173     if not possibilities:
    174         raise GetoptError(_('option --%s not recognized') % opt, opt)
    175     # Is there an exact match?
    176     if opt in possibilities:
    177         return False, opt
    178     elif opt + '=' in possibilities:
    179         return True, opt
    180     # No exact match, so better be unique.
    181     if len(possibilities) > 1:
    182         # XXX since possibilities contains all valid continuations, might be
    183         # nice to work them into the error msg
    184         raise GetoptError(_('option --%s not a unique prefix') % opt, opt)
    185     assert len(possibilities) == 1
    186     unique_match = possibilities[0]
    187     has_arg = unique_match.endswith('=')
    188     if has_arg:
    189         unique_match = unique_match[:-1]
    190     return has_arg, unique_match
    191 
    192 def do_shorts(opts, optstring, shortopts, args):
    193     while optstring != '':
    194         opt, optstring = optstring[0], optstring[1:]
    195         if short_has_arg(opt, shortopts):
    196             if optstring == '':
    197                 if not args:
    198                     raise GetoptError(_('option -%s requires argument') % opt,
    199                                       opt)
    200                 optstring, args = args[0], args[1:]
    201             optarg, optstring = optstring, ''
    202         else:
    203             optarg = ''
    204         opts.append(('-' + opt, optarg))
    205     return opts, args
    206 
    207 def short_has_arg(opt, shortopts):
    208     for i in range(len(shortopts)):
    209         if opt == shortopts[i] != ':':
    210             return shortopts.startswith(':', i+1)
    211     raise GetoptError(_('option -%s not recognized') % opt, opt)
    212 
    213 if __name__ == '__main__':
    214     import sys
    215     print(getopt(sys.argv[1:], "a:b", ["alpha=", "beta"]))
    216