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