##// END OF EJS Templates
py3: drop b'' from error message of fancyopts
Yuya Nishihara -
r37483:39e5e346 default
parent child Browse files
Show More
@@ -1,378 +1,378
1 1 # fancyopts.py - better command line parsing
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import abc
11 11 import functools
12 12
13 13 from .i18n import _
14 14 from . import (
15 15 error,
16 16 pycompat,
17 17 )
18 18
19 19 # Set of flags to not apply boolean negation logic on
20 20 nevernegate = {
21 21 # avoid --no-noninteractive
22 22 'noninteractive',
23 23 # These two flags are special because they cause hg to do one
24 24 # thing and then exit, and so aren't suitable for use in things
25 25 # like aliases anyway.
26 26 'help',
27 27 'version',
28 28 }
29 29
30 30 def _earlyoptarg(arg, shortlist, namelist):
31 31 """Check if the given arg is a valid unabbreviated option
32 32
33 33 Returns (flag_str, has_embedded_value?, embedded_value, takes_value?)
34 34
35 35 >>> def opt(arg):
36 36 ... return _earlyoptarg(arg, b'R:q', [b'cwd=', b'debugger'])
37 37
38 38 long form:
39 39
40 40 >>> opt(b'--cwd')
41 41 ('--cwd', False, '', True)
42 42 >>> opt(b'--cwd=')
43 43 ('--cwd', True, '', True)
44 44 >>> opt(b'--cwd=foo')
45 45 ('--cwd', True, 'foo', True)
46 46 >>> opt(b'--debugger')
47 47 ('--debugger', False, '', False)
48 48 >>> opt(b'--debugger=') # invalid but parsable
49 49 ('--debugger', True, '', False)
50 50
51 51 short form:
52 52
53 53 >>> opt(b'-R')
54 54 ('-R', False, '', True)
55 55 >>> opt(b'-Rfoo')
56 56 ('-R', True, 'foo', True)
57 57 >>> opt(b'-q')
58 58 ('-q', False, '', False)
59 59 >>> opt(b'-qfoo') # invalid but parsable
60 60 ('-q', True, 'foo', False)
61 61
62 62 unknown or invalid:
63 63
64 64 >>> opt(b'--unknown')
65 65 ('', False, '', False)
66 66 >>> opt(b'-u')
67 67 ('', False, '', False)
68 68 >>> opt(b'-ufoo')
69 69 ('', False, '', False)
70 70 >>> opt(b'--')
71 71 ('', False, '', False)
72 72 >>> opt(b'-')
73 73 ('', False, '', False)
74 74 >>> opt(b'-:')
75 75 ('', False, '', False)
76 76 >>> opt(b'-:foo')
77 77 ('', False, '', False)
78 78 """
79 79 if arg.startswith('--'):
80 80 flag, eq, val = arg.partition('=')
81 81 if flag[2:] in namelist:
82 82 return flag, bool(eq), val, False
83 83 if flag[2:] + '=' in namelist:
84 84 return flag, bool(eq), val, True
85 85 elif arg.startswith('-') and arg != '-' and not arg.startswith('-:'):
86 86 flag, val = arg[:2], arg[2:]
87 87 i = shortlist.find(flag[1:])
88 88 if i >= 0:
89 89 return flag, bool(val), val, shortlist.startswith(':', i + 1)
90 90 return '', False, '', False
91 91
92 92 def earlygetopt(args, shortlist, namelist, gnu=False, keepsep=False):
93 93 """Parse options like getopt, but ignores unknown options and abbreviated
94 94 forms
95 95
96 96 If gnu=False, this stops processing options as soon as a non/unknown-option
97 97 argument is encountered. Otherwise, option and non-option arguments may be
98 98 intermixed, and unknown-option arguments are taken as non-option.
99 99
100 100 If keepsep=True, '--' won't be removed from the list of arguments left.
101 101 This is useful for stripping early options from a full command arguments.
102 102
103 103 >>> def get(args, gnu=False, keepsep=False):
104 104 ... return earlygetopt(args, b'R:q', [b'cwd=', b'debugger'],
105 105 ... gnu=gnu, keepsep=keepsep)
106 106
107 107 default parsing rules for early options:
108 108
109 109 >>> get([b'x', b'--cwd', b'foo', b'-Rbar', b'-q', b'y'], gnu=True)
110 110 ([('--cwd', 'foo'), ('-R', 'bar'), ('-q', '')], ['x', 'y'])
111 111 >>> get([b'x', b'--cwd=foo', b'y', b'-R', b'bar', b'--debugger'], gnu=True)
112 112 ([('--cwd', 'foo'), ('-R', 'bar'), ('--debugger', '')], ['x', 'y'])
113 113 >>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=True)
114 114 ([('--cwd', 'foo')], ['--unknown', '--debugger'])
115 115
116 116 restricted parsing rules (early options must come first):
117 117
118 118 >>> get([b'--cwd', b'foo', b'-Rbar', b'x', b'-q', b'y'], gnu=False)
119 119 ([('--cwd', 'foo'), ('-R', 'bar')], ['x', '-q', 'y'])
120 120 >>> get([b'--cwd=foo', b'x', b'y', b'-R', b'bar', b'--debugger'], gnu=False)
121 121 ([('--cwd', 'foo')], ['x', 'y', '-R', 'bar', '--debugger'])
122 122 >>> get([b'--unknown', b'--cwd=foo', b'--', '--debugger'], gnu=False)
123 123 ([], ['--unknown', '--cwd=foo', '--', '--debugger'])
124 124
125 125 stripping early options (without loosing '--'):
126 126
127 127 >>> get([b'x', b'-Rbar', b'--', '--debugger'], gnu=True, keepsep=True)[1]
128 128 ['x', '--', '--debugger']
129 129
130 130 last argument:
131 131
132 132 >>> get([b'--cwd'])
133 133 ([], ['--cwd'])
134 134 >>> get([b'--cwd=foo'])
135 135 ([('--cwd', 'foo')], [])
136 136 >>> get([b'-R'])
137 137 ([], ['-R'])
138 138 >>> get([b'-Rbar'])
139 139 ([('-R', 'bar')], [])
140 140 >>> get([b'-q'])
141 141 ([('-q', '')], [])
142 142 >>> get([b'-q', b'--'])
143 143 ([('-q', '')], [])
144 144
145 145 '--' may be a value:
146 146
147 147 >>> get([b'-R', b'--', b'x'])
148 148 ([('-R', '--')], ['x'])
149 149 >>> get([b'--cwd', b'--', b'x'])
150 150 ([('--cwd', '--')], ['x'])
151 151
152 152 value passed to bool options:
153 153
154 154 >>> get([b'--debugger=foo', b'x'])
155 155 ([], ['--debugger=foo', 'x'])
156 156 >>> get([b'-qfoo', b'x'])
157 157 ([], ['-qfoo', 'x'])
158 158
159 159 short option isn't separated with '=':
160 160
161 161 >>> get([b'-R=bar'])
162 162 ([('-R', '=bar')], [])
163 163
164 164 ':' may be in shortlist, but shouldn't be taken as an option letter:
165 165
166 166 >>> get([b'-:', b'y'])
167 167 ([], ['-:', 'y'])
168 168
169 169 '-' is a valid non-option argument:
170 170
171 171 >>> get([b'-', b'y'])
172 172 ([], ['-', 'y'])
173 173 """
174 174 parsedopts = []
175 175 parsedargs = []
176 176 pos = 0
177 177 while pos < len(args):
178 178 arg = args[pos]
179 179 if arg == '--':
180 180 pos += not keepsep
181 181 break
182 182 flag, hasval, val, takeval = _earlyoptarg(arg, shortlist, namelist)
183 183 if not hasval and takeval and pos + 1 >= len(args):
184 184 # missing last argument
185 185 break
186 186 if not flag or hasval and not takeval:
187 187 # non-option argument or -b/--bool=INVALID_VALUE
188 188 if gnu:
189 189 parsedargs.append(arg)
190 190 pos += 1
191 191 else:
192 192 break
193 193 elif hasval == takeval:
194 194 # -b/--bool or -s/--str=VALUE
195 195 parsedopts.append((flag, val))
196 196 pos += 1
197 197 else:
198 198 # -s/--str VALUE
199 199 parsedopts.append((flag, args[pos + 1]))
200 200 pos += 2
201 201
202 202 parsedargs.extend(args[pos:])
203 203 return parsedopts, parsedargs
204 204
205 205 class customopt(object):
206 206 """Manage defaults and mutations for any type of opt."""
207 207
208 208 __metaclass__ = abc.ABCMeta
209 209
210 210 def __init__(self, defaultvalue):
211 211 self._defaultvalue = defaultvalue
212 212
213 213 def _isboolopt(self):
214 214 return False
215 215
216 216 def getdefaultvalue(self):
217 217 """Returns the default value for this opt.
218 218
219 219 Subclasses should override this to return a new value if the value type
220 220 is mutable."""
221 221 return self._defaultvalue
222 222
223 223 @abc.abstractmethod
224 224 def newstate(self, oldstate, newparam, abort):
225 225 """Adds newparam to oldstate and returns the new state.
226 226
227 227 On failure, abort can be called with a string error message."""
228 228
229 229 class _simpleopt(customopt):
230 230 def _isboolopt(self):
231 231 return isinstance(self._defaultvalue, (bool, type(None)))
232 232
233 233 def newstate(self, oldstate, newparam, abort):
234 234 return newparam
235 235
236 236 class _callableopt(customopt):
237 237 def __init__(self, callablefn):
238 238 self.callablefn = callablefn
239 239 super(_callableopt, self).__init__(None)
240 240
241 241 def newstate(self, oldstate, newparam, abort):
242 242 return self.callablefn(newparam)
243 243
244 244 class _listopt(customopt):
245 245 def getdefaultvalue(self):
246 246 return self._defaultvalue[:]
247 247
248 248 def newstate(self, oldstate, newparam, abort):
249 249 oldstate.append(newparam)
250 250 return oldstate
251 251
252 252 class _intopt(customopt):
253 253 def newstate(self, oldstate, newparam, abort):
254 254 try:
255 255 return int(newparam)
256 256 except ValueError:
257 257 abort(_('expected int'))
258 258
259 259 def _defaultopt(default):
260 260 """Returns a default opt implementation, given a default value."""
261 261
262 262 if isinstance(default, customopt):
263 263 return default
264 264 elif callable(default):
265 265 return _callableopt(default)
266 266 elif isinstance(default, list):
267 267 return _listopt(default[:])
268 268 elif type(default) is type(1):
269 269 return _intopt(default)
270 270 else:
271 271 return _simpleopt(default)
272 272
273 273 def fancyopts(args, options, state, gnu=False, early=False, optaliases=None):
274 274 """
275 275 read args, parse options, and store options in state
276 276
277 277 each option is a tuple of:
278 278
279 279 short option or ''
280 280 long option
281 281 default value
282 282 description
283 283 option value label(optional)
284 284
285 285 option types include:
286 286
287 287 boolean or none - option sets variable in state to true
288 288 string - parameter string is stored in state
289 289 list - parameter string is added to a list
290 290 integer - parameter strings is stored as int
291 291 function - call function with parameter
292 292 customopt - subclass of 'customopt'
293 293
294 294 optaliases is a mapping from a canonical option name to a list of
295 295 additional long options. This exists for preserving backward compatibility
296 296 of early options. If we want to use it extensively, please consider moving
297 297 the functionality to the options table (e.g separate long options by '|'.)
298 298
299 299 non-option args are returned
300 300 """
301 301 if optaliases is None:
302 302 optaliases = {}
303 303 namelist = []
304 304 shortlist = ''
305 305 argmap = {}
306 306 defmap = {}
307 307 negations = {}
308 308 alllong = set(o[1] for o in options)
309 309
310 310 for option in options:
311 311 if len(option) == 5:
312 312 short, name, default, comment, dummy = option
313 313 else:
314 314 short, name, default, comment = option
315 315 # convert opts to getopt format
316 316 onames = [name]
317 317 onames.extend(optaliases.get(name, []))
318 318 name = name.replace('-', '_')
319 319
320 320 argmap['-' + short] = name
321 321 for n in onames:
322 322 argmap['--' + n] = name
323 323 defmap[name] = _defaultopt(default)
324 324
325 325 # copy defaults to state
326 326 state[name] = defmap[name].getdefaultvalue()
327 327
328 328 # does it take a parameter?
329 329 if not defmap[name]._isboolopt():
330 330 if short:
331 331 short += ':'
332 332 onames = [n + '=' for n in onames]
333 333 elif name not in nevernegate:
334 334 for n in onames:
335 335 if n.startswith('no-'):
336 336 insert = n[3:]
337 337 else:
338 338 insert = 'no-' + n
339 339 # backout (as a practical example) has both --commit and
340 340 # --no-commit options, so we don't want to allow the
341 341 # negations of those flags.
342 342 if insert not in alllong:
343 343 assert ('--' + n) not in negations
344 344 negations['--' + insert] = '--' + n
345 345 namelist.append(insert)
346 346 if short:
347 347 shortlist += short
348 348 if name:
349 349 namelist.extend(onames)
350 350
351 351 # parse arguments
352 352 if early:
353 353 parse = functools.partial(earlygetopt, gnu=gnu)
354 354 elif gnu:
355 355 parse = pycompat.gnugetoptb
356 356 else:
357 357 parse = pycompat.getoptb
358 358 opts, args = parse(args, shortlist, namelist)
359 359
360 360 # transfer result to state
361 361 for opt, val in opts:
362 362 boolval = True
363 363 negation = negations.get(opt, False)
364 364 if negation:
365 365 opt = negation
366 366 boolval = False
367 367 name = argmap[opt]
368 368 obj = defmap[name]
369 369 if obj._isboolopt():
370 370 state[name] = boolval
371 371 else:
372 372 def abort(s):
373 raise error.Abort(
374 _('invalid value %r for option %s, %s') % (val, opt, s))
373 raise error.Abort(_('invalid value %r for option %s, %s')
374 % (pycompat.maybebytestr(val), opt, s))
375 375 state[name] = defmap[name].newstate(state[name], val, abort)
376 376
377 377 # return unparsed args
378 378 return args
General Comments 0
You need to be logged in to leave comments. Login now