getopt.py 7.16 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
"""Parser for command line options.
3 4 5 6 7 8

This module helps scripts to parse the command line arguments in
sys.argv.  It supports the same conventions as the Unix getopt()
function (including the special meanings of arguments of the form `-'
and `--').  Long options similar to those supported by GNU software
may be used as well via an optional third argument.  This module
9
provides two functions and an exception:
10 11

getopt() -- Parse command line options
12
gnu_getopt() -- Like getopt(), but allow option and non-option arguments
Tim Peters's avatar
Tim Peters committed
13
to be intermixed.
14 15
GetoptError -- exception (class) raised with 'opt' attribute, which is the
option involved with the exception.
16 17 18
"""

# Long option support added by Lars Wirzenius <liw@iki.fi>.
19
#
20 21
# Gerrit Holl <gerrit@nl.linux.org> moved the string-based exceptions
# to class-based exceptions.
22
#
23
# Peter Åstrand <astrand@lysator.liu.se> added gnu_getopt().
24 25 26 27 28 29 30 31 32 33
#
# TODO for gnu_getopt():
#
# - GNU getopt_long_only mechanism
# - allow the caller to specify ordering
# - RETURN_IN_ORDER option
# - GNU extension with '-' as first character of option string
# - optional arguments, specified by double colons
# - a option string with a W followed by semicolon should
#   treat "-W foo" as "--foo"
34

35
__all__ = ["GetoptError","error","getopt","gnu_getopt"]
Skip Montanaro's avatar
Skip Montanaro committed
36

37 38
import os

39 40 41
class GetoptError(Exception):
    opt = ''
    msg = ''
42
    def __init__(self, msg, opt=''):
43 44 45
        self.msg = msg
        self.opt = opt
        Exception.__init__(self, msg, opt)
46 47 48 49 50

    def __str__(self):
        return self.msg

error = GetoptError # backward compatibility
Guido van Rossum's avatar
Guido van Rossum committed
51

52
def getopt(args, shortopts, longopts = []):
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    """getopt(args, options[, long_options]) -> opts, args

    Parses command line options and parameter list.  args is the
    argument list to be parsed, without the leading reference to the
    running program.  Typically, this means "sys.argv[1:]".  shortopts
    is the string of option letters that the script wants to
    recognize, with options that require an argument followed by a
    colon (i.e., the same format that Unix getopt() uses).  If
    specified, longopts is a list of strings with the names of the
    long options which should be supported.  The leading '--'
    characters should not be included in the option name.  Options
    which require an argument should be followed by an equal sign
    ('=').

    The return value consists of two elements: the first is a list of
    (option, value) pairs; the second is the list of program arguments
    left after the option list was stripped (this is a trailing slice
    of the first argument).  Each option-and-value pair returned has
    the option as its first element, prefixed with a hyphen (e.g.,
    '-x'), and the option argument as its second element, or an empty
    string if the option has no argument.  The options occur in the
    list in the same order in which they were found, thus allowing
    multiple occurrences.  Long and short options may be mixed.

    """

    opts = []
    if type(longopts) == type(""):
        longopts = [longopts]
    else:
        longopts = list(longopts)
Tim Peters's avatar
Tim Peters committed
84
    while args and args[0].startswith('-') and args[0] != '-':
85 86 87
        if args[0] == '--':
            args = args[1:]
            break
Fred Drake's avatar
Fred Drake committed
88
        if args[0].startswith('--'):
89
            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
90
        else:
91
            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
92

93
    return opts, args
94

95 96 97 98 99 100 101 102 103 104 105 106
def gnu_getopt(args, shortopts, longopts = []):
    """getopt(args, options[, long_options]) -> opts, args

    This function works like getopt(), except that GNU style scanning
    mode is used by default. This means that option and non-option
    arguments may be intermixed. The getopt() function stops
    processing options as soon as a non-option argument is
    encountered.

    If the first character of the option string is `+', or if the
    environment variable POSIXLY_CORRECT is set, then option
    processing stops as soon as a non-option argument is encountered.
Tim Peters's avatar
Tim Peters committed
107

108 109 110 111
    """

    opts = []
    prog_args = []
