comparison env/lib/python3.9/site-packages/click/parser.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # -*- coding: utf-8 -*-
2 """
3 This module started out as largely a copy paste from the stdlib's
4 optparse module with the features removed that we do not need from
5 optparse because we implement them in Click on a higher level (for
6 instance type handling, help formatting and a lot more).
7
8 The plan is to remove more and more from here over time.
9
10 The reason this is a different module and not optparse from the stdlib
11 is that there are differences in 2.x and 3.x about the error messages
12 generated and optparse in the stdlib uses gettext for no good reason
13 and might cause us issues.
14
15 Click uses parts of optparse written by Gregory P. Ward and maintained
16 by the Python Software Foundation. This is limited to code in parser.py.
17
18 Copyright 2001-2006 Gregory P. Ward. All rights reserved.
19 Copyright 2002-2006 Python Software Foundation. All rights reserved.
20 """
21 import re
22 from collections import deque
23
24 from .exceptions import BadArgumentUsage
25 from .exceptions import BadOptionUsage
26 from .exceptions import NoSuchOption
27 from .exceptions import UsageError
28
29
30 def _unpack_args(args, nargs_spec):
31 """Given an iterable of arguments and an iterable of nargs specifications,
32 it returns a tuple with all the unpacked arguments at the first index
33 and all remaining arguments as the second.
34
35 The nargs specification is the number of arguments that should be consumed
36 or `-1` to indicate that this position should eat up all the remainders.
37
38 Missing items are filled with `None`.
39 """
40 args = deque(args)
41 nargs_spec = deque(nargs_spec)
42 rv = []
43 spos = None
44
45 def _fetch(c):
46 try:
47 if spos is None:
48 return c.popleft()
49 else:
50 return c.pop()
51 except IndexError:
52 return None
53
54 while nargs_spec:
55 nargs = _fetch(nargs_spec)
56 if nargs == 1:
57 rv.append(_fetch(args))
58 elif nargs > 1:
59 x = [_fetch(args) for _ in range(nargs)]
60 # If we're reversed, we're pulling in the arguments in reverse,
61 # so we need to turn them around.
62 if spos is not None:
63 x.reverse()
64 rv.append(tuple(x))
65 elif nargs < 0:
66 if spos is not None:
67 raise TypeError("Cannot have two nargs < 0")
68 spos = len(rv)
69 rv.append(None)
70
71 # spos is the position of the wildcard (star). If it's not `None`,
72 # we fill it with the remainder.
73 if spos is not None:
74 rv[spos] = tuple(args)
75 args = []
76 rv[spos + 1 :] = reversed(rv[spos + 1 :])
77
78 return tuple(rv), list(args)
79
80
81 def _error_opt_args(nargs, opt):
82 if nargs == 1:
83 raise BadOptionUsage(opt, "{} option requires an argument".format(opt))
84 raise BadOptionUsage(opt, "{} option requires {} arguments".format(opt, nargs))
85
86
87 def split_opt(opt):
88 first = opt[:1]
89 if first.isalnum():
90 return "", opt
91 if opt[1:2] == first:
92 return opt[:2], opt[2:]
93 return first, opt[1:]
94
95
96 def normalize_opt(opt, ctx):
97 if ctx is None or ctx.token_normalize_func is None:
98 return opt
99 prefix, opt = split_opt(opt)
100 return prefix + ctx.token_normalize_func(opt)
101
102
103 def split_arg_string(string):
104 """Given an argument string this attempts to split it into small parts."""
105 rv = []
106 for match in re.finditer(
107 r"('([^'\\]*(?:\\.[^'\\]*)*)'|\"([^\"\\]*(?:\\.[^\"\\]*)*)\"|\S+)\s*",
108 string,
109 re.S,
110 ):
111 arg = match.group().strip()
112 if arg[:1] == arg[-1:] and arg[:1] in "\"'":
113 arg = arg[1:-1].encode("ascii", "backslashreplace").decode("unicode-escape")
114 try:
115 arg = type(string)(arg)
116 except UnicodeError:
117 pass
118 rv.append(arg)
119 return rv
120
121
122 class Option(object):
123 def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None):
124 self._short_opts = []
125 self._long_opts = []
126 self.prefixes = set()
127
128 for opt in opts:
129 prefix, value = split_opt(opt)
130 if not prefix:
131 raise ValueError("Invalid start character for option ({})".format(opt))
132 self.prefixes.add(prefix[0])
133 if len(prefix) == 1 and len(value) == 1:
134 self._short_opts.append(opt)
135 else:
136 self._long_opts.append(opt)
137 self.prefixes.add(prefix)
138
139 if action is None:
140 action = "store"
141
142 self.dest = dest
143 self.action = action
144 self.nargs = nargs
145 self.const = const
146 self.obj = obj
147
148 @property
149 def takes_value(self):
150 return self.action in ("store", "append")
151
152 def process(self, value, state):
153 if self.action == "store":
154 state.opts[self.dest] = value
155 elif self.action == "store_const":
156 state.opts[self.dest] = self.const
157 elif self.action == "append":
158 state.opts.setdefault(self.dest, []).append(value)
159 elif self.action == "append_const":
160 state.opts.setdefault(self.dest, []).append(self.const)
161 elif self.action == "count":
162 state.opts[self.dest] = state.opts.get(self.dest, 0) + 1
163 else:
164 raise ValueError("unknown action '{}'".format(self.action))
165 state.order.append(self.obj)
166
167
168 class Argument(object):
169 def __init__(self, dest, nargs=1, obj=None):
170 self.dest = dest
171 self.nargs = nargs
172 self.obj = obj
173
174 def process(self, value, state):
175 if self.nargs > 1:
176 holes = sum(1 for x in value if x is None)
177 if holes == len(value):
178 value = None
179 elif holes != 0:
180 raise BadArgumentUsage(
181 "argument {} takes {} values".format(self.dest, self.nargs)
182 )
183 state.opts[self.dest] = value
184 state.order.append(self.obj)
185
186
187 class ParsingState(object):
188 def __init__(self, rargs):
189 self.opts = {}
190 self.largs = []
191 self.rargs = rargs
192 self.order = []
193
194
195 class OptionParser(object):
196 """The option parser is an internal class that is ultimately used to
197 parse options and arguments. It's modelled after optparse and brings
198 a similar but vastly simplified API. It should generally not be used
199 directly as the high level Click classes wrap it for you.
200
201 It's not nearly as extensible as optparse or argparse as it does not
202 implement features that are implemented on a higher level (such as
203 types or defaults).
204
205 :param ctx: optionally the :class:`~click.Context` where this parser
206 should go with.
207 """
208
209 def __init__(self, ctx=None):
210 #: The :class:`~click.Context` for this parser. This might be
211 #: `None` for some advanced use cases.
212 self.ctx = ctx
213 #: This controls how the parser deals with interspersed arguments.
214 #: If this is set to `False`, the parser will stop on the first
215 #: non-option. Click uses this to implement nested subcommands
216 #: safely.
217 self.allow_interspersed_args = True
218 #: This tells the parser how to deal with unknown options. By
219 #: default it will error out (which is sensible), but there is a
220 #: second mode where it will ignore it and continue processing
221 #: after shifting all the unknown options into the resulting args.
222 self.ignore_unknown_options = False
223 if ctx is not None:
224 self.allow_interspersed_args = ctx.allow_interspersed_args
225 self.ignore_unknown_options = ctx.ignore_unknown_options
226 self._short_opt = {}
227 self._long_opt = {}
228 self._opt_prefixes = {"-", "--"}
229 self._args = []
230
231 def add_option(self, opts, dest, action=None, nargs=1, const=None, obj=None):
232 """Adds a new option named `dest` to the parser. The destination
233 is not inferred (unlike with optparse) and needs to be explicitly
234 provided. Action can be any of ``store``, ``store_const``,
235 ``append``, ``appnd_const`` or ``count``.
236
237 The `obj` can be used to identify the option in the order list
238 that is returned from the parser.
239 """
240 if obj is None:
241 obj = dest
242 opts = [normalize_opt(opt, self.ctx) for opt in opts]
243 option = Option(opts, dest, action=action, nargs=nargs, const=const, obj=obj)
244 self._opt_prefixes.update(option.prefixes)
245 for opt in option._short_opts:
246 self._short_opt[opt] = option
247 for opt in option._long_opts:
248 self._long_opt[opt] = option
249
250 def add_argument(self, dest, nargs=1, obj=None):
251 """Adds a positional argument named `dest` to the parser.
252
253 The `obj` can be used to identify the option in the order list
254 that is returned from the parser.
255 """
256 if obj is None:
257 obj = dest
258 self._args.append(Argument(dest=dest, nargs=nargs, obj=obj))
259
260 def parse_args(self, args):
261 """Parses positional arguments and returns ``(values, args, order)``
262 for the parsed options and arguments as well as the leftover
263 arguments if there are any. The order is a list of objects as they
264 appear on the command line. If arguments appear multiple times they
265 will be memorized multiple times as well.
266 """
267 state = ParsingState(args)
268 try:
269 self._process_args_for_options(state)
270 self._process_args_for_args(state)
271 except UsageError:
272 if self.ctx is None or not self.ctx.resilient_parsing:
273 raise
274 return state.opts, state.largs, state.order
275
276 def _process_args_for_args(self, state):
277 pargs, args = _unpack_args(
278 state.largs + state.rargs, [x.nargs for x in self._args]
279 )
280
281 for idx, arg in enumerate(self._args):
282 arg.process(pargs[idx], state)
283
284 state.largs = args
285 state.rargs = []
286
287 def _process_args_for_options(self, state):
288 while state.rargs:
289 arg = state.rargs.pop(0)
290 arglen = len(arg)
291 # Double dashes always handled explicitly regardless of what
292 # prefixes are valid.
293 if arg == "--":
294 return
295 elif arg[:1] in self._opt_prefixes and arglen > 1:
296 self._process_opts(arg, state)
297 elif self.allow_interspersed_args:
298 state.largs.append(arg)
299 else:
300 state.rargs.insert(0, arg)
301 return
302
303 # Say this is the original argument list:
304 # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
305 # ^
306 # (we are about to process arg(i)).
307 #
308 # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
309 # [arg0, ..., arg(i-1)] (any options and their arguments will have
310 # been removed from largs).
311 #
312 # The while loop will usually consume 1 or more arguments per pass.
313 # If it consumes 1 (eg. arg is an option that takes no arguments),
314 # then after _process_arg() is done the situation is:
315 #
316 # largs = subset of [arg0, ..., arg(i)]
317 # rargs = [arg(i+1), ..., arg(N-1)]
318 #
319 # If allow_interspersed_args is false, largs will always be
320 # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
321 # not a very interesting subset!
322
323 def _match_long_opt(self, opt, explicit_value, state):
324 if opt not in self._long_opt:
325 possibilities = [word for word in self._long_opt if word.startswith(opt)]
326 raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx)
327
328 option = self._long_opt[opt]
329 if option.takes_value:
330 # At this point it's safe to modify rargs by injecting the
331 # explicit value, because no exception is raised in this
332 # branch. This means that the inserted value will be fully
333 # consumed.
334 if explicit_value is not None:
335 state.rargs.insert(0, explicit_value)
336
337 nargs = option.nargs
338 if len(state.rargs) < nargs:
339 _error_opt_args(nargs, opt)
340 elif nargs == 1:
341 value = state.rargs.pop(0)
342 else:
343 value = tuple(state.rargs[:nargs])
344 del state.rargs[:nargs]
345
346 elif explicit_value is not None:
347 raise BadOptionUsage(opt, "{} option does not take a value".format(opt))
348
349 else:
350 value = None
351
352 option.process(value, state)
353
354 def _match_short_opt(self, arg, state):
355 stop = False
356 i = 1
357 prefix = arg[0]
358 unknown_options = []
359
360 for ch in arg[1:]:
361 opt = normalize_opt(prefix + ch, self.ctx)
362 option = self._short_opt.get(opt)
363 i += 1
364
365 if not option:
366 if self.ignore_unknown_options:
367 unknown_options.append(ch)
368 continue
369 raise NoSuchOption(opt, ctx=self.ctx)
370 if option.takes_value:
371 # Any characters left in arg? Pretend they're the
372 # next arg, and stop consuming characters of arg.
373 if i < len(arg):
374 state.rargs.insert(0, arg[i:])
375 stop = True
376
377 nargs = option.nargs
378 if len(state.rargs) < nargs:
379 _error_opt_args(nargs, opt)
380 elif nargs == 1:
381 value = state.rargs.pop(0)
382 else:
383 value = tuple(state.rargs[:nargs])
384 del state.rargs[:nargs]
385
386 else:
387 value = None
388
389 option.process(value, state)
390
391 if stop:
392 break
393
394 # If we got any unknown options we re-combinate the string of the
395 # remaining options and re-attach the prefix, then report that
396 # to the state as new larg. This way there is basic combinatorics
397 # that can be achieved while still ignoring unknown arguments.
398 if self.ignore_unknown_options and unknown_options:
399 state.largs.append("{}{}".format(prefix, "".join(unknown_options)))
400
401 def _process_opts(self, arg, state):
402 explicit_value = None
403 # Long option handling happens in two parts. The first part is
404 # supporting explicitly attached values. In any case, we will try
405 # to long match the option first.
406 if "=" in arg:
407 long_opt, explicit_value = arg.split("=", 1)
408 else:
409 long_opt = arg
410 norm_long_opt = normalize_opt(long_opt, self.ctx)
411
412 # At this point we will match the (assumed) long option through
413 # the long option matching code. Note that this allows options
414 # like "-foo" to be matched as long options.
415 try:
416 self._match_long_opt(norm_long_opt, explicit_value, state)
417 except NoSuchOption:
418 # At this point the long option matching failed, and we need
419 # to try with short options. However there is a special rule
420 # which says, that if we have a two character options prefix
421 # (applies to "--foo" for instance), we do not dispatch to the
422 # short option code and will instead raise the no option
423 # error.
424 if arg[:2] not in self._opt_prefixes:
425 return self._match_short_opt(arg, state)
426 if not self.ignore_unknown_options:
427 raise
428 state.largs.append(arg)