comparison venv/lib/python2.7/site-packages/click/parser.py @ 0:d67268158946 draft

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