112
    if isinstance(longopts, str):
113 114 115 116 117 118 119
        longopts = [longopts]
    else:
        longopts = list(longopts)

    # Allow options after non-option arguments?
    if shortopts.startswith('+'):
        shortopts = shortopts[1:]
120
        all_options_first = True
121
    elif os.environ.get("POSIXLY_CORRECT"):
122
        all_options_first = True
123
    else:
124
        all_options_first = False
125 126 127 128 129 130 131 132

    while args:
        if args[0] == '--':
            prog_args += args[1:]
            break

        if args[0][:2] == '--':
            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])
133
        elif args[0][:1] == '-' and args[0] != '-':
134 135 136 137 138 139 140 141 142 143 144
            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])
        else:
            if all_options_first:
                prog_args += args
                break
            else:
                prog_args.append(args[0])
                args = args[1:]

    return opts, prog_args

145
def do_longs(opts, opt, longopts, args):
146
    try:
147
        i = opt.index('=')
148
    except ValueError:
149
        optarg = None
Tim Peters's avatar
Tim Peters committed
150 151
    else:
        opt, optarg = opt[:i], opt[i+1:]
152 153 154

    has_arg, opt = long_has_args(opt, longopts)
    if has_arg:
155 156
        if optarg is None:
            if not args:
157
                raise GetoptError('option --%s requires argument' % opt, opt)
158
            optarg, args = args[0], args[1:]
159
    elif optarg:
160
        raise GetoptError('option --%s must not have an argument' % opt, opt)
161 162
    opts.append(('--' + opt, optarg or ''))
    return opts, args
163 164 165 166 167

# Return:
#   has_arg?
#   full option name
def long_has_args(opt, longopts):
168 169
    possibilities = [o for o in longopts if o.startswith(opt)]
    if not possibilities:
Tim Peters's avatar
Tim Peters committed
170 171 172
        raise GetoptError('option --%s not recognized' % opt, opt)
    # Is there an exact match?
    if opt in possibilities:
173
        return False, opt
Tim Peters's avatar
Tim Peters committed
174
    elif opt + '=' in possibilities:
175
        return True, opt
Tim Peters's avatar
Tim Peters committed
176 177 178 179 180 181 182 183 184 185 186
    # No exact match, so better be unique.
    if len(possibilities) > 1:
        # XXX since possibilities contains all valid continuations, might be
        # nice to work them into the error msg
        raise GetoptError('option --%s not a unique prefix' % opt, opt)
    assert len(possibilities) == 1
    unique_match = possibilities[0]
    has_arg = unique_match.endswith('=')
    if has_arg:
        unique_match = unique_match[:-1]
    return has_arg, unique_match
187

188
def do_shorts(opts, optstring, shortopts, args):
189
    while optstring != '':
190 191 192 193
        opt, optstring = optstring[0], optstring[1:]
        if short_has_arg(opt, shortopts):
            if optstring == '':
                if not args:
Fred Drake's avatar
Fred Drake committed
194 195
                    raise GetoptError('option -%s requires argument' % opt,
                                      opt)
196 197 198 199
                optstring, args = args[0], args[1:]
            optarg, optstring = optstring, ''
        else:
            optarg = ''
200 201
        opts.append(('-' + opt, optarg))
    return opts, args
202 203

def short_has_arg(opt, shortopts):
204
    for i in range(len(shortopts)):
205
        if opt == shortopts[i] != ':':
Fred Drake's avatar
Fred Drake committed
206
            return shortopts.startswith(':', i+1)
207
    raise GetoptError('option -%s not recognized' % opt, opt)
208 209

if __name__ == '__main__':
210
    import sys
211
    print(getopt(sys.argv[1:], "a:b", ["alpha=", "beta"]))