blob: 9d4cab1bac360dda9d49dd8eef258f23dc252a2b [file] [log] [blame]
Olivier Deprezf4ef2d02021-04-20 13:36:24 +02001"""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"]))