outerbounds 0.3.55rc3__py3-none-any.whl → 0.3.133__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- outerbounds/_vendor/PyYAML.LICENSE +20 -0
- outerbounds/_vendor/__init__.py +0 -0
- outerbounds/_vendor/_yaml/__init__.py +34 -0
- outerbounds/_vendor/click/__init__.py +73 -0
- outerbounds/_vendor/click/_compat.py +626 -0
- outerbounds/_vendor/click/_termui_impl.py +717 -0
- outerbounds/_vendor/click/_textwrap.py +49 -0
- outerbounds/_vendor/click/_winconsole.py +279 -0
- outerbounds/_vendor/click/core.py +2998 -0
- outerbounds/_vendor/click/decorators.py +497 -0
- outerbounds/_vendor/click/exceptions.py +287 -0
- outerbounds/_vendor/click/formatting.py +301 -0
- outerbounds/_vendor/click/globals.py +68 -0
- outerbounds/_vendor/click/parser.py +529 -0
- outerbounds/_vendor/click/py.typed +0 -0
- outerbounds/_vendor/click/shell_completion.py +580 -0
- outerbounds/_vendor/click/termui.py +787 -0
- outerbounds/_vendor/click/testing.py +479 -0
- outerbounds/_vendor/click/types.py +1073 -0
- outerbounds/_vendor/click/utils.py +580 -0
- outerbounds/_vendor/click.LICENSE +28 -0
- outerbounds/_vendor/vendor_any.txt +2 -0
- outerbounds/_vendor/yaml/__init__.py +471 -0
- outerbounds/_vendor/yaml/_yaml.cpython-311-darwin.so +0 -0
- outerbounds/_vendor/yaml/composer.py +146 -0
- outerbounds/_vendor/yaml/constructor.py +862 -0
- outerbounds/_vendor/yaml/cyaml.py +177 -0
- outerbounds/_vendor/yaml/dumper.py +138 -0
- outerbounds/_vendor/yaml/emitter.py +1239 -0
- outerbounds/_vendor/yaml/error.py +94 -0
- outerbounds/_vendor/yaml/events.py +104 -0
- outerbounds/_vendor/yaml/loader.py +62 -0
- outerbounds/_vendor/yaml/nodes.py +51 -0
- outerbounds/_vendor/yaml/parser.py +629 -0
- outerbounds/_vendor/yaml/reader.py +208 -0
- outerbounds/_vendor/yaml/representer.py +378 -0
- outerbounds/_vendor/yaml/resolver.py +245 -0
- outerbounds/_vendor/yaml/scanner.py +1555 -0
- outerbounds/_vendor/yaml/serializer.py +127 -0
- outerbounds/_vendor/yaml/tokens.py +129 -0
- outerbounds/command_groups/apps_cli.py +450 -0
- outerbounds/command_groups/cli.py +9 -5
- outerbounds/command_groups/local_setup_cli.py +249 -33
- outerbounds/command_groups/perimeters_cli.py +231 -33
- outerbounds/command_groups/tutorials_cli.py +111 -0
- outerbounds/command_groups/workstations_cli.py +88 -15
- outerbounds/utils/kubeconfig.py +2 -2
- outerbounds/utils/metaflowconfig.py +111 -21
- outerbounds/utils/schema.py +8 -2
- outerbounds/utils/utils.py +19 -0
- outerbounds/vendor.py +159 -0
- {outerbounds-0.3.55rc3.dist-info → outerbounds-0.3.133.dist-info}/METADATA +17 -6
- outerbounds-0.3.133.dist-info/RECORD +59 -0
- {outerbounds-0.3.55rc3.dist-info → outerbounds-0.3.133.dist-info}/WHEEL +1 -1
- outerbounds-0.3.55rc3.dist-info/RECORD +0 -15
- {outerbounds-0.3.55rc3.dist-info → outerbounds-0.3.133.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,529 @@
|
|
1
|
+
"""
|
2
|
+
This module started out as largely a copy paste from the stdlib's
|
3
|
+
optparse module with the features removed that we do not need from
|
4
|
+
optparse because we implement them in Click on a higher level (for
|
5
|
+
instance type handling, help formatting and a lot more).
|
6
|
+
|
7
|
+
The plan is to remove more and more from here over time.
|
8
|
+
|
9
|
+
The reason this is a different module and not optparse from the stdlib
|
10
|
+
is that there are differences in 2.x and 3.x about the error messages
|
11
|
+
generated and optparse in the stdlib uses gettext for no good reason
|
12
|
+
and might cause us issues.
|
13
|
+
|
14
|
+
Click uses parts of optparse written by Gregory P. Ward and maintained
|
15
|
+
by the Python Software Foundation. This is limited to code in parser.py.
|
16
|
+
|
17
|
+
Copyright 2001-2006 Gregory P. Ward. All rights reserved.
|
18
|
+
Copyright 2002-2006 Python Software Foundation. All rights reserved.
|
19
|
+
"""
|
20
|
+
# This code uses parts of optparse written by Gregory P. Ward and
|
21
|
+
# maintained by the Python Software Foundation.
|
22
|
+
# Copyright 2001-2006 Gregory P. Ward
|
23
|
+
# Copyright 2002-2006 Python Software Foundation
|
24
|
+
import typing as t
|
25
|
+
from collections import deque
|
26
|
+
from gettext import gettext as _
|
27
|
+
from gettext import ngettext
|
28
|
+
|
29
|
+
from .exceptions import BadArgumentUsage
|
30
|
+
from .exceptions import BadOptionUsage
|
31
|
+
from .exceptions import NoSuchOption
|
32
|
+
from .exceptions import UsageError
|
33
|
+
|
34
|
+
if t.TYPE_CHECKING:
|
35
|
+
import typing_extensions as te
|
36
|
+
from .core import Argument as CoreArgument
|
37
|
+
from .core import Context
|
38
|
+
from .core import Option as CoreOption
|
39
|
+
from .core import Parameter as CoreParameter
|
40
|
+
|
41
|
+
V = t.TypeVar("V")
|
42
|
+
|
43
|
+
# Sentinel value that indicates an option was passed as a flag without a
|
44
|
+
# value but is not a flag option. Option.consume_value uses this to
|
45
|
+
# prompt or use the flag_value.
|
46
|
+
_flag_needs_value = object()
|
47
|
+
|
48
|
+
|
49
|
+
def _unpack_args(
|
50
|
+
args: t.Sequence[str], nargs_spec: t.Sequence[int]
|
51
|
+
) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]:
|
52
|
+
"""Given an iterable of arguments and an iterable of nargs specifications,
|
53
|
+
it returns a tuple with all the unpacked arguments at the first index
|
54
|
+
and all remaining arguments as the second.
|
55
|
+
|
56
|
+
The nargs specification is the number of arguments that should be consumed
|
57
|
+
or `-1` to indicate that this position should eat up all the remainders.
|
58
|
+
|
59
|
+
Missing items are filled with `None`.
|
60
|
+
"""
|
61
|
+
args = deque(args)
|
62
|
+
nargs_spec = deque(nargs_spec)
|
63
|
+
rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = []
|
64
|
+
spos: t.Optional[int] = None
|
65
|
+
|
66
|
+
def _fetch(c: "te.Deque[V]") -> t.Optional[V]:
|
67
|
+
try:
|
68
|
+
if spos is None:
|
69
|
+
return c.popleft()
|
70
|
+
else:
|
71
|
+
return c.pop()
|
72
|
+
except IndexError:
|
73
|
+
return None
|
74
|
+
|
75
|
+
while nargs_spec:
|
76
|
+
nargs = _fetch(nargs_spec)
|
77
|
+
|
78
|
+
if nargs is None:
|
79
|
+
continue
|
80
|
+
|
81
|
+
if nargs == 1:
|
82
|
+
rv.append(_fetch(args))
|
83
|
+
elif nargs > 1:
|
84
|
+
x = [_fetch(args) for _ in range(nargs)]
|
85
|
+
|
86
|
+
# If we're reversed, we're pulling in the arguments in reverse,
|
87
|
+
# so we need to turn them around.
|
88
|
+
if spos is not None:
|
89
|
+
x.reverse()
|
90
|
+
|
91
|
+
rv.append(tuple(x))
|
92
|
+
elif nargs < 0:
|
93
|
+
if spos is not None:
|
94
|
+
raise TypeError("Cannot have two nargs < 0")
|
95
|
+
|
96
|
+
spos = len(rv)
|
97
|
+
rv.append(None)
|
98
|
+
|
99
|
+
# spos is the position of the wildcard (star). If it's not `None`,
|
100
|
+
# we fill it with the remainder.
|
101
|
+
if spos is not None:
|
102
|
+
rv[spos] = tuple(args)
|
103
|
+
args = []
|
104
|
+
rv[spos + 1 :] = reversed(rv[spos + 1 :])
|
105
|
+
|
106
|
+
return tuple(rv), list(args)
|
107
|
+
|
108
|
+
|
109
|
+
def split_opt(opt: str) -> t.Tuple[str, str]:
|
110
|
+
first = opt[:1]
|
111
|
+
if first.isalnum():
|
112
|
+
return "", opt
|
113
|
+
if opt[1:2] == first:
|
114
|
+
return opt[:2], opt[2:]
|
115
|
+
return first, opt[1:]
|
116
|
+
|
117
|
+
|
118
|
+
def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str:
|
119
|
+
if ctx is None or ctx.token_normalize_func is None:
|
120
|
+
return opt
|
121
|
+
prefix, opt = split_opt(opt)
|
122
|
+
return f"{prefix}{ctx.token_normalize_func(opt)}"
|
123
|
+
|
124
|
+
|
125
|
+
def split_arg_string(string: str) -> t.List[str]:
|
126
|
+
"""Split an argument string as with :func:`shlex.split`, but don't
|
127
|
+
fail if the string is incomplete. Ignores a missing closing quote or
|
128
|
+
incomplete escape sequence and uses the partial token as-is.
|
129
|
+
|
130
|
+
.. code-block:: python
|
131
|
+
|
132
|
+
split_arg_string("example 'my file")
|
133
|
+
["example", "my file"]
|
134
|
+
|
135
|
+
split_arg_string("example my\\")
|
136
|
+
["example", "my"]
|
137
|
+
|
138
|
+
:param string: String to split.
|
139
|
+
"""
|
140
|
+
import shlex
|
141
|
+
|
142
|
+
lex = shlex.shlex(string, posix=True)
|
143
|
+
lex.whitespace_split = True
|
144
|
+
lex.commenters = ""
|
145
|
+
out = []
|
146
|
+
|
147
|
+
try:
|
148
|
+
for token in lex:
|
149
|
+
out.append(token)
|
150
|
+
except ValueError:
|
151
|
+
# Raised when end-of-string is reached in an invalid state. Use
|
152
|
+
# the partial token as-is. The quote or escape character is in
|
153
|
+
# lex.state, not lex.token.
|
154
|
+
out.append(lex.token)
|
155
|
+
|
156
|
+
return out
|
157
|
+
|
158
|
+
|
159
|
+
class Option:
|
160
|
+
def __init__(
|
161
|
+
self,
|
162
|
+
obj: "CoreOption",
|
163
|
+
opts: t.Sequence[str],
|
164
|
+
dest: t.Optional[str],
|
165
|
+
action: t.Optional[str] = None,
|
166
|
+
nargs: int = 1,
|
167
|
+
const: t.Optional[t.Any] = None,
|
168
|
+
):
|
169
|
+
self._short_opts = []
|
170
|
+
self._long_opts = []
|
171
|
+
self.prefixes = set()
|
172
|
+
|
173
|
+
for opt in opts:
|
174
|
+
prefix, value = split_opt(opt)
|
175
|
+
if not prefix:
|
176
|
+
raise ValueError(f"Invalid start character for option ({opt})")
|
177
|
+
self.prefixes.add(prefix[0])
|
178
|
+
if len(prefix) == 1 and len(value) == 1:
|
179
|
+
self._short_opts.append(opt)
|
180
|
+
else:
|
181
|
+
self._long_opts.append(opt)
|
182
|
+
self.prefixes.add(prefix)
|
183
|
+
|
184
|
+
if action is None:
|
185
|
+
action = "store"
|
186
|
+
|
187
|
+
self.dest = dest
|
188
|
+
self.action = action
|
189
|
+
self.nargs = nargs
|
190
|
+
self.const = const
|
191
|
+
self.obj = obj
|
192
|
+
|
193
|
+
@property
|
194
|
+
def takes_value(self) -> bool:
|
195
|
+
return self.action in ("store", "append")
|
196
|
+
|
197
|
+
def process(self, value: str, state: "ParsingState") -> None:
|
198
|
+
if self.action == "store":
|
199
|
+
state.opts[self.dest] = value # type: ignore
|
200
|
+
elif self.action == "store_const":
|
201
|
+
state.opts[self.dest] = self.const # type: ignore
|
202
|
+
elif self.action == "append":
|
203
|
+
state.opts.setdefault(self.dest, []).append(value) # type: ignore
|
204
|
+
elif self.action == "append_const":
|
205
|
+
state.opts.setdefault(self.dest, []).append(self.const) # type: ignore
|
206
|
+
elif self.action == "count":
|
207
|
+
state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore
|
208
|
+
else:
|
209
|
+
raise ValueError(f"unknown action '{self.action}'")
|
210
|
+
state.order.append(self.obj)
|
211
|
+
|
212
|
+
|
213
|
+
class Argument:
|
214
|
+
def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1):
|
215
|
+
self.dest = dest
|
216
|
+
self.nargs = nargs
|
217
|
+
self.obj = obj
|
218
|
+
|
219
|
+
def process(
|
220
|
+
self,
|
221
|
+
value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]],
|
222
|
+
state: "ParsingState",
|
223
|
+
) -> None:
|
224
|
+
if self.nargs > 1:
|
225
|
+
assert value is not None
|
226
|
+
holes = sum(1 for x in value if x is None)
|
227
|
+
if holes == len(value):
|
228
|
+
value = None
|
229
|
+
elif holes != 0:
|
230
|
+
raise BadArgumentUsage(
|
231
|
+
_("Argument {name!r} takes {nargs} values.").format(
|
232
|
+
name=self.dest, nargs=self.nargs
|
233
|
+
)
|
234
|
+
)
|
235
|
+
|
236
|
+
if self.nargs == -1 and self.obj.envvar is not None and value == ():
|
237
|
+
# Replace empty tuple with None so that a value from the
|
238
|
+
# environment may be tried.
|
239
|
+
value = None
|
240
|
+
|
241
|
+
state.opts[self.dest] = value # type: ignore
|
242
|
+
state.order.append(self.obj)
|
243
|
+
|
244
|
+
|
245
|
+
class ParsingState:
|
246
|
+
def __init__(self, rargs: t.List[str]) -> None:
|
247
|
+
self.opts: t.Dict[str, t.Any] = {}
|
248
|
+
self.largs: t.List[str] = []
|
249
|
+
self.rargs = rargs
|
250
|
+
self.order: t.List["CoreParameter"] = []
|
251
|
+
|
252
|
+
|
253
|
+
class OptionParser:
|
254
|
+
"""The option parser is an internal class that is ultimately used to
|
255
|
+
parse options and arguments. It's modelled after optparse and brings
|
256
|
+
a similar but vastly simplified API. It should generally not be used
|
257
|
+
directly as the high level Click classes wrap it for you.
|
258
|
+
|
259
|
+
It's not nearly as extensible as optparse or argparse as it does not
|
260
|
+
implement features that are implemented on a higher level (such as
|
261
|
+
types or defaults).
|
262
|
+
|
263
|
+
:param ctx: optionally the :class:`~click.Context` where this parser
|
264
|
+
should go with.
|
265
|
+
"""
|
266
|
+
|
267
|
+
def __init__(self, ctx: t.Optional["Context"] = None) -> None:
|
268
|
+
#: The :class:`~click.Context` for this parser. This might be
|
269
|
+
#: `None` for some advanced use cases.
|
270
|
+
self.ctx = ctx
|
271
|
+
#: This controls how the parser deals with interspersed arguments.
|
272
|
+
#: If this is set to `False`, the parser will stop on the first
|
273
|
+
#: non-option. Click uses this to implement nested subcommands
|
274
|
+
#: safely.
|
275
|
+
self.allow_interspersed_args = True
|
276
|
+
#: This tells the parser how to deal with unknown options. By
|
277
|
+
#: default it will error out (which is sensible), but there is a
|
278
|
+
#: second mode where it will ignore it and continue processing
|
279
|
+
#: after shifting all the unknown options into the resulting args.
|
280
|
+
self.ignore_unknown_options = False
|
281
|
+
|
282
|
+
if ctx is not None:
|
283
|
+
self.allow_interspersed_args = ctx.allow_interspersed_args
|
284
|
+
self.ignore_unknown_options = ctx.ignore_unknown_options
|
285
|
+
|
286
|
+
self._short_opt: t.Dict[str, Option] = {}
|
287
|
+
self._long_opt: t.Dict[str, Option] = {}
|
288
|
+
self._opt_prefixes = {"-", "--"}
|
289
|
+
self._args: t.List[Argument] = []
|
290
|
+
|
291
|
+
def add_option(
|
292
|
+
self,
|
293
|
+
obj: "CoreOption",
|
294
|
+
opts: t.Sequence[str],
|
295
|
+
dest: t.Optional[str],
|
296
|
+
action: t.Optional[str] = None,
|
297
|
+
nargs: int = 1,
|
298
|
+
const: t.Optional[t.Any] = None,
|
299
|
+
) -> None:
|
300
|
+
"""Adds a new option named `dest` to the parser. The destination
|
301
|
+
is not inferred (unlike with optparse) and needs to be explicitly
|
302
|
+
provided. Action can be any of ``store``, ``store_const``,
|
303
|
+
``append``, ``append_const`` or ``count``.
|
304
|
+
|
305
|
+
The `obj` can be used to identify the option in the order list
|
306
|
+
that is returned from the parser.
|
307
|
+
"""
|
308
|
+
opts = [normalize_opt(opt, self.ctx) for opt in opts]
|
309
|
+
option = Option(obj, opts, dest, action=action, nargs=nargs, const=const)
|
310
|
+
self._opt_prefixes.update(option.prefixes)
|
311
|
+
for opt in option._short_opts:
|
312
|
+
self._short_opt[opt] = option
|
313
|
+
for opt in option._long_opts:
|
314
|
+
self._long_opt[opt] = option
|
315
|
+
|
316
|
+
def add_argument(
|
317
|
+
self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1
|
318
|
+
) -> None:
|
319
|
+
"""Adds a positional argument named `dest` to the parser.
|
320
|
+
|
321
|
+
The `obj` can be used to identify the option in the order list
|
322
|
+
that is returned from the parser.
|
323
|
+
"""
|
324
|
+
self._args.append(Argument(obj, dest=dest, nargs=nargs))
|
325
|
+
|
326
|
+
def parse_args(
|
327
|
+
self, args: t.List[str]
|
328
|
+
) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]:
|
329
|
+
"""Parses positional arguments and returns ``(values, args, order)``
|
330
|
+
for the parsed options and arguments as well as the leftover
|
331
|
+
arguments if there are any. The order is a list of objects as they
|
332
|
+
appear on the command line. If arguments appear multiple times they
|
333
|
+
will be memorized multiple times as well.
|
334
|
+
"""
|
335
|
+
state = ParsingState(args)
|
336
|
+
try:
|
337
|
+
self._process_args_for_options(state)
|
338
|
+
self._process_args_for_args(state)
|
339
|
+
except UsageError:
|
340
|
+
if self.ctx is None or not self.ctx.resilient_parsing:
|
341
|
+
raise
|
342
|
+
return state.opts, state.largs, state.order
|
343
|
+
|
344
|
+
def _process_args_for_args(self, state: ParsingState) -> None:
|
345
|
+
pargs, args = _unpack_args(
|
346
|
+
state.largs + state.rargs, [x.nargs for x in self._args]
|
347
|
+
)
|
348
|
+
|
349
|
+
for idx, arg in enumerate(self._args):
|
350
|
+
arg.process(pargs[idx], state)
|
351
|
+
|
352
|
+
state.largs = args
|
353
|
+
state.rargs = []
|
354
|
+
|
355
|
+
def _process_args_for_options(self, state: ParsingState) -> None:
|
356
|
+
while state.rargs:
|
357
|
+
arg = state.rargs.pop(0)
|
358
|
+
arglen = len(arg)
|
359
|
+
# Double dashes always handled explicitly regardless of what
|
360
|
+
# prefixes are valid.
|
361
|
+
if arg == "--":
|
362
|
+
return
|
363
|
+
elif arg[:1] in self._opt_prefixes and arglen > 1:
|
364
|
+
self._process_opts(arg, state)
|
365
|
+
elif self.allow_interspersed_args:
|
366
|
+
state.largs.append(arg)
|
367
|
+
else:
|
368
|
+
state.rargs.insert(0, arg)
|
369
|
+
return
|
370
|
+
|
371
|
+
# Say this is the original argument list:
|
372
|
+
# [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
|
373
|
+
# ^
|
374
|
+
# (we are about to process arg(i)).
|
375
|
+
#
|
376
|
+
# Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
|
377
|
+
# [arg0, ..., arg(i-1)] (any options and their arguments will have
|
378
|
+
# been removed from largs).
|
379
|
+
#
|
380
|
+
# The while loop will usually consume 1 or more arguments per pass.
|
381
|
+
# If it consumes 1 (eg. arg is an option that takes no arguments),
|
382
|
+
# then after _process_arg() is done the situation is:
|
383
|
+
#
|
384
|
+
# largs = subset of [arg0, ..., arg(i)]
|
385
|
+
# rargs = [arg(i+1), ..., arg(N-1)]
|
386
|
+
#
|
387
|
+
# If allow_interspersed_args is false, largs will always be
|
388
|
+
# *empty* -- still a subset of [arg0, ..., arg(i-1)], but
|
389
|
+
# not a very interesting subset!
|
390
|
+
|
391
|
+
def _match_long_opt(
|
392
|
+
self, opt: str, explicit_value: t.Optional[str], state: ParsingState
|
393
|
+
) -> None:
|
394
|
+
if opt not in self._long_opt:
|
395
|
+
from difflib import get_close_matches
|
396
|
+
|
397
|
+
possibilities = get_close_matches(opt, self._long_opt)
|
398
|
+
raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx)
|
399
|
+
|
400
|
+
option = self._long_opt[opt]
|
401
|
+
if option.takes_value:
|
402
|
+
# At this point it's safe to modify rargs by injecting the
|
403
|
+
# explicit value, because no exception is raised in this
|
404
|
+
# branch. This means that the inserted value will be fully
|
405
|
+
# consumed.
|
406
|
+
if explicit_value is not None:
|
407
|
+
state.rargs.insert(0, explicit_value)
|
408
|
+
|
409
|
+
value = self._get_value_from_state(opt, option, state)
|
410
|
+
|
411
|
+
elif explicit_value is not None:
|
412
|
+
raise BadOptionUsage(
|
413
|
+
opt, _("Option {name!r} does not take a value.").format(name=opt)
|
414
|
+
)
|
415
|
+
|
416
|
+
else:
|
417
|
+
value = None
|
418
|
+
|
419
|
+
option.process(value, state)
|
420
|
+
|
421
|
+
def _match_short_opt(self, arg: str, state: ParsingState) -> None:
|
422
|
+
stop = False
|
423
|
+
i = 1
|
424
|
+
prefix = arg[0]
|
425
|
+
unknown_options = []
|
426
|
+
|
427
|
+
for ch in arg[1:]:
|
428
|
+
opt = normalize_opt(f"{prefix}{ch}", self.ctx)
|
429
|
+
option = self._short_opt.get(opt)
|
430
|
+
i += 1
|
431
|
+
|
432
|
+
if not option:
|
433
|
+
if self.ignore_unknown_options:
|
434
|
+
unknown_options.append(ch)
|
435
|
+
continue
|
436
|
+
raise NoSuchOption(opt, ctx=self.ctx)
|
437
|
+
if option.takes_value:
|
438
|
+
# Any characters left in arg? Pretend they're the
|
439
|
+
# next arg, and stop consuming characters of arg.
|
440
|
+
if i < len(arg):
|
441
|
+
state.rargs.insert(0, arg[i:])
|
442
|
+
stop = True
|
443
|
+
|
444
|
+
value = self._get_value_from_state(opt, option, state)
|
445
|
+
|
446
|
+
else:
|
447
|
+
value = None
|
448
|
+
|
449
|
+
option.process(value, state)
|
450
|
+
|
451
|
+
if stop:
|
452
|
+
break
|
453
|
+
|
454
|
+
# If we got any unknown options we re-combinate the string of the
|
455
|
+
# remaining options and re-attach the prefix, then report that
|
456
|
+
# to the state as new larg. This way there is basic combinatorics
|
457
|
+
# that can be achieved while still ignoring unknown arguments.
|
458
|
+
if self.ignore_unknown_options and unknown_options:
|
459
|
+
state.largs.append(f"{prefix}{''.join(unknown_options)}")
|
460
|
+
|
461
|
+
def _get_value_from_state(
|
462
|
+
self, option_name: str, option: Option, state: ParsingState
|
463
|
+
) -> t.Any:
|
464
|
+
nargs = option.nargs
|
465
|
+
|
466
|
+
if len(state.rargs) < nargs:
|
467
|
+
if option.obj._flag_needs_value:
|
468
|
+
# Option allows omitting the value.
|
469
|
+
value = _flag_needs_value
|
470
|
+
else:
|
471
|
+
raise BadOptionUsage(
|
472
|
+
option_name,
|
473
|
+
ngettext(
|
474
|
+
"Option {name!r} requires an argument.",
|
475
|
+
"Option {name!r} requires {nargs} arguments.",
|
476
|
+
nargs,
|
477
|
+
).format(name=option_name, nargs=nargs),
|
478
|
+
)
|
479
|
+
elif nargs == 1:
|
480
|
+
next_rarg = state.rargs[0]
|
481
|
+
|
482
|
+
if (
|
483
|
+
option.obj._flag_needs_value
|
484
|
+
and isinstance(next_rarg, str)
|
485
|
+
and next_rarg[:1] in self._opt_prefixes
|
486
|
+
and len(next_rarg) > 1
|
487
|
+
):
|
488
|
+
# The next arg looks like the start of an option, don't
|
489
|
+
# use it as the value if omitting the value is allowed.
|
490
|
+
value = _flag_needs_value
|
491
|
+
else:
|
492
|
+
value = state.rargs.pop(0)
|
493
|
+
else:
|
494
|
+
value = tuple(state.rargs[:nargs])
|
495
|
+
del state.rargs[:nargs]
|
496
|
+
|
497
|
+
return value
|
498
|
+
|
499
|
+
def _process_opts(self, arg: str, state: ParsingState) -> None:
|
500
|
+
explicit_value = None
|
501
|
+
# Long option handling happens in two parts. The first part is
|
502
|
+
# supporting explicitly attached values. In any case, we will try
|
503
|
+
# to long match the option first.
|
504
|
+
if "=" in arg:
|
505
|
+
long_opt, explicit_value = arg.split("=", 1)
|
506
|
+
else:
|
507
|
+
long_opt = arg
|
508
|
+
norm_long_opt = normalize_opt(long_opt, self.ctx)
|
509
|
+
|
510
|
+
# At this point we will match the (assumed) long option through
|
511
|
+
# the long option matching code. Note that this allows options
|
512
|
+
# like "-foo" to be matched as long options.
|
513
|
+
try:
|
514
|
+
self._match_long_opt(norm_long_opt, explicit_value, state)
|
515
|
+
except NoSuchOption:
|
516
|
+
# At this point the long option matching failed, and we need
|
517
|
+
# to try with short options. However there is a special rule
|
518
|
+
# which says, that if we have a two character options prefix
|
519
|
+
# (applies to "--foo" for instance), we do not dispatch to the
|
520
|
+
# short option code and will instead raise the no option
|
521
|
+
# error.
|
522
|
+
if arg[:2] not in self._opt_prefixes:
|
523
|
+
self._match_short_opt(arg, state)
|
524
|
+
return
|
525
|
+
|
526
|
+
if not self.ignore_unknown_options:
|
527
|
+
raise
|
528
|
+
|
529
|
+
state.largs.append(arg)
|
File without changes
|