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 Åstrand <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# - 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
36import os
37try:
38    from gettext import gettext as _
39except ImportError:
40    # Bootstrapping Python: gettext's dependencies not built yet
41    def _(s): return s
42
43class 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
54error = GetoptError # backward compatibility
55
56def 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
99def 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
149def 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
171def 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
192def 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
207def 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
213if __name__ == '__main__':
214    import sys
215    print(getopt(sys.argv[1:], "a:b", ["alpha=", "beta"]))
216