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