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,2998 @@
|
|
1
|
+
import enum
|
2
|
+
import errno
|
3
|
+
import inspect
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
import typing as t
|
7
|
+
from collections import abc
|
8
|
+
from contextlib import contextmanager
|
9
|
+
from contextlib import ExitStack
|
10
|
+
from functools import partial
|
11
|
+
from functools import update_wrapper
|
12
|
+
from gettext import gettext as _
|
13
|
+
from gettext import ngettext
|
14
|
+
from itertools import repeat
|
15
|
+
|
16
|
+
from . import types
|
17
|
+
from .exceptions import Abort
|
18
|
+
from .exceptions import BadParameter
|
19
|
+
from .exceptions import ClickException
|
20
|
+
from .exceptions import Exit
|
21
|
+
from .exceptions import MissingParameter
|
22
|
+
from .exceptions import UsageError
|
23
|
+
from .formatting import HelpFormatter
|
24
|
+
from .formatting import join_options
|
25
|
+
from .globals import pop_context
|
26
|
+
from .globals import push_context
|
27
|
+
from .parser import _flag_needs_value
|
28
|
+
from .parser import OptionParser
|
29
|
+
from .parser import split_opt
|
30
|
+
from .termui import confirm
|
31
|
+
from .termui import prompt
|
32
|
+
from .termui import style
|
33
|
+
from .utils import _detect_program_name
|
34
|
+
from .utils import _expand_args
|
35
|
+
from .utils import echo
|
36
|
+
from .utils import make_default_short_help
|
37
|
+
from .utils import make_str
|
38
|
+
from .utils import PacifyFlushWrapper
|
39
|
+
|
40
|
+
if t.TYPE_CHECKING:
|
41
|
+
import typing_extensions as te
|
42
|
+
from .shell_completion import CompletionItem
|
43
|
+
|
44
|
+
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
45
|
+
V = t.TypeVar("V")
|
46
|
+
|
47
|
+
|
48
|
+
def _complete_visible_commands(
|
49
|
+
ctx: "Context", incomplete: str
|
50
|
+
) -> t.Iterator[t.Tuple[str, "Command"]]:
|
51
|
+
"""List all the subcommands of a group that start with the
|
52
|
+
incomplete value and aren't hidden.
|
53
|
+
|
54
|
+
:param ctx: Invocation context for the group.
|
55
|
+
:param incomplete: Value being completed. May be empty.
|
56
|
+
"""
|
57
|
+
multi = t.cast(MultiCommand, ctx.command)
|
58
|
+
|
59
|
+
for name in multi.list_commands(ctx):
|
60
|
+
if name.startswith(incomplete):
|
61
|
+
command = multi.get_command(ctx, name)
|
62
|
+
|
63
|
+
if command is not None and not command.hidden:
|
64
|
+
yield name, command
|
65
|
+
|
66
|
+
|
67
|
+
def _check_multicommand(
|
68
|
+
base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False
|
69
|
+
) -> None:
|
70
|
+
if not base_command.chain or not isinstance(cmd, MultiCommand):
|
71
|
+
return
|
72
|
+
if register:
|
73
|
+
hint = (
|
74
|
+
"It is not possible to add multi commands as children to"
|
75
|
+
" another multi command that is in chain mode."
|
76
|
+
)
|
77
|
+
else:
|
78
|
+
hint = (
|
79
|
+
"Found a multi command as subcommand to a multi command"
|
80
|
+
" that is in chain mode. This is not supported."
|
81
|
+
)
|
82
|
+
raise RuntimeError(
|
83
|
+
f"{hint}. Command {base_command.name!r} is set to chain and"
|
84
|
+
f" {cmd_name!r} was added as a subcommand but it in itself is a"
|
85
|
+
f" multi command. ({cmd_name!r} is a {type(cmd).__name__}"
|
86
|
+
f" within a chained {type(base_command).__name__} named"
|
87
|
+
f" {base_command.name!r})."
|
88
|
+
)
|
89
|
+
|
90
|
+
|
91
|
+
def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]:
|
92
|
+
return list(zip(*repeat(iter(iterable), batch_size)))
|
93
|
+
|
94
|
+
|
95
|
+
@contextmanager
|
96
|
+
def augment_usage_errors(
|
97
|
+
ctx: "Context", param: t.Optional["Parameter"] = None
|
98
|
+
) -> t.Iterator[None]:
|
99
|
+
"""Context manager that attaches extra information to exceptions."""
|
100
|
+
try:
|
101
|
+
yield
|
102
|
+
except BadParameter as e:
|
103
|
+
if e.ctx is None:
|
104
|
+
e.ctx = ctx
|
105
|
+
if param is not None and e.param is None:
|
106
|
+
e.param = param
|
107
|
+
raise
|
108
|
+
except UsageError as e:
|
109
|
+
if e.ctx is None:
|
110
|
+
e.ctx = ctx
|
111
|
+
raise
|
112
|
+
|
113
|
+
|
114
|
+
def iter_params_for_processing(
|
115
|
+
invocation_order: t.Sequence["Parameter"],
|
116
|
+
declaration_order: t.Sequence["Parameter"],
|
117
|
+
) -> t.List["Parameter"]:
|
118
|
+
"""Given a sequence of parameters in the order as should be considered
|
119
|
+
for processing and an iterable of parameters that exist, this returns
|
120
|
+
a list in the correct order as they should be processed.
|
121
|
+
"""
|
122
|
+
|
123
|
+
def sort_key(item: "Parameter") -> t.Tuple[bool, float]:
|
124
|
+
try:
|
125
|
+
idx: float = invocation_order.index(item)
|
126
|
+
except ValueError:
|
127
|
+
idx = float("inf")
|
128
|
+
|
129
|
+
return not item.is_eager, idx
|
130
|
+
|
131
|
+
return sorted(declaration_order, key=sort_key)
|
132
|
+
|
133
|
+
|
134
|
+
class ParameterSource(enum.Enum):
|
135
|
+
"""This is an :class:`~enum.Enum` that indicates the source of a
|
136
|
+
parameter's value.
|
137
|
+
|
138
|
+
Use :meth:`click.Context.get_parameter_source` to get the
|
139
|
+
source for a parameter by name.
|
140
|
+
|
141
|
+
.. versionchanged:: 8.0
|
142
|
+
Use :class:`~enum.Enum` and drop the ``validate`` method.
|
143
|
+
|
144
|
+
.. versionchanged:: 8.0
|
145
|
+
Added the ``PROMPT`` value.
|
146
|
+
"""
|
147
|
+
|
148
|
+
COMMANDLINE = enum.auto()
|
149
|
+
"""The value was provided by the command line args."""
|
150
|
+
ENVIRONMENT = enum.auto()
|
151
|
+
"""The value was provided with an environment variable."""
|
152
|
+
DEFAULT = enum.auto()
|
153
|
+
"""Used the default specified by the parameter."""
|
154
|
+
DEFAULT_MAP = enum.auto()
|
155
|
+
"""Used a default provided by :attr:`Context.default_map`."""
|
156
|
+
PROMPT = enum.auto()
|
157
|
+
"""Used a prompt to confirm a default or provide a value."""
|
158
|
+
|
159
|
+
|
160
|
+
class Context:
|
161
|
+
"""The context is a special internal object that holds state relevant
|
162
|
+
for the script execution at every single level. It's normally invisible
|
163
|
+
to commands unless they opt-in to getting access to it.
|
164
|
+
|
165
|
+
The context is useful as it can pass internal objects around and can
|
166
|
+
control special execution features such as reading data from
|
167
|
+
environment variables.
|
168
|
+
|
169
|
+
A context can be used as context manager in which case it will call
|
170
|
+
:meth:`close` on teardown.
|
171
|
+
|
172
|
+
:param command: the command class for this context.
|
173
|
+
:param parent: the parent context.
|
174
|
+
:param info_name: the info name for this invocation. Generally this
|
175
|
+
is the most descriptive name for the script or
|
176
|
+
command. For the toplevel script it is usually
|
177
|
+
the name of the script, for commands below it it's
|
178
|
+
the name of the script.
|
179
|
+
:param obj: an arbitrary object of user data.
|
180
|
+
:param auto_envvar_prefix: the prefix to use for automatic environment
|
181
|
+
variables. If this is `None` then reading
|
182
|
+
from environment variables is disabled. This
|
183
|
+
does not affect manually set environment
|
184
|
+
variables which are always read.
|
185
|
+
:param default_map: a dictionary (like object) with default values
|
186
|
+
for parameters.
|
187
|
+
:param terminal_width: the width of the terminal. The default is
|
188
|
+
inherit from parent context. If no context
|
189
|
+
defines the terminal width then auto
|
190
|
+
detection will be applied.
|
191
|
+
:param max_content_width: the maximum width for content rendered by
|
192
|
+
Click (this currently only affects help
|
193
|
+
pages). This defaults to 80 characters if
|
194
|
+
not overridden. In other words: even if the
|
195
|
+
terminal is larger than that, Click will not
|
196
|
+
format things wider than 80 characters by
|
197
|
+
default. In addition to that, formatters might
|
198
|
+
add some safety mapping on the right.
|
199
|
+
:param resilient_parsing: if this flag is enabled then Click will
|
200
|
+
parse without any interactivity or callback
|
201
|
+
invocation. Default values will also be
|
202
|
+
ignored. This is useful for implementing
|
203
|
+
things such as completion support.
|
204
|
+
:param allow_extra_args: if this is set to `True` then extra arguments
|
205
|
+
at the end will not raise an error and will be
|
206
|
+
kept on the context. The default is to inherit
|
207
|
+
from the command.
|
208
|
+
:param allow_interspersed_args: if this is set to `False` then options
|
209
|
+
and arguments cannot be mixed. The
|
210
|
+
default is to inherit from the command.
|
211
|
+
:param ignore_unknown_options: instructs click to ignore options it does
|
212
|
+
not know and keeps them for later
|
213
|
+
processing.
|
214
|
+
:param help_option_names: optionally a list of strings that define how
|
215
|
+
the default help parameter is named. The
|
216
|
+
default is ``['--help']``.
|
217
|
+
:param token_normalize_func: an optional function that is used to
|
218
|
+
normalize tokens (options, choices,
|
219
|
+
etc.). This for instance can be used to
|
220
|
+
implement case insensitive behavior.
|
221
|
+
:param color: controls if the terminal supports ANSI colors or not. The
|
222
|
+
default is autodetection. This is only needed if ANSI
|
223
|
+
codes are used in texts that Click prints which is by
|
224
|
+
default not the case. This for instance would affect
|
225
|
+
help output.
|
226
|
+
:param show_default: Show the default value for commands. If this
|
227
|
+
value is not set, it defaults to the value from the parent
|
228
|
+
context. ``Command.show_default`` overrides this default for the
|
229
|
+
specific command.
|
230
|
+
|
231
|
+
.. versionchanged:: 8.1
|
232
|
+
The ``show_default`` parameter is overridden by
|
233
|
+
``Command.show_default``, instead of the other way around.
|
234
|
+
|
235
|
+
.. versionchanged:: 8.0
|
236
|
+
The ``show_default`` parameter defaults to the value from the
|
237
|
+
parent context.
|
238
|
+
|
239
|
+
.. versionchanged:: 7.1
|
240
|
+
Added the ``show_default`` parameter.
|
241
|
+
|
242
|
+
.. versionchanged:: 4.0
|
243
|
+
Added the ``color``, ``ignore_unknown_options``, and
|
244
|
+
``max_content_width`` parameters.
|
245
|
+
|
246
|
+
.. versionchanged:: 3.0
|
247
|
+
Added the ``allow_extra_args`` and ``allow_interspersed_args``
|
248
|
+
parameters.
|
249
|
+
|
250
|
+
.. versionchanged:: 2.0
|
251
|
+
Added the ``resilient_parsing``, ``help_option_names``, and
|
252
|
+
``token_normalize_func`` parameters.
|
253
|
+
"""
|
254
|
+
|
255
|
+
#: The formatter class to create with :meth:`make_formatter`.
|
256
|
+
#:
|
257
|
+
#: .. versionadded:: 8.0
|
258
|
+
formatter_class: t.Type["HelpFormatter"] = HelpFormatter
|
259
|
+
|
260
|
+
def __init__(
|
261
|
+
self,
|
262
|
+
command: "Command",
|
263
|
+
parent: t.Optional["Context"] = None,
|
264
|
+
info_name: t.Optional[str] = None,
|
265
|
+
obj: t.Optional[t.Any] = None,
|
266
|
+
auto_envvar_prefix: t.Optional[str] = None,
|
267
|
+
default_map: t.Optional[t.Dict[str, t.Any]] = None,
|
268
|
+
terminal_width: t.Optional[int] = None,
|
269
|
+
max_content_width: t.Optional[int] = None,
|
270
|
+
resilient_parsing: bool = False,
|
271
|
+
allow_extra_args: t.Optional[bool] = None,
|
272
|
+
allow_interspersed_args: t.Optional[bool] = None,
|
273
|
+
ignore_unknown_options: t.Optional[bool] = None,
|
274
|
+
help_option_names: t.Optional[t.List[str]] = None,
|
275
|
+
token_normalize_func: t.Optional[t.Callable[[str], str]] = None,
|
276
|
+
color: t.Optional[bool] = None,
|
277
|
+
show_default: t.Optional[bool] = None,
|
278
|
+
) -> None:
|
279
|
+
#: the parent context or `None` if none exists.
|
280
|
+
self.parent = parent
|
281
|
+
#: the :class:`Command` for this context.
|
282
|
+
self.command = command
|
283
|
+
#: the descriptive information name
|
284
|
+
self.info_name = info_name
|
285
|
+
#: Map of parameter names to their parsed values. Parameters
|
286
|
+
#: with ``expose_value=False`` are not stored.
|
287
|
+
self.params: t.Dict[str, t.Any] = {}
|
288
|
+
#: the leftover arguments.
|
289
|
+
self.args: t.List[str] = []
|
290
|
+
#: protected arguments. These are arguments that are prepended
|
291
|
+
#: to `args` when certain parsing scenarios are encountered but
|
292
|
+
#: must be never propagated to another arguments. This is used
|
293
|
+
#: to implement nested parsing.
|
294
|
+
self.protected_args: t.List[str] = []
|
295
|
+
#: the collected prefixes of the command's options.
|
296
|
+
self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set()
|
297
|
+
|
298
|
+
if obj is None and parent is not None:
|
299
|
+
obj = parent.obj
|
300
|
+
|
301
|
+
#: the user object stored.
|
302
|
+
self.obj: t.Any = obj
|
303
|
+
self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {})
|
304
|
+
|
305
|
+
#: A dictionary (-like object) with defaults for parameters.
|
306
|
+
if (
|
307
|
+
default_map is None
|
308
|
+
and info_name is not None
|
309
|
+
and parent is not None
|
310
|
+
and parent.default_map is not None
|
311
|
+
):
|
312
|
+
default_map = parent.default_map.get(info_name)
|
313
|
+
|
314
|
+
self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map
|
315
|
+
|
316
|
+
#: This flag indicates if a subcommand is going to be executed. A
|
317
|
+
#: group callback can use this information to figure out if it's
|
318
|
+
#: being executed directly or because the execution flow passes
|
319
|
+
#: onwards to a subcommand. By default it's None, but it can be
|
320
|
+
#: the name of the subcommand to execute.
|
321
|
+
#:
|
322
|
+
#: If chaining is enabled this will be set to ``'*'`` in case
|
323
|
+
#: any commands are executed. It is however not possible to
|
324
|
+
#: figure out which ones. If you require this knowledge you
|
325
|
+
#: should use a :func:`result_callback`.
|
326
|
+
self.invoked_subcommand: t.Optional[str] = None
|
327
|
+
|
328
|
+
if terminal_width is None and parent is not None:
|
329
|
+
terminal_width = parent.terminal_width
|
330
|
+
|
331
|
+
#: The width of the terminal (None is autodetection).
|
332
|
+
self.terminal_width: t.Optional[int] = terminal_width
|
333
|
+
|
334
|
+
if max_content_width is None and parent is not None:
|
335
|
+
max_content_width = parent.max_content_width
|
336
|
+
|
337
|
+
#: The maximum width of formatted content (None implies a sensible
|
338
|
+
#: default which is 80 for most things).
|
339
|
+
self.max_content_width: t.Optional[int] = max_content_width
|
340
|
+
|
341
|
+
if allow_extra_args is None:
|
342
|
+
allow_extra_args = command.allow_extra_args
|
343
|
+
|
344
|
+
#: Indicates if the context allows extra args or if it should
|
345
|
+
#: fail on parsing.
|
346
|
+
#:
|
347
|
+
#: .. versionadded:: 3.0
|
348
|
+
self.allow_extra_args = allow_extra_args
|
349
|
+
|
350
|
+
if allow_interspersed_args is None:
|
351
|
+
allow_interspersed_args = command.allow_interspersed_args
|
352
|
+
|
353
|
+
#: Indicates if the context allows mixing of arguments and
|
354
|
+
#: options or not.
|
355
|
+
#:
|
356
|
+
#: .. versionadded:: 3.0
|
357
|
+
self.allow_interspersed_args: bool = allow_interspersed_args
|
358
|
+
|
359
|
+
if ignore_unknown_options is None:
|
360
|
+
ignore_unknown_options = command.ignore_unknown_options
|
361
|
+
|
362
|
+
#: Instructs click to ignore options that a command does not
|
363
|
+
#: understand and will store it on the context for later
|
364
|
+
#: processing. This is primarily useful for situations where you
|
365
|
+
#: want to call into external programs. Generally this pattern is
|
366
|
+
#: strongly discouraged because it's not possibly to losslessly
|
367
|
+
#: forward all arguments.
|
368
|
+
#:
|
369
|
+
#: .. versionadded:: 4.0
|
370
|
+
self.ignore_unknown_options: bool = ignore_unknown_options
|
371
|
+
|
372
|
+
if help_option_names is None:
|
373
|
+
if parent is not None:
|
374
|
+
help_option_names = parent.help_option_names
|
375
|
+
else:
|
376
|
+
help_option_names = ["--help"]
|
377
|
+
|
378
|
+
#: The names for the help options.
|
379
|
+
self.help_option_names: t.List[str] = help_option_names
|
380
|
+
|
381
|
+
if token_normalize_func is None and parent is not None:
|
382
|
+
token_normalize_func = parent.token_normalize_func
|
383
|
+
|
384
|
+
#: An optional normalization function for tokens. This is
|
385
|
+
#: options, choices, commands etc.
|
386
|
+
self.token_normalize_func: t.Optional[
|
387
|
+
t.Callable[[str], str]
|
388
|
+
] = token_normalize_func
|
389
|
+
|
390
|
+
#: Indicates if resilient parsing is enabled. In that case Click
|
391
|
+
#: will do its best to not cause any failures and default values
|
392
|
+
#: will be ignored. Useful for completion.
|
393
|
+
self.resilient_parsing: bool = resilient_parsing
|
394
|
+
|
395
|
+
# If there is no envvar prefix yet, but the parent has one and
|
396
|
+
# the command on this level has a name, we can expand the envvar
|
397
|
+
# prefix automatically.
|
398
|
+
if auto_envvar_prefix is None:
|
399
|
+
if (
|
400
|
+
parent is not None
|
401
|
+
and parent.auto_envvar_prefix is not None
|
402
|
+
and self.info_name is not None
|
403
|
+
):
|
404
|
+
auto_envvar_prefix = (
|
405
|
+
f"{parent.auto_envvar_prefix}_{self.info_name.upper()}"
|
406
|
+
)
|
407
|
+
else:
|
408
|
+
auto_envvar_prefix = auto_envvar_prefix.upper()
|
409
|
+
|
410
|
+
if auto_envvar_prefix is not None:
|
411
|
+
auto_envvar_prefix = auto_envvar_prefix.replace("-", "_")
|
412
|
+
|
413
|
+
self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix
|
414
|
+
|
415
|
+
if color is None and parent is not None:
|
416
|
+
color = parent.color
|
417
|
+
|
418
|
+
#: Controls if styling output is wanted or not.
|
419
|
+
self.color: t.Optional[bool] = color
|
420
|
+
|
421
|
+
if show_default is None and parent is not None:
|
422
|
+
show_default = parent.show_default
|
423
|
+
|
424
|
+
#: Show option default values when formatting help text.
|
425
|
+
self.show_default: t.Optional[bool] = show_default
|
426
|
+
|
427
|
+
self._close_callbacks: t.List[t.Callable[[], t.Any]] = []
|
428
|
+
self._depth = 0
|
429
|
+
self._parameter_source: t.Dict[str, ParameterSource] = {}
|
430
|
+
self._exit_stack = ExitStack()
|
431
|
+
|
432
|
+
def to_info_dict(self) -> t.Dict[str, t.Any]:
|
433
|
+
"""Gather information that could be useful for a tool generating
|
434
|
+
user-facing documentation. This traverses the entire CLI
|
435
|
+
structure.
|
436
|
+
|
437
|
+
.. code-block:: python
|
438
|
+
|
439
|
+
with Context(cli) as ctx:
|
440
|
+
info = ctx.to_info_dict()
|
441
|
+
|
442
|
+
.. versionadded:: 8.0
|
443
|
+
"""
|
444
|
+
return {
|
445
|
+
"command": self.command.to_info_dict(self),
|
446
|
+
"info_name": self.info_name,
|
447
|
+
"allow_extra_args": self.allow_extra_args,
|
448
|
+
"allow_interspersed_args": self.allow_interspersed_args,
|
449
|
+
"ignore_unknown_options": self.ignore_unknown_options,
|
450
|
+
"auto_envvar_prefix": self.auto_envvar_prefix,
|
451
|
+
}
|
452
|
+
|
453
|
+
def __enter__(self) -> "Context":
|
454
|
+
self._depth += 1
|
455
|
+
push_context(self)
|
456
|
+
return self
|
457
|
+
|
458
|
+
def __exit__(self, exc_type, exc_value, tb): # type: ignore
|
459
|
+
self._depth -= 1
|
460
|
+
if self._depth == 0:
|
461
|
+
self.close()
|
462
|
+
pop_context()
|
463
|
+
|
464
|
+
@contextmanager
|
465
|
+
def scope(self, cleanup: bool = True) -> t.Iterator["Context"]:
|
466
|
+
"""This helper method can be used with the context object to promote
|
467
|
+
it to the current thread local (see :func:`get_current_context`).
|
468
|
+
The default behavior of this is to invoke the cleanup functions which
|
469
|
+
can be disabled by setting `cleanup` to `False`. The cleanup
|
470
|
+
functions are typically used for things such as closing file handles.
|
471
|
+
|
472
|
+
If the cleanup is intended the context object can also be directly
|
473
|
+
used as a context manager.
|
474
|
+
|
475
|
+
Example usage::
|
476
|
+
|
477
|
+
with ctx.scope():
|
478
|
+
assert get_current_context() is ctx
|
479
|
+
|
480
|
+
This is equivalent::
|
481
|
+
|
482
|
+
with ctx:
|
483
|
+
assert get_current_context() is ctx
|
484
|
+
|
485
|
+
.. versionadded:: 5.0
|
486
|
+
|
487
|
+
:param cleanup: controls if the cleanup functions should be run or
|
488
|
+
not. The default is to run these functions. In
|
489
|
+
some situations the context only wants to be
|
490
|
+
temporarily pushed in which case this can be disabled.
|
491
|
+
Nested pushes automatically defer the cleanup.
|
492
|
+
"""
|
493
|
+
if not cleanup:
|
494
|
+
self._depth += 1
|
495
|
+
try:
|
496
|
+
with self as rv:
|
497
|
+
yield rv
|
498
|
+
finally:
|
499
|
+
if not cleanup:
|
500
|
+
self._depth -= 1
|
501
|
+
|
502
|
+
@property
|
503
|
+
def meta(self) -> t.Dict[str, t.Any]:
|
504
|
+
"""This is a dictionary which is shared with all the contexts
|
505
|
+
that are nested. It exists so that click utilities can store some
|
506
|
+
state here if they need to. It is however the responsibility of
|
507
|
+
that code to manage this dictionary well.
|
508
|
+
|
509
|
+
The keys are supposed to be unique dotted strings. For instance
|
510
|
+
module paths are a good choice for it. What is stored in there is
|
511
|
+
irrelevant for the operation of click. However what is important is
|
512
|
+
that code that places data here adheres to the general semantics of
|
513
|
+
the system.
|
514
|
+
|
515
|
+
Example usage::
|
516
|
+
|
517
|
+
LANG_KEY = f'{__name__}.lang'
|
518
|
+
|
519
|
+
def set_language(value):
|
520
|
+
ctx = get_current_context()
|
521
|
+
ctx.meta[LANG_KEY] = value
|
522
|
+
|
523
|
+
def get_language():
|
524
|
+
return get_current_context().meta.get(LANG_KEY, 'en_US')
|
525
|
+
|
526
|
+
.. versionadded:: 5.0
|
527
|
+
"""
|
528
|
+
return self._meta
|
529
|
+
|
530
|
+
def make_formatter(self) -> HelpFormatter:
|
531
|
+
"""Creates the :class:`~click.HelpFormatter` for the help and
|
532
|
+
usage output.
|
533
|
+
|
534
|
+
To quickly customize the formatter class used without overriding
|
535
|
+
this method, set the :attr:`formatter_class` attribute.
|
536
|
+
|
537
|
+
.. versionchanged:: 8.0
|
538
|
+
Added the :attr:`formatter_class` attribute.
|
539
|
+
"""
|
540
|
+
return self.formatter_class(
|
541
|
+
width=self.terminal_width, max_width=self.max_content_width
|
542
|
+
)
|
543
|
+
|
544
|
+
def with_resource(self, context_manager: t.ContextManager[V]) -> V:
|
545
|
+
"""Register a resource as if it were used in a ``with``
|
546
|
+
statement. The resource will be cleaned up when the context is
|
547
|
+
popped.
|
548
|
+
|
549
|
+
Uses :meth:`contextlib.ExitStack.enter_context`. It calls the
|
550
|
+
resource's ``__enter__()`` method and returns the result. When
|
551
|
+
the context is popped, it closes the stack, which calls the
|
552
|
+
resource's ``__exit__()`` method.
|
553
|
+
|
554
|
+
To register a cleanup function for something that isn't a
|
555
|
+
context manager, use :meth:`call_on_close`. Or use something
|
556
|
+
from :mod:`contextlib` to turn it into a context manager first.
|
557
|
+
|
558
|
+
.. code-block:: python
|
559
|
+
|
560
|
+
@click.group()
|
561
|
+
@click.option("--name")
|
562
|
+
@click.pass_context
|
563
|
+
def cli(ctx):
|
564
|
+
ctx.obj = ctx.with_resource(connect_db(name))
|
565
|
+
|
566
|
+
:param context_manager: The context manager to enter.
|
567
|
+
:return: Whatever ``context_manager.__enter__()`` returns.
|
568
|
+
|
569
|
+
.. versionadded:: 8.0
|
570
|
+
"""
|
571
|
+
return self._exit_stack.enter_context(context_manager)
|
572
|
+
|
573
|
+
def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
|
574
|
+
"""Register a function to be called when the context tears down.
|
575
|
+
|
576
|
+
This can be used to close resources opened during the script
|
577
|
+
execution. Resources that support Python's context manager
|
578
|
+
protocol which would be used in a ``with`` statement should be
|
579
|
+
registered with :meth:`with_resource` instead.
|
580
|
+
|
581
|
+
:param f: The function to execute on teardown.
|
582
|
+
"""
|
583
|
+
return self._exit_stack.callback(f)
|
584
|
+
|
585
|
+
def close(self) -> None:
|
586
|
+
"""Invoke all close callbacks registered with
|
587
|
+
:meth:`call_on_close`, and exit all context managers entered
|
588
|
+
with :meth:`with_resource`.
|
589
|
+
"""
|
590
|
+
self._exit_stack.close()
|
591
|
+
# In case the context is reused, create a new exit stack.
|
592
|
+
self._exit_stack = ExitStack()
|
593
|
+
|
594
|
+
@property
|
595
|
+
def command_path(self) -> str:
|
596
|
+
"""The computed command path. This is used for the ``usage``
|
597
|
+
information on the help page. It's automatically created by
|
598
|
+
combining the info names of the chain of contexts to the root.
|
599
|
+
"""
|
600
|
+
rv = ""
|
601
|
+
if self.info_name is not None:
|
602
|
+
rv = self.info_name
|
603
|
+
if self.parent is not None:
|
604
|
+
parent_command_path = [self.parent.command_path]
|
605
|
+
|
606
|
+
if isinstance(self.parent.command, Command):
|
607
|
+
for param in self.parent.command.get_params(self):
|
608
|
+
parent_command_path.extend(param.get_usage_pieces(self))
|
609
|
+
|
610
|
+
rv = f"{' '.join(parent_command_path)} {rv}"
|
611
|
+
return rv.lstrip()
|
612
|
+
|
613
|
+
def find_root(self) -> "Context":
|
614
|
+
"""Finds the outermost context."""
|
615
|
+
node = self
|
616
|
+
while node.parent is not None:
|
617
|
+
node = node.parent
|
618
|
+
return node
|
619
|
+
|
620
|
+
def find_object(self, object_type: t.Type[V]) -> t.Optional[V]:
|
621
|
+
"""Finds the closest object of a given type."""
|
622
|
+
node: t.Optional["Context"] = self
|
623
|
+
|
624
|
+
while node is not None:
|
625
|
+
if isinstance(node.obj, object_type):
|
626
|
+
return node.obj
|
627
|
+
|
628
|
+
node = node.parent
|
629
|
+
|
630
|
+
return None
|
631
|
+
|
632
|
+
def ensure_object(self, object_type: t.Type[V]) -> V:
|
633
|
+
"""Like :meth:`find_object` but sets the innermost object to a
|
634
|
+
new instance of `object_type` if it does not exist.
|
635
|
+
"""
|
636
|
+
rv = self.find_object(object_type)
|
637
|
+
if rv is None:
|
638
|
+
self.obj = rv = object_type()
|
639
|
+
return rv
|
640
|
+
|
641
|
+
@t.overload
|
642
|
+
def lookup_default(
|
643
|
+
self, name: str, call: "te.Literal[True]" = True
|
644
|
+
) -> t.Optional[t.Any]:
|
645
|
+
...
|
646
|
+
|
647
|
+
@t.overload
|
648
|
+
def lookup_default(
|
649
|
+
self, name: str, call: "te.Literal[False]" = ...
|
650
|
+
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
651
|
+
...
|
652
|
+
|
653
|
+
def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]:
|
654
|
+
"""Get the default for a parameter from :attr:`default_map`.
|
655
|
+
|
656
|
+
:param name: Name of the parameter.
|
657
|
+
:param call: If the default is a callable, call it. Disable to
|
658
|
+
return the callable instead.
|
659
|
+
|
660
|
+
.. versionchanged:: 8.0
|
661
|
+
Added the ``call`` parameter.
|
662
|
+
"""
|
663
|
+
if self.default_map is not None:
|
664
|
+
value = self.default_map.get(name)
|
665
|
+
|
666
|
+
if call and callable(value):
|
667
|
+
return value()
|
668
|
+
|
669
|
+
return value
|
670
|
+
|
671
|
+
return None
|
672
|
+
|
673
|
+
def fail(self, message: str) -> "te.NoReturn":
|
674
|
+
"""Aborts the execution of the program with a specific error
|
675
|
+
message.
|
676
|
+
|
677
|
+
:param message: the error message to fail with.
|
678
|
+
"""
|
679
|
+
raise UsageError(message, self)
|
680
|
+
|
681
|
+
def abort(self) -> "te.NoReturn":
|
682
|
+
"""Aborts the script."""
|
683
|
+
raise Abort()
|
684
|
+
|
685
|
+
def exit(self, code: int = 0) -> "te.NoReturn":
|
686
|
+
"""Exits the application with a given exit code."""
|
687
|
+
raise Exit(code)
|
688
|
+
|
689
|
+
def get_usage(self) -> str:
|
690
|
+
"""Helper method to get formatted usage string for the current
|
691
|
+
context and command.
|
692
|
+
"""
|
693
|
+
return self.command.get_usage(self)
|
694
|
+
|
695
|
+
def get_help(self) -> str:
|
696
|
+
"""Helper method to get formatted help page for the current
|
697
|
+
context and command.
|
698
|
+
"""
|
699
|
+
return self.command.get_help(self)
|
700
|
+
|
701
|
+
def _make_sub_context(self, command: "Command") -> "Context":
|
702
|
+
"""Create a new context of the same type as this context, but
|
703
|
+
for a new command.
|
704
|
+
|
705
|
+
:meta private:
|
706
|
+
"""
|
707
|
+
return type(self)(command, info_name=command.name, parent=self)
|
708
|
+
|
709
|
+
def invoke(
|
710
|
+
__self, # noqa: B902
|
711
|
+
__callback: t.Union["Command", t.Callable[..., t.Any]],
|
712
|
+
*args: t.Any,
|
713
|
+
**kwargs: t.Any,
|
714
|
+
) -> t.Any:
|
715
|
+
"""Invokes a command callback in exactly the way it expects. There
|
716
|
+
are two ways to invoke this method:
|
717
|
+
|
718
|
+
1. the first argument can be a callback and all other arguments and
|
719
|
+
keyword arguments are forwarded directly to the function.
|
720
|
+
2. the first argument is a click command object. In that case all
|
721
|
+
arguments are forwarded as well but proper click parameters
|
722
|
+
(options and click arguments) must be keyword arguments and Click
|
723
|
+
will fill in defaults.
|
724
|
+
|
725
|
+
Note that before Click 3.2 keyword arguments were not properly filled
|
726
|
+
in against the intention of this code and no context was created. For
|
727
|
+
more information about this change and why it was done in a bugfix
|
728
|
+
release see :ref:`upgrade-to-3.2`.
|
729
|
+
|
730
|
+
.. versionchanged:: 8.0
|
731
|
+
All ``kwargs`` are tracked in :attr:`params` so they will be
|
732
|
+
passed if :meth:`forward` is called at multiple levels.
|
733
|
+
"""
|
734
|
+
if isinstance(__callback, Command):
|
735
|
+
other_cmd = __callback
|
736
|
+
|
737
|
+
if other_cmd.callback is None:
|
738
|
+
raise TypeError(
|
739
|
+
"The given command does not have a callback that can be invoked."
|
740
|
+
)
|
741
|
+
else:
|
742
|
+
__callback = other_cmd.callback
|
743
|
+
|
744
|
+
ctx = __self._make_sub_context(other_cmd)
|
745
|
+
|
746
|
+
for param in other_cmd.params:
|
747
|
+
if param.name not in kwargs and param.expose_value:
|
748
|
+
kwargs[param.name] = param.type_cast_value( # type: ignore
|
749
|
+
ctx, param.get_default(ctx)
|
750
|
+
)
|
751
|
+
|
752
|
+
# Track all kwargs as params, so that forward() will pass
|
753
|
+
# them on in subsequent calls.
|
754
|
+
ctx.params.update(kwargs)
|
755
|
+
else:
|
756
|
+
ctx = __self
|
757
|
+
|
758
|
+
with augment_usage_errors(__self):
|
759
|
+
with ctx:
|
760
|
+
return __callback(*args, **kwargs)
|
761
|
+
|
762
|
+
def forward(
|
763
|
+
__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902
|
764
|
+
) -> t.Any:
|
765
|
+
"""Similar to :meth:`invoke` but fills in default keyword
|
766
|
+
arguments from the current context if the other command expects
|
767
|
+
it. This cannot invoke callbacks directly, only other commands.
|
768
|
+
|
769
|
+
.. versionchanged:: 8.0
|
770
|
+
All ``kwargs`` are tracked in :attr:`params` so they will be
|
771
|
+
passed if ``forward`` is called at multiple levels.
|
772
|
+
"""
|
773
|
+
# Can only forward to other commands, not direct callbacks.
|
774
|
+
if not isinstance(__cmd, Command):
|
775
|
+
raise TypeError("Callback is not a command.")
|
776
|
+
|
777
|
+
for param in __self.params:
|
778
|
+
if param not in kwargs:
|
779
|
+
kwargs[param] = __self.params[param]
|
780
|
+
|
781
|
+
return __self.invoke(__cmd, *args, **kwargs)
|
782
|
+
|
783
|
+
def set_parameter_source(self, name: str, source: ParameterSource) -> None:
|
784
|
+
"""Set the source of a parameter. This indicates the location
|
785
|
+
from which the value of the parameter was obtained.
|
786
|
+
|
787
|
+
:param name: The name of the parameter.
|
788
|
+
:param source: A member of :class:`~click.core.ParameterSource`.
|
789
|
+
"""
|
790
|
+
self._parameter_source[name] = source
|
791
|
+
|
792
|
+
def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]:
|
793
|
+
"""Get the source of a parameter. This indicates the location
|
794
|
+
from which the value of the parameter was obtained.
|
795
|
+
|
796
|
+
This can be useful for determining when a user specified a value
|
797
|
+
on the command line that is the same as the default value. It
|
798
|
+
will be :attr:`~click.core.ParameterSource.DEFAULT` only if the
|
799
|
+
value was actually taken from the default.
|
800
|
+
|
801
|
+
:param name: The name of the parameter.
|
802
|
+
:rtype: ParameterSource
|
803
|
+
|
804
|
+
.. versionchanged:: 8.0
|
805
|
+
Returns ``None`` if the parameter was not provided from any
|
806
|
+
source.
|
807
|
+
"""
|
808
|
+
return self._parameter_source.get(name)
|
809
|
+
|
810
|
+
|
811
|
+
class BaseCommand:
|
812
|
+
"""The base command implements the minimal API contract of commands.
|
813
|
+
Most code will never use this as it does not implement a lot of useful
|
814
|
+
functionality but it can act as the direct subclass of alternative
|
815
|
+
parsing methods that do not depend on the Click parser.
|
816
|
+
|
817
|
+
For instance, this can be used to bridge Click and other systems like
|
818
|
+
argparse or docopt.
|
819
|
+
|
820
|
+
Because base commands do not implement a lot of the API that other
|
821
|
+
parts of Click take for granted, they are not supported for all
|
822
|
+
operations. For instance, they cannot be used with the decorators
|
823
|
+
usually and they have no built-in callback system.
|
824
|
+
|
825
|
+
.. versionchanged:: 2.0
|
826
|
+
Added the `context_settings` parameter.
|
827
|
+
|
828
|
+
:param name: the name of the command to use unless a group overrides it.
|
829
|
+
:param context_settings: an optional dictionary with defaults that are
|
830
|
+
passed to the context object.
|
831
|
+
"""
|
832
|
+
|
833
|
+
#: The context class to create with :meth:`make_context`.
|
834
|
+
#:
|
835
|
+
#: .. versionadded:: 8.0
|
836
|
+
context_class: t.Type[Context] = Context
|
837
|
+
#: the default for the :attr:`Context.allow_extra_args` flag.
|
838
|
+
allow_extra_args = False
|
839
|
+
#: the default for the :attr:`Context.allow_interspersed_args` flag.
|
840
|
+
allow_interspersed_args = True
|
841
|
+
#: the default for the :attr:`Context.ignore_unknown_options` flag.
|
842
|
+
ignore_unknown_options = False
|
843
|
+
|
844
|
+
def __init__(
|
845
|
+
self,
|
846
|
+
name: t.Optional[str],
|
847
|
+
context_settings: t.Optional[t.Dict[str, t.Any]] = None,
|
848
|
+
) -> None:
|
849
|
+
#: the name the command thinks it has. Upon registering a command
|
850
|
+
#: on a :class:`Group` the group will default the command name
|
851
|
+
#: with this information. You should instead use the
|
852
|
+
#: :class:`Context`\'s :attr:`~Context.info_name` attribute.
|
853
|
+
self.name = name
|
854
|
+
|
855
|
+
if context_settings is None:
|
856
|
+
context_settings = {}
|
857
|
+
|
858
|
+
#: an optional dictionary with defaults passed to the context.
|
859
|
+
self.context_settings: t.Dict[str, t.Any] = context_settings
|
860
|
+
|
861
|
+
def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]:
|
862
|
+
"""Gather information that could be useful for a tool generating
|
863
|
+
user-facing documentation. This traverses the entire structure
|
864
|
+
below this command.
|
865
|
+
|
866
|
+
Use :meth:`click.Context.to_info_dict` to traverse the entire
|
867
|
+
CLI structure.
|
868
|
+
|
869
|
+
:param ctx: A :class:`Context` representing this command.
|
870
|
+
|
871
|
+
.. versionadded:: 8.0
|
872
|
+
"""
|
873
|
+
return {"name": self.name}
|
874
|
+
|
875
|
+
def __repr__(self) -> str:
|
876
|
+
return f"<{self.__class__.__name__} {self.name}>"
|
877
|
+
|
878
|
+
def get_usage(self, ctx: Context) -> str:
|
879
|
+
raise NotImplementedError("Base commands cannot get usage")
|
880
|
+
|
881
|
+
def get_help(self, ctx: Context) -> str:
|
882
|
+
raise NotImplementedError("Base commands cannot get help")
|
883
|
+
|
884
|
+
def make_context(
|
885
|
+
self,
|
886
|
+
info_name: t.Optional[str],
|
887
|
+
args: t.List[str],
|
888
|
+
parent: t.Optional[Context] = None,
|
889
|
+
**extra: t.Any,
|
890
|
+
) -> Context:
|
891
|
+
"""This function when given an info name and arguments will kick
|
892
|
+
off the parsing and create a new :class:`Context`. It does not
|
893
|
+
invoke the actual command callback though.
|
894
|
+
|
895
|
+
To quickly customize the context class used without overriding
|
896
|
+
this method, set the :attr:`context_class` attribute.
|
897
|
+
|
898
|
+
:param info_name: the info name for this invocation. Generally this
|
899
|
+
is the most descriptive name for the script or
|
900
|
+
command. For the toplevel script it's usually
|
901
|
+
the name of the script, for commands below it it's
|
902
|
+
the name of the command.
|
903
|
+
:param args: the arguments to parse as list of strings.
|
904
|
+
:param parent: the parent context if available.
|
905
|
+
:param extra: extra keyword arguments forwarded to the context
|
906
|
+
constructor.
|
907
|
+
|
908
|
+
.. versionchanged:: 8.0
|
909
|
+
Added the :attr:`context_class` attribute.
|
910
|
+
"""
|
911
|
+
for key, value in self.context_settings.items():
|
912
|
+
if key not in extra:
|
913
|
+
extra[key] = value
|
914
|
+
|
915
|
+
ctx = self.context_class(
|
916
|
+
self, info_name=info_name, parent=parent, **extra # type: ignore
|
917
|
+
)
|
918
|
+
|
919
|
+
with ctx.scope(cleanup=False):
|
920
|
+
self.parse_args(ctx, args)
|
921
|
+
return ctx
|
922
|
+
|
923
|
+
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]:
|
924
|
+
"""Given a context and a list of arguments this creates the parser
|
925
|
+
and parses the arguments, then modifies the context as necessary.
|
926
|
+
This is automatically invoked by :meth:`make_context`.
|
927
|
+
"""
|
928
|
+
raise NotImplementedError("Base commands do not know how to parse arguments.")
|
929
|
+
|
930
|
+
def invoke(self, ctx: Context) -> t.Any:
|
931
|
+
"""Given a context, this invokes the command. The default
|
932
|
+
implementation is raising a not implemented error.
|
933
|
+
"""
|
934
|
+
raise NotImplementedError("Base commands are not invokable by default")
|
935
|
+
|
936
|
+
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]:
|
937
|
+
"""Return a list of completions for the incomplete value. Looks
|
938
|
+
at the names of chained multi-commands.
|
939
|
+
|
940
|
+
Any command could be part of a chained multi-command, so sibling
|
941
|
+
commands are valid at any point during command completion. Other
|
942
|
+
command classes will return more completions.
|
943
|
+
|
944
|
+
:param ctx: Invocation context for this command.
|
945
|
+
:param incomplete: Value being completed. May be empty.
|
946
|
+
|
947
|
+
.. versionadded:: 8.0
|
948
|
+
"""
|
949
|
+
from outerbounds._vendor.click.shell_completion import CompletionItem
|
950
|
+
|
951
|
+
results: t.List["CompletionItem"] = []
|
952
|
+
|
953
|
+
while ctx.parent is not None:
|
954
|
+
ctx = ctx.parent
|
955
|
+
|
956
|
+
if isinstance(ctx.command, MultiCommand) and ctx.command.chain:
|
957
|
+
results.extend(
|
958
|
+
CompletionItem(name, help=command.get_short_help_str())
|
959
|
+
for name, command in _complete_visible_commands(ctx, incomplete)
|
960
|
+
if name not in ctx.protected_args
|
961
|
+
)
|
962
|
+
|
963
|
+
return results
|
964
|
+
|
965
|
+
@t.overload
|
966
|
+
def main(
|
967
|
+
self,
|
968
|
+
args: t.Optional[t.Sequence[str]] = None,
|
969
|
+
prog_name: t.Optional[str] = None,
|
970
|
+
complete_var: t.Optional[str] = None,
|
971
|
+
standalone_mode: "te.Literal[True]" = True,
|
972
|
+
**extra: t.Any,
|
973
|
+
) -> "te.NoReturn":
|
974
|
+
...
|
975
|
+
|
976
|
+
@t.overload
|
977
|
+
def main(
|
978
|
+
self,
|
979
|
+
args: t.Optional[t.Sequence[str]] = None,
|
980
|
+
prog_name: t.Optional[str] = None,
|
981
|
+
complete_var: t.Optional[str] = None,
|
982
|
+
standalone_mode: bool = ...,
|
983
|
+
**extra: t.Any,
|
984
|
+
) -> t.Any:
|
985
|
+
...
|
986
|
+
|
987
|
+
def main(
|
988
|
+
self,
|
989
|
+
args: t.Optional[t.Sequence[str]] = None,
|
990
|
+
prog_name: t.Optional[str] = None,
|
991
|
+
complete_var: t.Optional[str] = None,
|
992
|
+
standalone_mode: bool = True,
|
993
|
+
windows_expand_args: bool = True,
|
994
|
+
**extra: t.Any,
|
995
|
+
) -> t.Any:
|
996
|
+
"""This is the way to invoke a script with all the bells and
|
997
|
+
whistles as a command line application. This will always terminate
|
998
|
+
the application after a call. If this is not wanted, ``SystemExit``
|
999
|
+
needs to be caught.
|
1000
|
+
|
1001
|
+
This method is also available by directly calling the instance of
|
1002
|
+
a :class:`Command`.
|
1003
|
+
|
1004
|
+
:param args: the arguments that should be used for parsing. If not
|
1005
|
+
provided, ``sys.argv[1:]`` is used.
|
1006
|
+
:param prog_name: the program name that should be used. By default
|
1007
|
+
the program name is constructed by taking the file
|
1008
|
+
name from ``sys.argv[0]``.
|
1009
|
+
:param complete_var: the environment variable that controls the
|
1010
|
+
bash completion support. The default is
|
1011
|
+
``"_<prog_name>_COMPLETE"`` with prog_name in
|
1012
|
+
uppercase.
|
1013
|
+
:param standalone_mode: the default behavior is to invoke the script
|
1014
|
+
in standalone mode. Click will then
|
1015
|
+
handle exceptions and convert them into
|
1016
|
+
error messages and the function will never
|
1017
|
+
return but shut down the interpreter. If
|
1018
|
+
this is set to `False` they will be
|
1019
|
+
propagated to the caller and the return
|
1020
|
+
value of this function is the return value
|
1021
|
+
of :meth:`invoke`.
|
1022
|
+
:param windows_expand_args: Expand glob patterns, user dir, and
|
1023
|
+
env vars in command line args on Windows.
|
1024
|
+
:param extra: extra keyword arguments are forwarded to the context
|
1025
|
+
constructor. See :class:`Context` for more information.
|
1026
|
+
|
1027
|
+
.. versionchanged:: 8.0.1
|
1028
|
+
Added the ``windows_expand_args`` parameter to allow
|
1029
|
+
disabling command line arg expansion on Windows.
|
1030
|
+
|
1031
|
+
.. versionchanged:: 8.0
|
1032
|
+
When taking arguments from ``sys.argv`` on Windows, glob
|
1033
|
+
patterns, user dir, and env vars are expanded.
|
1034
|
+
|
1035
|
+
.. versionchanged:: 3.0
|
1036
|
+
Added the ``standalone_mode`` parameter.
|
1037
|
+
"""
|
1038
|
+
if args is None:
|
1039
|
+
args = sys.argv[1:]
|
1040
|
+
|
1041
|
+
if os.name == "nt" and windows_expand_args:
|
1042
|
+
args = _expand_args(args)
|
1043
|
+
else:
|
1044
|
+
args = list(args)
|
1045
|
+
|
1046
|
+
if prog_name is None:
|
1047
|
+
prog_name = _detect_program_name()
|
1048
|
+
|
1049
|
+
# Process shell completion requests and exit early.
|
1050
|
+
self._main_shell_completion(extra, prog_name, complete_var)
|
1051
|
+
|
1052
|
+
try:
|
1053
|
+
try:
|
1054
|
+
with self.make_context(prog_name, args, **extra) as ctx:
|
1055
|
+
rv = self.invoke(ctx)
|
1056
|
+
if not standalone_mode:
|
1057
|
+
return rv
|
1058
|
+
# it's not safe to `ctx.exit(rv)` here!
|
1059
|
+
# note that `rv` may actually contain data like "1" which
|
1060
|
+
# has obvious effects
|
1061
|
+
# more subtle case: `rv=[None, None]` can come out of
|
1062
|
+
# chained commands which all returned `None` -- so it's not
|
1063
|
+
# even always obvious that `rv` indicates success/failure
|
1064
|
+
# by its truthiness/falsiness
|
1065
|
+
ctx.exit()
|
1066
|
+
except (EOFError, KeyboardInterrupt):
|
1067
|
+
echo(file=sys.stderr)
|
1068
|
+
raise Abort() from None
|
1069
|
+
except ClickException as e:
|
1070
|
+
if not standalone_mode:
|
1071
|
+
raise
|
1072
|
+
e.show()
|
1073
|
+
sys.exit(e.exit_code)
|
1074
|
+
except OSError as e:
|
1075
|
+
if e.errno == errno.EPIPE:
|
1076
|
+
sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout))
|
1077
|
+
sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr))
|
1078
|
+
sys.exit(1)
|
1079
|
+
else:
|
1080
|
+
raise
|
1081
|
+
except Exit as e:
|
1082
|
+
if standalone_mode:
|
1083
|
+
sys.exit(e.exit_code)
|
1084
|
+
else:
|
1085
|
+
# in non-standalone mode, return the exit code
|
1086
|
+
# note that this is only reached if `self.invoke` above raises
|
1087
|
+
# an Exit explicitly -- thus bypassing the check there which
|
1088
|
+
# would return its result
|
1089
|
+
# the results of non-standalone execution may therefore be
|
1090
|
+
# somewhat ambiguous: if there are codepaths which lead to
|
1091
|
+
# `ctx.exit(1)` and to `return 1`, the caller won't be able to
|
1092
|
+
# tell the difference between the two
|
1093
|
+
return e.exit_code
|
1094
|
+
except Abort:
|
1095
|
+
if not standalone_mode:
|
1096
|
+
raise
|
1097
|
+
echo(_("Aborted!"), file=sys.stderr)
|
1098
|
+
sys.exit(1)
|
1099
|
+
|
1100
|
+
def _main_shell_completion(
|
1101
|
+
self,
|
1102
|
+
ctx_args: t.Dict[str, t.Any],
|
1103
|
+
prog_name: str,
|
1104
|
+
complete_var: t.Optional[str] = None,
|
1105
|
+
) -> None:
|
1106
|
+
"""Check if the shell is asking for tab completion, process
|
1107
|
+
that, then exit early. Called from :meth:`main` before the
|
1108
|
+
program is invoked.
|
1109
|
+
|
1110
|
+
:param prog_name: Name of the executable in the shell.
|
1111
|
+
:param complete_var: Name of the environment variable that holds
|
1112
|
+
the completion instruction. Defaults to
|
1113
|
+
``_{PROG_NAME}_COMPLETE``.
|
1114
|
+
"""
|
1115
|
+
if complete_var is None:
|
1116
|
+
complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper()
|
1117
|
+
|
1118
|
+
instruction = os.environ.get(complete_var)
|
1119
|
+
|
1120
|
+
if not instruction:
|
1121
|
+
return
|
1122
|
+
|
1123
|
+
from .shell_completion import shell_complete
|
1124
|
+
|
1125
|
+
rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction)
|
1126
|
+
sys.exit(rv)
|
1127
|
+
|
1128
|
+
def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
1129
|
+
"""Alias for :meth:`main`."""
|
1130
|
+
return self.main(*args, **kwargs)
|
1131
|
+
|
1132
|
+
|
1133
|
+
class Command(BaseCommand):
|
1134
|
+
"""Commands are the basic building block of command line interfaces in
|
1135
|
+
Click. A basic command handles command line parsing and might dispatch
|
1136
|
+
more parsing to commands nested below it.
|
1137
|
+
|
1138
|
+
:param name: the name of the command to use unless a group overrides it.
|
1139
|
+
:param context_settings: an optional dictionary with defaults that are
|
1140
|
+
passed to the context object.
|
1141
|
+
:param callback: the callback to invoke. This is optional.
|
1142
|
+
:param params: the parameters to register with this command. This can
|
1143
|
+
be either :class:`Option` or :class:`Argument` objects.
|
1144
|
+
:param help: the help string to use for this command.
|
1145
|
+
:param epilog: like the help string but it's printed at the end of the
|
1146
|
+
help page after everything else.
|
1147
|
+
:param short_help: the short help to use for this command. This is
|
1148
|
+
shown on the command listing of the parent command.
|
1149
|
+
:param add_help_option: by default each command registers a ``--help``
|
1150
|
+
option. This can be disabled by this parameter.
|
1151
|
+
:param no_args_is_help: this controls what happens if no arguments are
|
1152
|
+
provided. This option is disabled by default.
|
1153
|
+
If enabled this will add ``--help`` as argument
|
1154
|
+
if no arguments are passed
|
1155
|
+
:param hidden: hide this command from help outputs.
|
1156
|
+
|
1157
|
+
:param deprecated: issues a message indicating that
|
1158
|
+
the command is deprecated.
|
1159
|
+
|
1160
|
+
.. versionchanged:: 8.1
|
1161
|
+
``help``, ``epilog``, and ``short_help`` are stored unprocessed,
|
1162
|
+
all formatting is done when outputting help text, not at init,
|
1163
|
+
and is done even if not using the ``@command`` decorator.
|
1164
|
+
|
1165
|
+
.. versionchanged:: 8.0
|
1166
|
+
Added a ``repr`` showing the command name.
|
1167
|
+
|
1168
|
+
.. versionchanged:: 7.1
|
1169
|
+
Added the ``no_args_is_help`` parameter.
|
1170
|
+
|
1171
|
+
.. versionchanged:: 2.0
|
1172
|
+
Added the ``context_settings`` parameter.
|
1173
|
+
"""
|
1174
|
+
|
1175
|
+
def __init__(
|
1176
|
+
self,
|
1177
|
+
name: t.Optional[str],
|
1178
|
+
context_settings: t.Optional[t.Dict[str, t.Any]] = None,
|
1179
|
+
callback: t.Optional[t.Callable[..., t.Any]] = None,
|
1180
|
+
params: t.Optional[t.List["Parameter"]] = None,
|
1181
|
+
help: t.Optional[str] = None,
|
1182
|
+
epilog: t.Optional[str] = None,
|
1183
|
+
short_help: t.Optional[str] = None,
|
1184
|
+
options_metavar: t.Optional[str] = "[OPTIONS]",
|
1185
|
+
add_help_option: bool = True,
|
1186
|
+
no_args_is_help: bool = False,
|
1187
|
+
hidden: bool = False,
|
1188
|
+
deprecated: bool = False,
|
1189
|
+
) -> None:
|
1190
|
+
super().__init__(name, context_settings)
|
1191
|
+
#: the callback to execute when the command fires. This might be
|
1192
|
+
#: `None` in which case nothing happens.
|
1193
|
+
self.callback = callback
|
1194
|
+
#: the list of parameters for this command in the order they
|
1195
|
+
#: should show up in the help page and execute. Eager parameters
|
1196
|
+
#: will automatically be handled before non eager ones.
|
1197
|
+
self.params: t.List["Parameter"] = params or []
|
1198
|
+
self.help = help
|
1199
|
+
self.epilog = epilog
|
1200
|
+
self.options_metavar = options_metavar
|
1201
|
+
self.short_help = short_help
|
1202
|
+
self.add_help_option = add_help_option
|
1203
|
+
self.no_args_is_help = no_args_is_help
|
1204
|
+
self.hidden = hidden
|
1205
|
+
self.deprecated = deprecated
|
1206
|
+
|
1207
|
+
def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]:
|
1208
|
+
info_dict = super().to_info_dict(ctx)
|
1209
|
+
info_dict.update(
|
1210
|
+
params=[param.to_info_dict() for param in self.get_params(ctx)],
|
1211
|
+
help=self.help,
|
1212
|
+
epilog=self.epilog,
|
1213
|
+
short_help=self.short_help,
|
1214
|
+
hidden=self.hidden,
|
1215
|
+
deprecated=self.deprecated,
|
1216
|
+
)
|
1217
|
+
return info_dict
|
1218
|
+
|
1219
|
+
def get_usage(self, ctx: Context) -> str:
|
1220
|
+
"""Formats the usage line into a string and returns it.
|
1221
|
+
|
1222
|
+
Calls :meth:`format_usage` internally.
|
1223
|
+
"""
|
1224
|
+
formatter = ctx.make_formatter()
|
1225
|
+
self.format_usage(ctx, formatter)
|
1226
|
+
return formatter.getvalue().rstrip("\n")
|
1227
|
+
|
1228
|
+
def get_params(self, ctx: Context) -> t.List["Parameter"]:
|
1229
|
+
rv = self.params
|
1230
|
+
help_option = self.get_help_option(ctx)
|
1231
|
+
|
1232
|
+
if help_option is not None:
|
1233
|
+
rv = [*rv, help_option]
|
1234
|
+
|
1235
|
+
return rv
|
1236
|
+
|
1237
|
+
def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1238
|
+
"""Writes the usage line into the formatter.
|
1239
|
+
|
1240
|
+
This is a low-level method called by :meth:`get_usage`.
|
1241
|
+
"""
|
1242
|
+
pieces = self.collect_usage_pieces(ctx)
|
1243
|
+
formatter.write_usage(ctx.command_path, " ".join(pieces))
|
1244
|
+
|
1245
|
+
def collect_usage_pieces(self, ctx: Context) -> t.List[str]:
|
1246
|
+
"""Returns all the pieces that go into the usage line and returns
|
1247
|
+
it as a list of strings.
|
1248
|
+
"""
|
1249
|
+
rv = [self.options_metavar] if self.options_metavar else []
|
1250
|
+
|
1251
|
+
for param in self.get_params(ctx):
|
1252
|
+
rv.extend(param.get_usage_pieces(ctx))
|
1253
|
+
|
1254
|
+
return rv
|
1255
|
+
|
1256
|
+
def get_help_option_names(self, ctx: Context) -> t.List[str]:
|
1257
|
+
"""Returns the names for the help option."""
|
1258
|
+
all_names = set(ctx.help_option_names)
|
1259
|
+
for param in self.params:
|
1260
|
+
all_names.difference_update(param.opts)
|
1261
|
+
all_names.difference_update(param.secondary_opts)
|
1262
|
+
return list(all_names)
|
1263
|
+
|
1264
|
+
def get_help_option(self, ctx: Context) -> t.Optional["Option"]:
|
1265
|
+
"""Returns the help option object."""
|
1266
|
+
help_options = self.get_help_option_names(ctx)
|
1267
|
+
|
1268
|
+
if not help_options or not self.add_help_option:
|
1269
|
+
return None
|
1270
|
+
|
1271
|
+
def show_help(ctx: Context, param: "Parameter", value: str) -> None:
|
1272
|
+
if value and not ctx.resilient_parsing:
|
1273
|
+
echo(ctx.get_help(), color=ctx.color)
|
1274
|
+
ctx.exit()
|
1275
|
+
|
1276
|
+
return Option(
|
1277
|
+
help_options,
|
1278
|
+
is_flag=True,
|
1279
|
+
is_eager=True,
|
1280
|
+
expose_value=False,
|
1281
|
+
callback=show_help,
|
1282
|
+
help=_("Show this message and exit."),
|
1283
|
+
)
|
1284
|
+
|
1285
|
+
def make_parser(self, ctx: Context) -> OptionParser:
|
1286
|
+
"""Creates the underlying option parser for this command."""
|
1287
|
+
parser = OptionParser(ctx)
|
1288
|
+
for param in self.get_params(ctx):
|
1289
|
+
param.add_to_parser(parser, ctx)
|
1290
|
+
return parser
|
1291
|
+
|
1292
|
+
def get_help(self, ctx: Context) -> str:
|
1293
|
+
"""Formats the help into a string and returns it.
|
1294
|
+
|
1295
|
+
Calls :meth:`format_help` internally.
|
1296
|
+
"""
|
1297
|
+
formatter = ctx.make_formatter()
|
1298
|
+
self.format_help(ctx, formatter)
|
1299
|
+
return formatter.getvalue().rstrip("\n")
|
1300
|
+
|
1301
|
+
def get_short_help_str(self, limit: int = 45) -> str:
|
1302
|
+
"""Gets short help for the command or makes it by shortening the
|
1303
|
+
long help string.
|
1304
|
+
"""
|
1305
|
+
if self.short_help:
|
1306
|
+
text = inspect.cleandoc(self.short_help)
|
1307
|
+
elif self.help:
|
1308
|
+
text = make_default_short_help(self.help, limit)
|
1309
|
+
else:
|
1310
|
+
text = ""
|
1311
|
+
|
1312
|
+
if self.deprecated:
|
1313
|
+
text = _("(Deprecated) {text}").format(text=text)
|
1314
|
+
|
1315
|
+
return text.strip()
|
1316
|
+
|
1317
|
+
def format_help(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1318
|
+
"""Writes the help into the formatter if it exists.
|
1319
|
+
|
1320
|
+
This is a low-level method called by :meth:`get_help`.
|
1321
|
+
|
1322
|
+
This calls the following methods:
|
1323
|
+
|
1324
|
+
- :meth:`format_usage`
|
1325
|
+
- :meth:`format_help_text`
|
1326
|
+
- :meth:`format_options`
|
1327
|
+
- :meth:`format_epilog`
|
1328
|
+
"""
|
1329
|
+
self.format_usage(ctx, formatter)
|
1330
|
+
self.format_help_text(ctx, formatter)
|
1331
|
+
self.format_options(ctx, formatter)
|
1332
|
+
self.format_epilog(ctx, formatter)
|
1333
|
+
|
1334
|
+
def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1335
|
+
"""Writes the help text to the formatter if it exists."""
|
1336
|
+
text = self.help if self.help is not None else ""
|
1337
|
+
|
1338
|
+
if self.deprecated:
|
1339
|
+
text = _("(Deprecated) {text}").format(text=text)
|
1340
|
+
|
1341
|
+
if text:
|
1342
|
+
text = inspect.cleandoc(text).partition("\f")[0]
|
1343
|
+
formatter.write_paragraph()
|
1344
|
+
|
1345
|
+
with formatter.indentation():
|
1346
|
+
formatter.write_text(text)
|
1347
|
+
|
1348
|
+
def format_options(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1349
|
+
"""Writes all the options into the formatter if they exist."""
|
1350
|
+
opts = []
|
1351
|
+
for param in self.get_params(ctx):
|
1352
|
+
rv = param.get_help_record(ctx)
|
1353
|
+
if rv is not None:
|
1354
|
+
opts.append(rv)
|
1355
|
+
|
1356
|
+
if opts:
|
1357
|
+
with formatter.section(_("Options")):
|
1358
|
+
formatter.write_dl(opts)
|
1359
|
+
|
1360
|
+
def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1361
|
+
"""Writes the epilog into the formatter if it exists."""
|
1362
|
+
if self.epilog:
|
1363
|
+
epilog = inspect.cleandoc(self.epilog)
|
1364
|
+
formatter.write_paragraph()
|
1365
|
+
|
1366
|
+
with formatter.indentation():
|
1367
|
+
formatter.write_text(epilog)
|
1368
|
+
|
1369
|
+
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]:
|
1370
|
+
if not args and self.no_args_is_help and not ctx.resilient_parsing:
|
1371
|
+
echo(ctx.get_help(), color=ctx.color)
|
1372
|
+
ctx.exit()
|
1373
|
+
|
1374
|
+
parser = self.make_parser(ctx)
|
1375
|
+
opts, args, param_order = parser.parse_args(args=args)
|
1376
|
+
|
1377
|
+
for param in iter_params_for_processing(param_order, self.get_params(ctx)):
|
1378
|
+
value, args = param.handle_parse_result(ctx, opts, args)
|
1379
|
+
|
1380
|
+
if args and not ctx.allow_extra_args and not ctx.resilient_parsing:
|
1381
|
+
ctx.fail(
|
1382
|
+
ngettext(
|
1383
|
+
"Got unexpected extra argument ({args})",
|
1384
|
+
"Got unexpected extra arguments ({args})",
|
1385
|
+
len(args),
|
1386
|
+
).format(args=" ".join(map(str, args)))
|
1387
|
+
)
|
1388
|
+
|
1389
|
+
ctx.args = args
|
1390
|
+
ctx._opt_prefixes.update(parser._opt_prefixes)
|
1391
|
+
return args
|
1392
|
+
|
1393
|
+
def invoke(self, ctx: Context) -> t.Any:
|
1394
|
+
"""Given a context, this invokes the attached callback (if it exists)
|
1395
|
+
in the right way.
|
1396
|
+
"""
|
1397
|
+
if self.deprecated:
|
1398
|
+
message = _(
|
1399
|
+
"DeprecationWarning: The command {name!r} is deprecated."
|
1400
|
+
).format(name=self.name)
|
1401
|
+
echo(style(message, fg="red"), err=True)
|
1402
|
+
|
1403
|
+
if self.callback is not None:
|
1404
|
+
return ctx.invoke(self.callback, **ctx.params)
|
1405
|
+
|
1406
|
+
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]:
|
1407
|
+
"""Return a list of completions for the incomplete value. Looks
|
1408
|
+
at the names of options and chained multi-commands.
|
1409
|
+
|
1410
|
+
:param ctx: Invocation context for this command.
|
1411
|
+
:param incomplete: Value being completed. May be empty.
|
1412
|
+
|
1413
|
+
.. versionadded:: 8.0
|
1414
|
+
"""
|
1415
|
+
from outerbounds._vendor.click.shell_completion import CompletionItem
|
1416
|
+
|
1417
|
+
results: t.List["CompletionItem"] = []
|
1418
|
+
|
1419
|
+
if incomplete and not incomplete[0].isalnum():
|
1420
|
+
for param in self.get_params(ctx):
|
1421
|
+
if (
|
1422
|
+
not isinstance(param, Option)
|
1423
|
+
or param.hidden
|
1424
|
+
or (
|
1425
|
+
not param.multiple
|
1426
|
+
and ctx.get_parameter_source(param.name) # type: ignore
|
1427
|
+
is ParameterSource.COMMANDLINE
|
1428
|
+
)
|
1429
|
+
):
|
1430
|
+
continue
|
1431
|
+
|
1432
|
+
results.extend(
|
1433
|
+
CompletionItem(name, help=param.help)
|
1434
|
+
for name in [*param.opts, *param.secondary_opts]
|
1435
|
+
if name.startswith(incomplete)
|
1436
|
+
)
|
1437
|
+
|
1438
|
+
results.extend(super().shell_complete(ctx, incomplete))
|
1439
|
+
return results
|
1440
|
+
|
1441
|
+
|
1442
|
+
class MultiCommand(Command):
|
1443
|
+
"""A multi command is the basic implementation of a command that
|
1444
|
+
dispatches to subcommands. The most common version is the
|
1445
|
+
:class:`Group`.
|
1446
|
+
|
1447
|
+
:param invoke_without_command: this controls how the multi command itself
|
1448
|
+
is invoked. By default it's only invoked
|
1449
|
+
if a subcommand is provided.
|
1450
|
+
:param no_args_is_help: this controls what happens if no arguments are
|
1451
|
+
provided. This option is enabled by default if
|
1452
|
+
`invoke_without_command` is disabled or disabled
|
1453
|
+
if it's enabled. If enabled this will add
|
1454
|
+
``--help`` as argument if no arguments are
|
1455
|
+
passed.
|
1456
|
+
:param subcommand_metavar: the string that is used in the documentation
|
1457
|
+
to indicate the subcommand place.
|
1458
|
+
:param chain: if this is set to `True` chaining of multiple subcommands
|
1459
|
+
is enabled. This restricts the form of commands in that
|
1460
|
+
they cannot have optional arguments but it allows
|
1461
|
+
multiple commands to be chained together.
|
1462
|
+
:param result_callback: The result callback to attach to this multi
|
1463
|
+
command. This can be set or changed later with the
|
1464
|
+
:meth:`result_callback` decorator.
|
1465
|
+
"""
|
1466
|
+
|
1467
|
+
allow_extra_args = True
|
1468
|
+
allow_interspersed_args = False
|
1469
|
+
|
1470
|
+
def __init__(
|
1471
|
+
self,
|
1472
|
+
name: t.Optional[str] = None,
|
1473
|
+
invoke_without_command: bool = False,
|
1474
|
+
no_args_is_help: t.Optional[bool] = None,
|
1475
|
+
subcommand_metavar: t.Optional[str] = None,
|
1476
|
+
chain: bool = False,
|
1477
|
+
result_callback: t.Optional[t.Callable[..., t.Any]] = None,
|
1478
|
+
**attrs: t.Any,
|
1479
|
+
) -> None:
|
1480
|
+
super().__init__(name, **attrs)
|
1481
|
+
|
1482
|
+
if no_args_is_help is None:
|
1483
|
+
no_args_is_help = not invoke_without_command
|
1484
|
+
|
1485
|
+
self.no_args_is_help = no_args_is_help
|
1486
|
+
self.invoke_without_command = invoke_without_command
|
1487
|
+
|
1488
|
+
if subcommand_metavar is None:
|
1489
|
+
if chain:
|
1490
|
+
subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..."
|
1491
|
+
else:
|
1492
|
+
subcommand_metavar = "COMMAND [ARGS]..."
|
1493
|
+
|
1494
|
+
self.subcommand_metavar = subcommand_metavar
|
1495
|
+
self.chain = chain
|
1496
|
+
# The result callback that is stored. This can be set or
|
1497
|
+
# overridden with the :func:`result_callback` decorator.
|
1498
|
+
self._result_callback = result_callback
|
1499
|
+
|
1500
|
+
if self.chain:
|
1501
|
+
for param in self.params:
|
1502
|
+
if isinstance(param, Argument) and not param.required:
|
1503
|
+
raise RuntimeError(
|
1504
|
+
"Multi commands in chain mode cannot have"
|
1505
|
+
" optional arguments."
|
1506
|
+
)
|
1507
|
+
|
1508
|
+
def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]:
|
1509
|
+
info_dict = super().to_info_dict(ctx)
|
1510
|
+
commands = {}
|
1511
|
+
|
1512
|
+
for name in self.list_commands(ctx):
|
1513
|
+
command = self.get_command(ctx, name)
|
1514
|
+
|
1515
|
+
if command is None:
|
1516
|
+
continue
|
1517
|
+
|
1518
|
+
sub_ctx = ctx._make_sub_context(command)
|
1519
|
+
|
1520
|
+
with sub_ctx.scope(cleanup=False):
|
1521
|
+
commands[name] = command.to_info_dict(sub_ctx)
|
1522
|
+
|
1523
|
+
info_dict.update(commands=commands, chain=self.chain)
|
1524
|
+
return info_dict
|
1525
|
+
|
1526
|
+
def collect_usage_pieces(self, ctx: Context) -> t.List[str]:
|
1527
|
+
rv = super().collect_usage_pieces(ctx)
|
1528
|
+
rv.append(self.subcommand_metavar)
|
1529
|
+
return rv
|
1530
|
+
|
1531
|
+
def format_options(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1532
|
+
super().format_options(ctx, formatter)
|
1533
|
+
self.format_commands(ctx, formatter)
|
1534
|
+
|
1535
|
+
def result_callback(self, replace: bool = False) -> t.Callable[[F], F]:
|
1536
|
+
"""Adds a result callback to the command. By default if a
|
1537
|
+
result callback is already registered this will chain them but
|
1538
|
+
this can be disabled with the `replace` parameter. The result
|
1539
|
+
callback is invoked with the return value of the subcommand
|
1540
|
+
(or the list of return values from all subcommands if chaining
|
1541
|
+
is enabled) as well as the parameters as they would be passed
|
1542
|
+
to the main callback.
|
1543
|
+
|
1544
|
+
Example::
|
1545
|
+
|
1546
|
+
@click.group()
|
1547
|
+
@click.option('-i', '--input', default=23)
|
1548
|
+
def cli(input):
|
1549
|
+
return 42
|
1550
|
+
|
1551
|
+
@cli.result_callback()
|
1552
|
+
def process_result(result, input):
|
1553
|
+
return result + input
|
1554
|
+
|
1555
|
+
:param replace: if set to `True` an already existing result
|
1556
|
+
callback will be removed.
|
1557
|
+
|
1558
|
+
.. versionchanged:: 8.0
|
1559
|
+
Renamed from ``resultcallback``.
|
1560
|
+
|
1561
|
+
.. versionadded:: 3.0
|
1562
|
+
"""
|
1563
|
+
|
1564
|
+
def decorator(f: F) -> F:
|
1565
|
+
old_callback = self._result_callback
|
1566
|
+
|
1567
|
+
if old_callback is None or replace:
|
1568
|
+
self._result_callback = f
|
1569
|
+
return f
|
1570
|
+
|
1571
|
+
def function(__value, *args, **kwargs): # type: ignore
|
1572
|
+
inner = old_callback(__value, *args, **kwargs) # type: ignore
|
1573
|
+
return f(inner, *args, **kwargs)
|
1574
|
+
|
1575
|
+
self._result_callback = rv = update_wrapper(t.cast(F, function), f)
|
1576
|
+
return rv
|
1577
|
+
|
1578
|
+
return decorator
|
1579
|
+
|
1580
|
+
def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None:
|
1581
|
+
"""Extra format methods for multi methods that adds all the commands
|
1582
|
+
after the options.
|
1583
|
+
"""
|
1584
|
+
commands = []
|
1585
|
+
for subcommand in self.list_commands(ctx):
|
1586
|
+
cmd = self.get_command(ctx, subcommand)
|
1587
|
+
# What is this, the tool lied about a command. Ignore it
|
1588
|
+
if cmd is None:
|
1589
|
+
continue
|
1590
|
+
if cmd.hidden:
|
1591
|
+
continue
|
1592
|
+
|
1593
|
+
commands.append((subcommand, cmd))
|
1594
|
+
|
1595
|
+
# allow for 3 times the default spacing
|
1596
|
+
if len(commands):
|
1597
|
+
limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands)
|
1598
|
+
|
1599
|
+
rows = []
|
1600
|
+
for subcommand, cmd in commands:
|
1601
|
+
help = cmd.get_short_help_str(limit)
|
1602
|
+
rows.append((subcommand, help))
|
1603
|
+
|
1604
|
+
if rows:
|
1605
|
+
with formatter.section(_("Commands")):
|
1606
|
+
formatter.write_dl(rows)
|
1607
|
+
|
1608
|
+
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]:
|
1609
|
+
if not args and self.no_args_is_help and not ctx.resilient_parsing:
|
1610
|
+
echo(ctx.get_help(), color=ctx.color)
|
1611
|
+
ctx.exit()
|
1612
|
+
|
1613
|
+
rest = super().parse_args(ctx, args)
|
1614
|
+
|
1615
|
+
if self.chain:
|
1616
|
+
ctx.protected_args = rest
|
1617
|
+
ctx.args = []
|
1618
|
+
elif rest:
|
1619
|
+
ctx.protected_args, ctx.args = rest[:1], rest[1:]
|
1620
|
+
|
1621
|
+
return ctx.args
|
1622
|
+
|
1623
|
+
def invoke(self, ctx: Context) -> t.Any:
|
1624
|
+
def _process_result(value: t.Any) -> t.Any:
|
1625
|
+
if self._result_callback is not None:
|
1626
|
+
value = ctx.invoke(self._result_callback, value, **ctx.params)
|
1627
|
+
return value
|
1628
|
+
|
1629
|
+
if not ctx.protected_args:
|
1630
|
+
if self.invoke_without_command:
|
1631
|
+
# No subcommand was invoked, so the result callback is
|
1632
|
+
# invoked with the group return value for regular
|
1633
|
+
# groups, or an empty list for chained groups.
|
1634
|
+
with ctx:
|
1635
|
+
rv = super().invoke(ctx)
|
1636
|
+
return _process_result([] if self.chain else rv)
|
1637
|
+
ctx.fail(_("Missing command."))
|
1638
|
+
|
1639
|
+
# Fetch args back out
|
1640
|
+
args = [*ctx.protected_args, *ctx.args]
|
1641
|
+
ctx.args = []
|
1642
|
+
ctx.protected_args = []
|
1643
|
+
|
1644
|
+
# If we're not in chain mode, we only allow the invocation of a
|
1645
|
+
# single command but we also inform the current context about the
|
1646
|
+
# name of the command to invoke.
|
1647
|
+
if not self.chain:
|
1648
|
+
# Make sure the context is entered so we do not clean up
|
1649
|
+
# resources until the result processor has worked.
|
1650
|
+
with ctx:
|
1651
|
+
cmd_name, cmd, args = self.resolve_command(ctx, args)
|
1652
|
+
assert cmd is not None
|
1653
|
+
ctx.invoked_subcommand = cmd_name
|
1654
|
+
super().invoke(ctx)
|
1655
|
+
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
|
1656
|
+
with sub_ctx:
|
1657
|
+
return _process_result(sub_ctx.command.invoke(sub_ctx))
|
1658
|
+
|
1659
|
+
# In chain mode we create the contexts step by step, but after the
|
1660
|
+
# base command has been invoked. Because at that point we do not
|
1661
|
+
# know the subcommands yet, the invoked subcommand attribute is
|
1662
|
+
# set to ``*`` to inform the command that subcommands are executed
|
1663
|
+
# but nothing else.
|
1664
|
+
with ctx:
|
1665
|
+
ctx.invoked_subcommand = "*" if args else None
|
1666
|
+
super().invoke(ctx)
|
1667
|
+
|
1668
|
+
# Otherwise we make every single context and invoke them in a
|
1669
|
+
# chain. In that case the return value to the result processor
|
1670
|
+
# is the list of all invoked subcommand's results.
|
1671
|
+
contexts = []
|
1672
|
+
while args:
|
1673
|
+
cmd_name, cmd, args = self.resolve_command(ctx, args)
|
1674
|
+
assert cmd is not None
|
1675
|
+
sub_ctx = cmd.make_context(
|
1676
|
+
cmd_name,
|
1677
|
+
args,
|
1678
|
+
parent=ctx,
|
1679
|
+
allow_extra_args=True,
|
1680
|
+
allow_interspersed_args=False,
|
1681
|
+
)
|
1682
|
+
contexts.append(sub_ctx)
|
1683
|
+
args, sub_ctx.args = sub_ctx.args, []
|
1684
|
+
|
1685
|
+
rv = []
|
1686
|
+
for sub_ctx in contexts:
|
1687
|
+
with sub_ctx:
|
1688
|
+
rv.append(sub_ctx.command.invoke(sub_ctx))
|
1689
|
+
return _process_result(rv)
|
1690
|
+
|
1691
|
+
def resolve_command(
|
1692
|
+
self, ctx: Context, args: t.List[str]
|
1693
|
+
) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]:
|
1694
|
+
cmd_name = make_str(args[0])
|
1695
|
+
original_cmd_name = cmd_name
|
1696
|
+
|
1697
|
+
# Get the command
|
1698
|
+
cmd = self.get_command(ctx, cmd_name)
|
1699
|
+
|
1700
|
+
# If we can't find the command but there is a normalization
|
1701
|
+
# function available, we try with that one.
|
1702
|
+
if cmd is None and ctx.token_normalize_func is not None:
|
1703
|
+
cmd_name = ctx.token_normalize_func(cmd_name)
|
1704
|
+
cmd = self.get_command(ctx, cmd_name)
|
1705
|
+
|
1706
|
+
# If we don't find the command we want to show an error message
|
1707
|
+
# to the user that it was not provided. However, there is
|
1708
|
+
# something else we should do: if the first argument looks like
|
1709
|
+
# an option we want to kick off parsing again for arguments to
|
1710
|
+
# resolve things like --help which now should go to the main
|
1711
|
+
# place.
|
1712
|
+
if cmd is None and not ctx.resilient_parsing:
|
1713
|
+
if split_opt(cmd_name)[0]:
|
1714
|
+
self.parse_args(ctx, ctx.args)
|
1715
|
+
ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name))
|
1716
|
+
return cmd_name if cmd else None, cmd, args[1:]
|
1717
|
+
|
1718
|
+
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]:
|
1719
|
+
"""Given a context and a command name, this returns a
|
1720
|
+
:class:`Command` object if it exists or returns `None`.
|
1721
|
+
"""
|
1722
|
+
raise NotImplementedError
|
1723
|
+
|
1724
|
+
def list_commands(self, ctx: Context) -> t.List[str]:
|
1725
|
+
"""Returns a list of subcommand names in the order they should
|
1726
|
+
appear.
|
1727
|
+
"""
|
1728
|
+
return []
|
1729
|
+
|
1730
|
+
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]:
|
1731
|
+
"""Return a list of completions for the incomplete value. Looks
|
1732
|
+
at the names of options, subcommands, and chained
|
1733
|
+
multi-commands.
|
1734
|
+
|
1735
|
+
:param ctx: Invocation context for this command.
|
1736
|
+
:param incomplete: Value being completed. May be empty.
|
1737
|
+
|
1738
|
+
.. versionadded:: 8.0
|
1739
|
+
"""
|
1740
|
+
from outerbounds._vendor.click.shell_completion import CompletionItem
|
1741
|
+
|
1742
|
+
results = [
|
1743
|
+
CompletionItem(name, help=command.get_short_help_str())
|
1744
|
+
for name, command in _complete_visible_commands(ctx, incomplete)
|
1745
|
+
]
|
1746
|
+
results.extend(super().shell_complete(ctx, incomplete))
|
1747
|
+
return results
|
1748
|
+
|
1749
|
+
|
1750
|
+
class Group(MultiCommand):
|
1751
|
+
"""A group allows a command to have subcommands attached. This is
|
1752
|
+
the most common way to implement nesting in Click.
|
1753
|
+
|
1754
|
+
:param name: The name of the group command.
|
1755
|
+
:param commands: A dict mapping names to :class:`Command` objects.
|
1756
|
+
Can also be a list of :class:`Command`, which will use
|
1757
|
+
:attr:`Command.name` to create the dict.
|
1758
|
+
:param attrs: Other command arguments described in
|
1759
|
+
:class:`MultiCommand`, :class:`Command`, and
|
1760
|
+
:class:`BaseCommand`.
|
1761
|
+
|
1762
|
+
.. versionchanged:: 8.0
|
1763
|
+
The ``commmands`` argument can be a list of command objects.
|
1764
|
+
"""
|
1765
|
+
|
1766
|
+
#: If set, this is used by the group's :meth:`command` decorator
|
1767
|
+
#: as the default :class:`Command` class. This is useful to make all
|
1768
|
+
#: subcommands use a custom command class.
|
1769
|
+
#:
|
1770
|
+
#: .. versionadded:: 8.0
|
1771
|
+
command_class: t.Optional[t.Type[Command]] = None
|
1772
|
+
|
1773
|
+
#: If set, this is used by the group's :meth:`group` decorator
|
1774
|
+
#: as the default :class:`Group` class. This is useful to make all
|
1775
|
+
#: subgroups use a custom group class.
|
1776
|
+
#:
|
1777
|
+
#: If set to the special value :class:`type` (literally
|
1778
|
+
#: ``group_class = type``), this group's class will be used as the
|
1779
|
+
#: default class. This makes a custom group class continue to make
|
1780
|
+
#: custom groups.
|
1781
|
+
#:
|
1782
|
+
#: .. versionadded:: 8.0
|
1783
|
+
group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None
|
1784
|
+
# Literal[type] isn't valid, so use Type[type]
|
1785
|
+
|
1786
|
+
def __init__(
|
1787
|
+
self,
|
1788
|
+
name: t.Optional[str] = None,
|
1789
|
+
commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None,
|
1790
|
+
**attrs: t.Any,
|
1791
|
+
) -> None:
|
1792
|
+
super().__init__(name, **attrs)
|
1793
|
+
|
1794
|
+
if commands is None:
|
1795
|
+
commands = {}
|
1796
|
+
elif isinstance(commands, abc.Sequence):
|
1797
|
+
commands = {c.name: c for c in commands if c.name is not None}
|
1798
|
+
|
1799
|
+
#: The registered subcommands by their exported names.
|
1800
|
+
self.commands: t.Dict[str, Command] = commands
|
1801
|
+
|
1802
|
+
def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None:
|
1803
|
+
"""Registers another :class:`Command` with this group. If the name
|
1804
|
+
is not provided, the name of the command is used.
|
1805
|
+
"""
|
1806
|
+
name = name or cmd.name
|
1807
|
+
if name is None:
|
1808
|
+
raise TypeError("Command has no name.")
|
1809
|
+
_check_multicommand(self, name, cmd, register=True)
|
1810
|
+
self.commands[name] = cmd
|
1811
|
+
|
1812
|
+
@t.overload
|
1813
|
+
def command(self, __func: t.Callable[..., t.Any]) -> Command:
|
1814
|
+
...
|
1815
|
+
|
1816
|
+
@t.overload
|
1817
|
+
def command(
|
1818
|
+
self, *args: t.Any, **kwargs: t.Any
|
1819
|
+
) -> t.Callable[[t.Callable[..., t.Any]], Command]:
|
1820
|
+
...
|
1821
|
+
|
1822
|
+
def command(
|
1823
|
+
self, *args: t.Any, **kwargs: t.Any
|
1824
|
+
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]:
|
1825
|
+
"""A shortcut decorator for declaring and attaching a command to
|
1826
|
+
the group. This takes the same arguments as :func:`command` and
|
1827
|
+
immediately registers the created command with this group by
|
1828
|
+
calling :meth:`add_command`.
|
1829
|
+
|
1830
|
+
To customize the command class used, set the
|
1831
|
+
:attr:`command_class` attribute.
|
1832
|
+
|
1833
|
+
.. versionchanged:: 8.1
|
1834
|
+
This decorator can be applied without parentheses.
|
1835
|
+
|
1836
|
+
.. versionchanged:: 8.0
|
1837
|
+
Added the :attr:`command_class` attribute.
|
1838
|
+
"""
|
1839
|
+
from .decorators import command
|
1840
|
+
|
1841
|
+
if self.command_class and kwargs.get("cls") is None:
|
1842
|
+
kwargs["cls"] = self.command_class
|
1843
|
+
|
1844
|
+
func: t.Optional[t.Callable] = None
|
1845
|
+
|
1846
|
+
if args and callable(args[0]):
|
1847
|
+
assert (
|
1848
|
+
len(args) == 1 and not kwargs
|
1849
|
+
), "Use 'command(**kwargs)(callable)' to provide arguments."
|
1850
|
+
(func,) = args
|
1851
|
+
args = ()
|
1852
|
+
|
1853
|
+
def decorator(f: t.Callable[..., t.Any]) -> Command:
|
1854
|
+
cmd: Command = command(*args, **kwargs)(f)
|
1855
|
+
self.add_command(cmd)
|
1856
|
+
return cmd
|
1857
|
+
|
1858
|
+
if func is not None:
|
1859
|
+
return decorator(func)
|
1860
|
+
|
1861
|
+
return decorator
|
1862
|
+
|
1863
|
+
@t.overload
|
1864
|
+
def group(self, __func: t.Callable[..., t.Any]) -> "Group":
|
1865
|
+
...
|
1866
|
+
|
1867
|
+
@t.overload
|
1868
|
+
def group(
|
1869
|
+
self, *args: t.Any, **kwargs: t.Any
|
1870
|
+
) -> t.Callable[[t.Callable[..., t.Any]], "Group"]:
|
1871
|
+
...
|
1872
|
+
|
1873
|
+
def group(
|
1874
|
+
self, *args: t.Any, **kwargs: t.Any
|
1875
|
+
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]:
|
1876
|
+
"""A shortcut decorator for declaring and attaching a group to
|
1877
|
+
the group. This takes the same arguments as :func:`group` and
|
1878
|
+
immediately registers the created group with this group by
|
1879
|
+
calling :meth:`add_command`.
|
1880
|
+
|
1881
|
+
To customize the group class used, set the :attr:`group_class`
|
1882
|
+
attribute.
|
1883
|
+
|
1884
|
+
.. versionchanged:: 8.1
|
1885
|
+
This decorator can be applied without parentheses.
|
1886
|
+
|
1887
|
+
.. versionchanged:: 8.0
|
1888
|
+
Added the :attr:`group_class` attribute.
|
1889
|
+
"""
|
1890
|
+
from .decorators import group
|
1891
|
+
|
1892
|
+
func: t.Optional[t.Callable] = None
|
1893
|
+
|
1894
|
+
if args and callable(args[0]):
|
1895
|
+
assert (
|
1896
|
+
len(args) == 1 and not kwargs
|
1897
|
+
), "Use 'group(**kwargs)(callable)' to provide arguments."
|
1898
|
+
(func,) = args
|
1899
|
+
args = ()
|
1900
|
+
|
1901
|
+
if self.group_class is not None and kwargs.get("cls") is None:
|
1902
|
+
if self.group_class is type:
|
1903
|
+
kwargs["cls"] = type(self)
|
1904
|
+
else:
|
1905
|
+
kwargs["cls"] = self.group_class
|
1906
|
+
|
1907
|
+
def decorator(f: t.Callable[..., t.Any]) -> "Group":
|
1908
|
+
cmd: Group = group(*args, **kwargs)(f)
|
1909
|
+
self.add_command(cmd)
|
1910
|
+
return cmd
|
1911
|
+
|
1912
|
+
if func is not None:
|
1913
|
+
return decorator(func)
|
1914
|
+
|
1915
|
+
return decorator
|
1916
|
+
|
1917
|
+
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]:
|
1918
|
+
return self.commands.get(cmd_name)
|
1919
|
+
|
1920
|
+
def list_commands(self, ctx: Context) -> t.List[str]:
|
1921
|
+
return sorted(self.commands)
|
1922
|
+
|
1923
|
+
|
1924
|
+
class CommandCollection(MultiCommand):
|
1925
|
+
"""A command collection is a multi command that merges multiple multi
|
1926
|
+
commands together into one. This is a straightforward implementation
|
1927
|
+
that accepts a list of different multi commands as sources and
|
1928
|
+
provides all the commands for each of them.
|
1929
|
+
"""
|
1930
|
+
|
1931
|
+
def __init__(
|
1932
|
+
self,
|
1933
|
+
name: t.Optional[str] = None,
|
1934
|
+
sources: t.Optional[t.List[MultiCommand]] = None,
|
1935
|
+
**attrs: t.Any,
|
1936
|
+
) -> None:
|
1937
|
+
super().__init__(name, **attrs)
|
1938
|
+
#: The list of registered multi commands.
|
1939
|
+
self.sources: t.List[MultiCommand] = sources or []
|
1940
|
+
|
1941
|
+
def add_source(self, multi_cmd: MultiCommand) -> None:
|
1942
|
+
"""Adds a new multi command to the chain dispatcher."""
|
1943
|
+
self.sources.append(multi_cmd)
|
1944
|
+
|
1945
|
+
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]:
|
1946
|
+
for source in self.sources:
|
1947
|
+
rv = source.get_command(ctx, cmd_name)
|
1948
|
+
|
1949
|
+
if rv is not None:
|
1950
|
+
if self.chain:
|
1951
|
+
_check_multicommand(self, cmd_name, rv)
|
1952
|
+
|
1953
|
+
return rv
|
1954
|
+
|
1955
|
+
return None
|
1956
|
+
|
1957
|
+
def list_commands(self, ctx: Context) -> t.List[str]:
|
1958
|
+
rv: t.Set[str] = set()
|
1959
|
+
|
1960
|
+
for source in self.sources:
|
1961
|
+
rv.update(source.list_commands(ctx))
|
1962
|
+
|
1963
|
+
return sorted(rv)
|
1964
|
+
|
1965
|
+
|
1966
|
+
def _check_iter(value: t.Any) -> t.Iterator[t.Any]:
|
1967
|
+
"""Check if the value is iterable but not a string. Raises a type
|
1968
|
+
error, or return an iterator over the value.
|
1969
|
+
"""
|
1970
|
+
if isinstance(value, str):
|
1971
|
+
raise TypeError
|
1972
|
+
|
1973
|
+
return iter(value)
|
1974
|
+
|
1975
|
+
|
1976
|
+
class Parameter:
|
1977
|
+
r"""A parameter to a command comes in two versions: they are either
|
1978
|
+
:class:`Option`\s or :class:`Argument`\s. Other subclasses are currently
|
1979
|
+
not supported by design as some of the internals for parsing are
|
1980
|
+
intentionally not finalized.
|
1981
|
+
|
1982
|
+
Some settings are supported by both options and arguments.
|
1983
|
+
|
1984
|
+
:param param_decls: the parameter declarations for this option or
|
1985
|
+
argument. This is a list of flags or argument
|
1986
|
+
names.
|
1987
|
+
:param type: the type that should be used. Either a :class:`ParamType`
|
1988
|
+
or a Python type. The later is converted into the former
|
1989
|
+
automatically if supported.
|
1990
|
+
:param required: controls if this is optional or not.
|
1991
|
+
:param default: the default value if omitted. This can also be a callable,
|
1992
|
+
in which case it's invoked when the default is needed
|
1993
|
+
without any arguments.
|
1994
|
+
:param callback: A function to further process or validate the value
|
1995
|
+
after type conversion. It is called as ``f(ctx, param, value)``
|
1996
|
+
and must return the value. It is called for all sources,
|
1997
|
+
including prompts.
|
1998
|
+
:param nargs: the number of arguments to match. If not ``1`` the return
|
1999
|
+
value is a tuple instead of single value. The default for
|
2000
|
+
nargs is ``1`` (except if the type is a tuple, then it's
|
2001
|
+
the arity of the tuple). If ``nargs=-1``, all remaining
|
2002
|
+
parameters are collected.
|
2003
|
+
:param metavar: how the value is represented in the help page.
|
2004
|
+
:param expose_value: if this is `True` then the value is passed onwards
|
2005
|
+
to the command callback and stored on the context,
|
2006
|
+
otherwise it's skipped.
|
2007
|
+
:param is_eager: eager values are processed before non eager ones. This
|
2008
|
+
should not be set for arguments or it will inverse the
|
2009
|
+
order of processing.
|
2010
|
+
:param envvar: a string or list of strings that are environment variables
|
2011
|
+
that should be checked.
|
2012
|
+
:param shell_complete: A function that returns custom shell
|
2013
|
+
completions. Used instead of the param's type completion if
|
2014
|
+
given. Takes ``ctx, param, incomplete`` and must return a list
|
2015
|
+
of :class:`~click.shell_completion.CompletionItem` or a list of
|
2016
|
+
strings.
|
2017
|
+
|
2018
|
+
.. versionchanged:: 8.0
|
2019
|
+
``process_value`` validates required parameters and bounded
|
2020
|
+
``nargs``, and invokes the parameter callback before returning
|
2021
|
+
the value. This allows the callback to validate prompts.
|
2022
|
+
``full_process_value`` is removed.
|
2023
|
+
|
2024
|
+
.. versionchanged:: 8.0
|
2025
|
+
``autocompletion`` is renamed to ``shell_complete`` and has new
|
2026
|
+
semantics described above. The old name is deprecated and will
|
2027
|
+
be removed in 8.1, until then it will be wrapped to match the
|
2028
|
+
new requirements.
|
2029
|
+
|
2030
|
+
.. versionchanged:: 8.0
|
2031
|
+
For ``multiple=True, nargs>1``, the default must be a list of
|
2032
|
+
tuples.
|
2033
|
+
|
2034
|
+
.. versionchanged:: 8.0
|
2035
|
+
Setting a default is no longer required for ``nargs>1``, it will
|
2036
|
+
default to ``None``. ``multiple=True`` or ``nargs=-1`` will
|
2037
|
+
default to ``()``.
|
2038
|
+
|
2039
|
+
.. versionchanged:: 7.1
|
2040
|
+
Empty environment variables are ignored rather than taking the
|
2041
|
+
empty string value. This makes it possible for scripts to clear
|
2042
|
+
variables if they can't unset them.
|
2043
|
+
|
2044
|
+
.. versionchanged:: 2.0
|
2045
|
+
Changed signature for parameter callback to also be passed the
|
2046
|
+
parameter. The old callback format will still work, but it will
|
2047
|
+
raise a warning to give you a chance to migrate the code easier.
|
2048
|
+
"""
|
2049
|
+
|
2050
|
+
param_type_name = "parameter"
|
2051
|
+
|
2052
|
+
def __init__(
|
2053
|
+
self,
|
2054
|
+
param_decls: t.Optional[t.Sequence[str]] = None,
|
2055
|
+
type: t.Optional[t.Union[types.ParamType, t.Any]] = None,
|
2056
|
+
required: bool = False,
|
2057
|
+
default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None,
|
2058
|
+
callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None,
|
2059
|
+
nargs: t.Optional[int] = None,
|
2060
|
+
multiple: bool = False,
|
2061
|
+
metavar: t.Optional[str] = None,
|
2062
|
+
expose_value: bool = True,
|
2063
|
+
is_eager: bool = False,
|
2064
|
+
envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None,
|
2065
|
+
shell_complete: t.Optional[
|
2066
|
+
t.Callable[
|
2067
|
+
[Context, "Parameter", str],
|
2068
|
+
t.Union[t.List["CompletionItem"], t.List[str]],
|
2069
|
+
]
|
2070
|
+
] = None,
|
2071
|
+
) -> None:
|
2072
|
+
self.name, self.opts, self.secondary_opts = self._parse_decls(
|
2073
|
+
param_decls or (), expose_value
|
2074
|
+
)
|
2075
|
+
self.type = types.convert_type(type, default)
|
2076
|
+
|
2077
|
+
# Default nargs to what the type tells us if we have that
|
2078
|
+
# information available.
|
2079
|
+
if nargs is None:
|
2080
|
+
if self.type.is_composite:
|
2081
|
+
nargs = self.type.arity
|
2082
|
+
else:
|
2083
|
+
nargs = 1
|
2084
|
+
|
2085
|
+
self.required = required
|
2086
|
+
self.callback = callback
|
2087
|
+
self.nargs = nargs
|
2088
|
+
self.multiple = multiple
|
2089
|
+
self.expose_value = expose_value
|
2090
|
+
self.default = default
|
2091
|
+
self.is_eager = is_eager
|
2092
|
+
self.metavar = metavar
|
2093
|
+
self.envvar = envvar
|
2094
|
+
self._custom_shell_complete = shell_complete
|
2095
|
+
|
2096
|
+
if __debug__:
|
2097
|
+
if self.type.is_composite and nargs != self.type.arity:
|
2098
|
+
raise ValueError(
|
2099
|
+
f"'nargs' must be {self.type.arity} (or None) for"
|
2100
|
+
f" type {self.type!r}, but it was {nargs}."
|
2101
|
+
)
|
2102
|
+
|
2103
|
+
# Skip no default or callable default.
|
2104
|
+
check_default = default if not callable(default) else None
|
2105
|
+
|
2106
|
+
if check_default is not None:
|
2107
|
+
if multiple:
|
2108
|
+
try:
|
2109
|
+
# Only check the first value against nargs.
|
2110
|
+
check_default = next(_check_iter(check_default), None)
|
2111
|
+
except TypeError:
|
2112
|
+
raise ValueError(
|
2113
|
+
"'default' must be a list when 'multiple' is true."
|
2114
|
+
) from None
|
2115
|
+
|
2116
|
+
# Can be None for multiple with empty default.
|
2117
|
+
if nargs != 1 and check_default is not None:
|
2118
|
+
try:
|
2119
|
+
_check_iter(check_default)
|
2120
|
+
except TypeError:
|
2121
|
+
if multiple:
|
2122
|
+
message = (
|
2123
|
+
"'default' must be a list of lists when 'multiple' is"
|
2124
|
+
" true and 'nargs' != 1."
|
2125
|
+
)
|
2126
|
+
else:
|
2127
|
+
message = "'default' must be a list when 'nargs' != 1."
|
2128
|
+
|
2129
|
+
raise ValueError(message) from None
|
2130
|
+
|
2131
|
+
if nargs > 1 and len(check_default) != nargs:
|
2132
|
+
subject = "item length" if multiple else "length"
|
2133
|
+
raise ValueError(
|
2134
|
+
f"'default' {subject} must match nargs={nargs}."
|
2135
|
+
)
|
2136
|
+
|
2137
|
+
def to_info_dict(self) -> t.Dict[str, t.Any]:
|
2138
|
+
"""Gather information that could be useful for a tool generating
|
2139
|
+
user-facing documentation.
|
2140
|
+
|
2141
|
+
Use :meth:`click.Context.to_info_dict` to traverse the entire
|
2142
|
+
CLI structure.
|
2143
|
+
|
2144
|
+
.. versionadded:: 8.0
|
2145
|
+
"""
|
2146
|
+
return {
|
2147
|
+
"name": self.name,
|
2148
|
+
"param_type_name": self.param_type_name,
|
2149
|
+
"opts": self.opts,
|
2150
|
+
"secondary_opts": self.secondary_opts,
|
2151
|
+
"type": self.type.to_info_dict(),
|
2152
|
+
"required": self.required,
|
2153
|
+
"nargs": self.nargs,
|
2154
|
+
"multiple": self.multiple,
|
2155
|
+
"default": self.default,
|
2156
|
+
"envvar": self.envvar,
|
2157
|
+
}
|
2158
|
+
|
2159
|
+
def __repr__(self) -> str:
|
2160
|
+
return f"<{self.__class__.__name__} {self.name}>"
|
2161
|
+
|
2162
|
+
def _parse_decls(
|
2163
|
+
self, decls: t.Sequence[str], expose_value: bool
|
2164
|
+
) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]:
|
2165
|
+
raise NotImplementedError()
|
2166
|
+
|
2167
|
+
@property
|
2168
|
+
def human_readable_name(self) -> str:
|
2169
|
+
"""Returns the human readable name of this parameter. This is the
|
2170
|
+
same as the name for options, but the metavar for arguments.
|
2171
|
+
"""
|
2172
|
+
return self.name # type: ignore
|
2173
|
+
|
2174
|
+
def make_metavar(self) -> str:
|
2175
|
+
if self.metavar is not None:
|
2176
|
+
return self.metavar
|
2177
|
+
|
2178
|
+
metavar = self.type.get_metavar(self)
|
2179
|
+
|
2180
|
+
if metavar is None:
|
2181
|
+
metavar = self.type.name.upper()
|
2182
|
+
|
2183
|
+
if self.nargs != 1:
|
2184
|
+
metavar += "..."
|
2185
|
+
|
2186
|
+
return metavar
|
2187
|
+
|
2188
|
+
@t.overload
|
2189
|
+
def get_default(
|
2190
|
+
self, ctx: Context, call: "te.Literal[True]" = True
|
2191
|
+
) -> t.Optional[t.Any]:
|
2192
|
+
...
|
2193
|
+
|
2194
|
+
@t.overload
|
2195
|
+
def get_default(
|
2196
|
+
self, ctx: Context, call: bool = ...
|
2197
|
+
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
2198
|
+
...
|
2199
|
+
|
2200
|
+
def get_default(
|
2201
|
+
self, ctx: Context, call: bool = True
|
2202
|
+
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
2203
|
+
"""Get the default for the parameter. Tries
|
2204
|
+
:meth:`Context.lookup_default` first, then the local default.
|
2205
|
+
|
2206
|
+
:param ctx: Current context.
|
2207
|
+
:param call: If the default is a callable, call it. Disable to
|
2208
|
+
return the callable instead.
|
2209
|
+
|
2210
|
+
.. versionchanged:: 8.0.2
|
2211
|
+
Type casting is no longer performed when getting a default.
|
2212
|
+
|
2213
|
+
.. versionchanged:: 8.0.1
|
2214
|
+
Type casting can fail in resilient parsing mode. Invalid
|
2215
|
+
defaults will not prevent showing help text.
|
2216
|
+
|
2217
|
+
.. versionchanged:: 8.0
|
2218
|
+
Looks at ``ctx.default_map`` first.
|
2219
|
+
|
2220
|
+
.. versionchanged:: 8.0
|
2221
|
+
Added the ``call`` parameter.
|
2222
|
+
"""
|
2223
|
+
value = ctx.lookup_default(self.name, call=False) # type: ignore
|
2224
|
+
|
2225
|
+
if value is None:
|
2226
|
+
value = self.default
|
2227
|
+
|
2228
|
+
if call and callable(value):
|
2229
|
+
value = value()
|
2230
|
+
|
2231
|
+
return value
|
2232
|
+
|
2233
|
+
def add_to_parser(self, parser: OptionParser, ctx: Context) -> None:
|
2234
|
+
raise NotImplementedError()
|
2235
|
+
|
2236
|
+
def consume_value(
|
2237
|
+
self, ctx: Context, opts: t.Mapping[str, t.Any]
|
2238
|
+
) -> t.Tuple[t.Any, ParameterSource]:
|
2239
|
+
value = opts.get(self.name) # type: ignore
|
2240
|
+
source = ParameterSource.COMMANDLINE
|
2241
|
+
|
2242
|
+
if value is None:
|
2243
|
+
value = self.value_from_envvar(ctx)
|
2244
|
+
source = ParameterSource.ENVIRONMENT
|
2245
|
+
|
2246
|
+
if value is None:
|
2247
|
+
value = ctx.lookup_default(self.name) # type: ignore
|
2248
|
+
source = ParameterSource.DEFAULT_MAP
|
2249
|
+
|
2250
|
+
if value is None:
|
2251
|
+
value = self.get_default(ctx)
|
2252
|
+
source = ParameterSource.DEFAULT
|
2253
|
+
|
2254
|
+
return value, source
|
2255
|
+
|
2256
|
+
def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any:
|
2257
|
+
"""Convert and validate a value against the option's
|
2258
|
+
:attr:`type`, :attr:`multiple`, and :attr:`nargs`.
|
2259
|
+
"""
|
2260
|
+
if value is None:
|
2261
|
+
return () if self.multiple or self.nargs == -1 else None
|
2262
|
+
|
2263
|
+
def check_iter(value: t.Any) -> t.Iterator:
|
2264
|
+
try:
|
2265
|
+
return _check_iter(value)
|
2266
|
+
except TypeError:
|
2267
|
+
# This should only happen when passing in args manually,
|
2268
|
+
# the parser should construct an iterable when parsing
|
2269
|
+
# the command line.
|
2270
|
+
raise BadParameter(
|
2271
|
+
_("Value must be an iterable."), ctx=ctx, param=self
|
2272
|
+
) from None
|
2273
|
+
|
2274
|
+
if self.nargs == 1 or self.type.is_composite:
|
2275
|
+
convert: t.Callable[[t.Any], t.Any] = partial(
|
2276
|
+
self.type, param=self, ctx=ctx
|
2277
|
+
)
|
2278
|
+
elif self.nargs == -1:
|
2279
|
+
|
2280
|
+
def convert(value: t.Any) -> t.Tuple:
|
2281
|
+
return tuple(self.type(x, self, ctx) for x in check_iter(value))
|
2282
|
+
|
2283
|
+
else: # nargs > 1
|
2284
|
+
|
2285
|
+
def convert(value: t.Any) -> t.Tuple:
|
2286
|
+
value = tuple(check_iter(value))
|
2287
|
+
|
2288
|
+
if len(value) != self.nargs:
|
2289
|
+
raise BadParameter(
|
2290
|
+
ngettext(
|
2291
|
+
"Takes {nargs} values but 1 was given.",
|
2292
|
+
"Takes {nargs} values but {len} were given.",
|
2293
|
+
len(value),
|
2294
|
+
).format(nargs=self.nargs, len=len(value)),
|
2295
|
+
ctx=ctx,
|
2296
|
+
param=self,
|
2297
|
+
)
|
2298
|
+
|
2299
|
+
return tuple(self.type(x, self, ctx) for x in value)
|
2300
|
+
|
2301
|
+
if self.multiple:
|
2302
|
+
return tuple(convert(x) for x in check_iter(value))
|
2303
|
+
|
2304
|
+
return convert(value)
|
2305
|
+
|
2306
|
+
def value_is_missing(self, value: t.Any) -> bool:
|
2307
|
+
if value is None:
|
2308
|
+
return True
|
2309
|
+
|
2310
|
+
if (self.nargs != 1 or self.multiple) and value == ():
|
2311
|
+
return True
|
2312
|
+
|
2313
|
+
return False
|
2314
|
+
|
2315
|
+
def process_value(self, ctx: Context, value: t.Any) -> t.Any:
|
2316
|
+
value = self.type_cast_value(ctx, value)
|
2317
|
+
|
2318
|
+
if self.required and self.value_is_missing(value):
|
2319
|
+
raise MissingParameter(ctx=ctx, param=self)
|
2320
|
+
|
2321
|
+
if self.callback is not None:
|
2322
|
+
value = self.callback(ctx, self, value)
|
2323
|
+
|
2324
|
+
return value
|
2325
|
+
|
2326
|
+
def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]:
|
2327
|
+
if self.envvar is None:
|
2328
|
+
return None
|
2329
|
+
|
2330
|
+
if isinstance(self.envvar, str):
|
2331
|
+
rv = os.environ.get(self.envvar)
|
2332
|
+
|
2333
|
+
if rv:
|
2334
|
+
return rv
|
2335
|
+
else:
|
2336
|
+
for envvar in self.envvar:
|
2337
|
+
rv = os.environ.get(envvar)
|
2338
|
+
|
2339
|
+
if rv:
|
2340
|
+
return rv
|
2341
|
+
|
2342
|
+
return None
|
2343
|
+
|
2344
|
+
def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]:
|
2345
|
+
rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx)
|
2346
|
+
|
2347
|
+
if rv is not None and self.nargs != 1:
|
2348
|
+
rv = self.type.split_envvar_value(rv)
|
2349
|
+
|
2350
|
+
return rv
|
2351
|
+
|
2352
|
+
def handle_parse_result(
|
2353
|
+
self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str]
|
2354
|
+
) -> t.Tuple[t.Any, t.List[str]]:
|
2355
|
+
with augment_usage_errors(ctx, param=self):
|
2356
|
+
value, source = self.consume_value(ctx, opts)
|
2357
|
+
ctx.set_parameter_source(self.name, source) # type: ignore
|
2358
|
+
|
2359
|
+
try:
|
2360
|
+
value = self.process_value(ctx, value)
|
2361
|
+
except Exception:
|
2362
|
+
if not ctx.resilient_parsing:
|
2363
|
+
raise
|
2364
|
+
|
2365
|
+
value = None
|
2366
|
+
|
2367
|
+
if self.expose_value:
|
2368
|
+
ctx.params[self.name] = value # type: ignore
|
2369
|
+
|
2370
|
+
return value, args
|
2371
|
+
|
2372
|
+
def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]:
|
2373
|
+
pass
|
2374
|
+
|
2375
|
+
def get_usage_pieces(self, ctx: Context) -> t.List[str]:
|
2376
|
+
return []
|
2377
|
+
|
2378
|
+
def get_error_hint(self, ctx: Context) -> str:
|
2379
|
+
"""Get a stringified version of the param for use in error messages to
|
2380
|
+
indicate which param caused the error.
|
2381
|
+
"""
|
2382
|
+
hint_list = self.opts or [self.human_readable_name]
|
2383
|
+
return " / ".join(f"'{x}'" for x in hint_list)
|
2384
|
+
|
2385
|
+
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]:
|
2386
|
+
"""Return a list of completions for the incomplete value. If a
|
2387
|
+
``shell_complete`` function was given during init, it is used.
|
2388
|
+
Otherwise, the :attr:`type`
|
2389
|
+
:meth:`~click.types.ParamType.shell_complete` function is used.
|
2390
|
+
|
2391
|
+
:param ctx: Invocation context for this command.
|
2392
|
+
:param incomplete: Value being completed. May be empty.
|
2393
|
+
|
2394
|
+
.. versionadded:: 8.0
|
2395
|
+
"""
|
2396
|
+
if self._custom_shell_complete is not None:
|
2397
|
+
results = self._custom_shell_complete(ctx, self, incomplete)
|
2398
|
+
|
2399
|
+
if results and isinstance(results[0], str):
|
2400
|
+
from outerbounds._vendor.click.shell_completion import CompletionItem
|
2401
|
+
|
2402
|
+
results = [CompletionItem(c) for c in results]
|
2403
|
+
|
2404
|
+
return t.cast(t.List["CompletionItem"], results)
|
2405
|
+
|
2406
|
+
return self.type.shell_complete(ctx, self, incomplete)
|
2407
|
+
|
2408
|
+
|
2409
|
+
class Option(Parameter):
|
2410
|
+
"""Options are usually optional values on the command line and
|
2411
|
+
have some extra features that arguments don't have.
|
2412
|
+
|
2413
|
+
All other parameters are passed onwards to the parameter constructor.
|
2414
|
+
|
2415
|
+
:param show_default: Show the default value for this option in its
|
2416
|
+
help text. Values are not shown by default, unless
|
2417
|
+
:attr:`Context.show_default` is ``True``. If this value is a
|
2418
|
+
string, it shows that string in parentheses instead of the
|
2419
|
+
actual value. This is particularly useful for dynamic options.
|
2420
|
+
For single option boolean flags, the default remains hidden if
|
2421
|
+
its value is ``False``.
|
2422
|
+
:param show_envvar: Controls if an environment variable should be
|
2423
|
+
shown on the help page. Normally, environment variables are not
|
2424
|
+
shown.
|
2425
|
+
:param prompt: If set to ``True`` or a non empty string then the
|
2426
|
+
user will be prompted for input. If set to ``True`` the prompt
|
2427
|
+
will be the option name capitalized.
|
2428
|
+
:param confirmation_prompt: Prompt a second time to confirm the
|
2429
|
+
value if it was prompted for. Can be set to a string instead of
|
2430
|
+
``True`` to customize the message.
|
2431
|
+
:param prompt_required: If set to ``False``, the user will be
|
2432
|
+
prompted for input only when the option was specified as a flag
|
2433
|
+
without a value.
|
2434
|
+
:param hide_input: If this is ``True`` then the input on the prompt
|
2435
|
+
will be hidden from the user. This is useful for password input.
|
2436
|
+
:param is_flag: forces this option to act as a flag. The default is
|
2437
|
+
auto detection.
|
2438
|
+
:param flag_value: which value should be used for this flag if it's
|
2439
|
+
enabled. This is set to a boolean automatically if
|
2440
|
+
the option string contains a slash to mark two options.
|
2441
|
+
:param multiple: if this is set to `True` then the argument is accepted
|
2442
|
+
multiple times and recorded. This is similar to ``nargs``
|
2443
|
+
in how it works but supports arbitrary number of
|
2444
|
+
arguments.
|
2445
|
+
:param count: this flag makes an option increment an integer.
|
2446
|
+
:param allow_from_autoenv: if this is enabled then the value of this
|
2447
|
+
parameter will be pulled from an environment
|
2448
|
+
variable in case a prefix is defined on the
|
2449
|
+
context.
|
2450
|
+
:param help: the help string.
|
2451
|
+
:param hidden: hide this option from help outputs.
|
2452
|
+
|
2453
|
+
.. versionchanged:: 8.1.0
|
2454
|
+
Help text indentation is cleaned here instead of only in the
|
2455
|
+
``@option`` decorator.
|
2456
|
+
|
2457
|
+
.. versionchanged:: 8.1.0
|
2458
|
+
The ``show_default`` parameter overrides
|
2459
|
+
``Context.show_default``.
|
2460
|
+
|
2461
|
+
.. versionchanged:: 8.1.0
|
2462
|
+
The default of a single option boolean flag is not shown if the
|
2463
|
+
default value is ``False``.
|
2464
|
+
|
2465
|
+
.. versionchanged:: 8.0.1
|
2466
|
+
``type`` is detected from ``flag_value`` if given.
|
2467
|
+
"""
|
2468
|
+
|
2469
|
+
param_type_name = "option"
|
2470
|
+
|
2471
|
+
def __init__(
|
2472
|
+
self,
|
2473
|
+
param_decls: t.Optional[t.Sequence[str]] = None,
|
2474
|
+
show_default: t.Union[bool, str, None] = None,
|
2475
|
+
prompt: t.Union[bool, str] = False,
|
2476
|
+
confirmation_prompt: t.Union[bool, str] = False,
|
2477
|
+
prompt_required: bool = True,
|
2478
|
+
hide_input: bool = False,
|
2479
|
+
is_flag: t.Optional[bool] = None,
|
2480
|
+
flag_value: t.Optional[t.Any] = None,
|
2481
|
+
multiple: bool = False,
|
2482
|
+
count: bool = False,
|
2483
|
+
allow_from_autoenv: bool = True,
|
2484
|
+
type: t.Optional[t.Union[types.ParamType, t.Any]] = None,
|
2485
|
+
help: t.Optional[str] = None,
|
2486
|
+
hidden: bool = False,
|
2487
|
+
show_choices: bool = True,
|
2488
|
+
show_envvar: bool = False,
|
2489
|
+
**attrs: t.Any,
|
2490
|
+
) -> None:
|
2491
|
+
if help:
|
2492
|
+
help = inspect.cleandoc(help)
|
2493
|
+
|
2494
|
+
default_is_missing = "default" not in attrs
|
2495
|
+
super().__init__(param_decls, type=type, multiple=multiple, **attrs)
|
2496
|
+
|
2497
|
+
if prompt is True:
|
2498
|
+
if self.name is None:
|
2499
|
+
raise TypeError("'name' is required with 'prompt=True'.")
|
2500
|
+
|
2501
|
+
prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize()
|
2502
|
+
elif prompt is False:
|
2503
|
+
prompt_text = None
|
2504
|
+
else:
|
2505
|
+
prompt_text = prompt
|
2506
|
+
|
2507
|
+
self.prompt = prompt_text
|
2508
|
+
self.confirmation_prompt = confirmation_prompt
|
2509
|
+
self.prompt_required = prompt_required
|
2510
|
+
self.hide_input = hide_input
|
2511
|
+
self.hidden = hidden
|
2512
|
+
|
2513
|
+
# If prompt is enabled but not required, then the option can be
|
2514
|
+
# used as a flag to indicate using prompt or flag_value.
|
2515
|
+
self._flag_needs_value = self.prompt is not None and not self.prompt_required
|
2516
|
+
|
2517
|
+
if is_flag is None:
|
2518
|
+
if flag_value is not None:
|
2519
|
+
# Implicitly a flag because flag_value was set.
|
2520
|
+
is_flag = True
|
2521
|
+
elif self._flag_needs_value:
|
2522
|
+
# Not a flag, but when used as a flag it shows a prompt.
|
2523
|
+
is_flag = False
|
2524
|
+
else:
|
2525
|
+
# Implicitly a flag because flag options were given.
|
2526
|
+
is_flag = bool(self.secondary_opts)
|
2527
|
+
elif is_flag is False and not self._flag_needs_value:
|
2528
|
+
# Not a flag, and prompt is not enabled, can be used as a
|
2529
|
+
# flag if flag_value is set.
|
2530
|
+
self._flag_needs_value = flag_value is not None
|
2531
|
+
|
2532
|
+
if is_flag and default_is_missing and not self.required:
|
2533
|
+
self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False
|
2534
|
+
|
2535
|
+
if flag_value is None:
|
2536
|
+
flag_value = not self.default
|
2537
|
+
|
2538
|
+
if is_flag and type is None:
|
2539
|
+
# Re-guess the type from the flag value instead of the
|
2540
|
+
# default.
|
2541
|
+
self.type = types.convert_type(None, flag_value)
|
2542
|
+
|
2543
|
+
self.is_flag: bool = is_flag
|
2544
|
+
self.is_bool_flag = is_flag and isinstance(self.type, types.BoolParamType)
|
2545
|
+
self.flag_value: t.Any = flag_value
|
2546
|
+
|
2547
|
+
# Counting
|
2548
|
+
self.count = count
|
2549
|
+
if count:
|
2550
|
+
if type is None:
|
2551
|
+
self.type = types.IntRange(min=0)
|
2552
|
+
if default_is_missing:
|
2553
|
+
self.default = 0
|
2554
|
+
|
2555
|
+
self.allow_from_autoenv = allow_from_autoenv
|
2556
|
+
self.help = help
|
2557
|
+
self.show_default = show_default
|
2558
|
+
self.show_choices = show_choices
|
2559
|
+
self.show_envvar = show_envvar
|
2560
|
+
|
2561
|
+
if __debug__:
|
2562
|
+
if self.nargs == -1:
|
2563
|
+
raise TypeError("nargs=-1 is not supported for options.")
|
2564
|
+
|
2565
|
+
if self.prompt and self.is_flag and not self.is_bool_flag:
|
2566
|
+
raise TypeError("'prompt' is not valid for non-boolean flag.")
|
2567
|
+
|
2568
|
+
if not self.is_bool_flag and self.secondary_opts:
|
2569
|
+
raise TypeError("Secondary flag is not valid for non-boolean flag.")
|
2570
|
+
|
2571
|
+
if self.is_bool_flag and self.hide_input and self.prompt is not None:
|
2572
|
+
raise TypeError(
|
2573
|
+
"'prompt' with 'hide_input' is not valid for boolean flag."
|
2574
|
+
)
|
2575
|
+
|
2576
|
+
if self.count:
|
2577
|
+
if self.multiple:
|
2578
|
+
raise TypeError("'count' is not valid with 'multiple'.")
|
2579
|
+
|
2580
|
+
if self.is_flag:
|
2581
|
+
raise TypeError("'count' is not valid with 'is_flag'.")
|
2582
|
+
|
2583
|
+
if self.multiple and self.is_flag:
|
2584
|
+
raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.")
|
2585
|
+
|
2586
|
+
def to_info_dict(self) -> t.Dict[str, t.Any]:
|
2587
|
+
info_dict = super().to_info_dict()
|
2588
|
+
info_dict.update(
|
2589
|
+
help=self.help,
|
2590
|
+
prompt=self.prompt,
|
2591
|
+
is_flag=self.is_flag,
|
2592
|
+
flag_value=self.flag_value,
|
2593
|
+
count=self.count,
|
2594
|
+
hidden=self.hidden,
|
2595
|
+
)
|
2596
|
+
return info_dict
|
2597
|
+
|
2598
|
+
def _parse_decls(
|
2599
|
+
self, decls: t.Sequence[str], expose_value: bool
|
2600
|
+
) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]:
|
2601
|
+
opts = []
|
2602
|
+
secondary_opts = []
|
2603
|
+
name = None
|
2604
|
+
possible_names = []
|
2605
|
+
|
2606
|
+
for decl in decls:
|
2607
|
+
if decl.isidentifier():
|
2608
|
+
if name is not None:
|
2609
|
+
raise TypeError(f"Name '{name}' defined twice")
|
2610
|
+
name = decl
|
2611
|
+
else:
|
2612
|
+
split_char = ";" if decl[:1] == "/" else "/"
|
2613
|
+
if split_char in decl:
|
2614
|
+
first, second = decl.split(split_char, 1)
|
2615
|
+
first = first.rstrip()
|
2616
|
+
if first:
|
2617
|
+
possible_names.append(split_opt(first))
|
2618
|
+
opts.append(first)
|
2619
|
+
second = second.lstrip()
|
2620
|
+
if second:
|
2621
|
+
secondary_opts.append(second.lstrip())
|
2622
|
+
if first == second:
|
2623
|
+
raise ValueError(
|
2624
|
+
f"Boolean option {decl!r} cannot use the"
|
2625
|
+
" same flag for true/false."
|
2626
|
+
)
|
2627
|
+
else:
|
2628
|
+
possible_names.append(split_opt(decl))
|
2629
|
+
opts.append(decl)
|
2630
|
+
|
2631
|
+
if name is None and possible_names:
|
2632
|
+
possible_names.sort(key=lambda x: -len(x[0])) # group long options first
|
2633
|
+
name = possible_names[0][1].replace("-", "_").lower()
|
2634
|
+
if not name.isidentifier():
|
2635
|
+
name = None
|
2636
|
+
|
2637
|
+
if name is None:
|
2638
|
+
if not expose_value:
|
2639
|
+
return None, opts, secondary_opts
|
2640
|
+
raise TypeError("Could not determine name for option")
|
2641
|
+
|
2642
|
+
if not opts and not secondary_opts:
|
2643
|
+
raise TypeError(
|
2644
|
+
f"No options defined but a name was passed ({name})."
|
2645
|
+
" Did you mean to declare an argument instead? Did"
|
2646
|
+
f" you mean to pass '--{name}'?"
|
2647
|
+
)
|
2648
|
+
|
2649
|
+
return name, opts, secondary_opts
|
2650
|
+
|
2651
|
+
def add_to_parser(self, parser: OptionParser, ctx: Context) -> None:
|
2652
|
+
if self.multiple:
|
2653
|
+
action = "append"
|
2654
|
+
elif self.count:
|
2655
|
+
action = "count"
|
2656
|
+
else:
|
2657
|
+
action = "store"
|
2658
|
+
|
2659
|
+
if self.is_flag:
|
2660
|
+
action = f"{action}_const"
|
2661
|
+
|
2662
|
+
if self.is_bool_flag and self.secondary_opts:
|
2663
|
+
parser.add_option(
|
2664
|
+
obj=self, opts=self.opts, dest=self.name, action=action, const=True
|
2665
|
+
)
|
2666
|
+
parser.add_option(
|
2667
|
+
obj=self,
|
2668
|
+
opts=self.secondary_opts,
|
2669
|
+
dest=self.name,
|
2670
|
+
action=action,
|
2671
|
+
const=False,
|
2672
|
+
)
|
2673
|
+
else:
|
2674
|
+
parser.add_option(
|
2675
|
+
obj=self,
|
2676
|
+
opts=self.opts,
|
2677
|
+
dest=self.name,
|
2678
|
+
action=action,
|
2679
|
+
const=self.flag_value,
|
2680
|
+
)
|
2681
|
+
else:
|
2682
|
+
parser.add_option(
|
2683
|
+
obj=self,
|
2684
|
+
opts=self.opts,
|
2685
|
+
dest=self.name,
|
2686
|
+
action=action,
|
2687
|
+
nargs=self.nargs,
|
2688
|
+
)
|
2689
|
+
|
2690
|
+
def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]:
|
2691
|
+
if self.hidden:
|
2692
|
+
return None
|
2693
|
+
|
2694
|
+
any_prefix_is_slash = False
|
2695
|
+
|
2696
|
+
def _write_opts(opts: t.Sequence[str]) -> str:
|
2697
|
+
nonlocal any_prefix_is_slash
|
2698
|
+
|
2699
|
+
rv, any_slashes = join_options(opts)
|
2700
|
+
|
2701
|
+
if any_slashes:
|
2702
|
+
any_prefix_is_slash = True
|
2703
|
+
|
2704
|
+
if not self.is_flag and not self.count:
|
2705
|
+
rv += f" {self.make_metavar()}"
|
2706
|
+
|
2707
|
+
return rv
|
2708
|
+
|
2709
|
+
rv = [_write_opts(self.opts)]
|
2710
|
+
|
2711
|
+
if self.secondary_opts:
|
2712
|
+
rv.append(_write_opts(self.secondary_opts))
|
2713
|
+
|
2714
|
+
help = self.help or ""
|
2715
|
+
extra = []
|
2716
|
+
|
2717
|
+
if self.show_envvar:
|
2718
|
+
envvar = self.envvar
|
2719
|
+
|
2720
|
+
if envvar is None:
|
2721
|
+
if (
|
2722
|
+
self.allow_from_autoenv
|
2723
|
+
and ctx.auto_envvar_prefix is not None
|
2724
|
+
and self.name is not None
|
2725
|
+
):
|
2726
|
+
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"
|
2727
|
+
|
2728
|
+
if envvar is not None:
|
2729
|
+
var_str = (
|
2730
|
+
envvar
|
2731
|
+
if isinstance(envvar, str)
|
2732
|
+
else ", ".join(str(d) for d in envvar)
|
2733
|
+
)
|
2734
|
+
extra.append(_("env var: {var}").format(var=var_str))
|
2735
|
+
|
2736
|
+
# Temporarily enable resilient parsing to avoid type casting
|
2737
|
+
# failing for the default. Might be possible to extend this to
|
2738
|
+
# help formatting in general.
|
2739
|
+
resilient = ctx.resilient_parsing
|
2740
|
+
ctx.resilient_parsing = True
|
2741
|
+
|
2742
|
+
try:
|
2743
|
+
default_value = self.get_default(ctx, call=False)
|
2744
|
+
finally:
|
2745
|
+
ctx.resilient_parsing = resilient
|
2746
|
+
|
2747
|
+
show_default = False
|
2748
|
+
show_default_is_str = False
|
2749
|
+
|
2750
|
+
if self.show_default is not None:
|
2751
|
+
if isinstance(self.show_default, str):
|
2752
|
+
show_default_is_str = show_default = True
|
2753
|
+
else:
|
2754
|
+
show_default = self.show_default
|
2755
|
+
elif ctx.show_default is not None:
|
2756
|
+
show_default = ctx.show_default
|
2757
|
+
|
2758
|
+
if show_default_is_str or (show_default and (default_value is not None)):
|
2759
|
+
if show_default_is_str:
|
2760
|
+
default_string = f"({self.show_default})"
|
2761
|
+
elif isinstance(default_value, (list, tuple)):
|
2762
|
+
default_string = ", ".join(str(d) for d in default_value)
|
2763
|
+
elif inspect.isfunction(default_value):
|
2764
|
+
default_string = _("(dynamic)")
|
2765
|
+
elif self.is_bool_flag and self.secondary_opts:
|
2766
|
+
# For boolean flags that have distinct True/False opts,
|
2767
|
+
# use the opt without prefix instead of the value.
|
2768
|
+
default_string = split_opt(
|
2769
|
+
(self.opts if self.default else self.secondary_opts)[0]
|
2770
|
+
)[1]
|
2771
|
+
elif self.is_bool_flag and not self.secondary_opts and not default_value:
|
2772
|
+
default_string = ""
|
2773
|
+
else:
|
2774
|
+
default_string = str(default_value)
|
2775
|
+
|
2776
|
+
if default_string:
|
2777
|
+
extra.append(_("default: {default}").format(default=default_string))
|
2778
|
+
|
2779
|
+
if (
|
2780
|
+
isinstance(self.type, types._NumberRangeBase)
|
2781
|
+
# skip count with default range type
|
2782
|
+
and not (self.count and self.type.min == 0 and self.type.max is None)
|
2783
|
+
):
|
2784
|
+
range_str = self.type._describe_range()
|
2785
|
+
|
2786
|
+
if range_str:
|
2787
|
+
extra.append(range_str)
|
2788
|
+
|
2789
|
+
if self.required:
|
2790
|
+
extra.append(_("required"))
|
2791
|
+
|
2792
|
+
if extra:
|
2793
|
+
extra_str = "; ".join(extra)
|
2794
|
+
help = f"{help} [{extra_str}]" if help else f"[{extra_str}]"
|
2795
|
+
|
2796
|
+
return ("; " if any_prefix_is_slash else " / ").join(rv), help
|
2797
|
+
|
2798
|
+
@t.overload
|
2799
|
+
def get_default(
|
2800
|
+
self, ctx: Context, call: "te.Literal[True]" = True
|
2801
|
+
) -> t.Optional[t.Any]:
|
2802
|
+
...
|
2803
|
+
|
2804
|
+
@t.overload
|
2805
|
+
def get_default(
|
2806
|
+
self, ctx: Context, call: bool = ...
|
2807
|
+
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
2808
|
+
...
|
2809
|
+
|
2810
|
+
def get_default(
|
2811
|
+
self, ctx: Context, call: bool = True
|
2812
|
+
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]:
|
2813
|
+
# If we're a non boolean flag our default is more complex because
|
2814
|
+
# we need to look at all flags in the same group to figure out
|
2815
|
+
# if we're the default one in which case we return the flag
|
2816
|
+
# value as default.
|
2817
|
+
if self.is_flag and not self.is_bool_flag:
|
2818
|
+
for param in ctx.command.params:
|
2819
|
+
if param.name == self.name and param.default:
|
2820
|
+
return param.flag_value # type: ignore
|
2821
|
+
|
2822
|
+
return None
|
2823
|
+
|
2824
|
+
return super().get_default(ctx, call=call)
|
2825
|
+
|
2826
|
+
def prompt_for_value(self, ctx: Context) -> t.Any:
|
2827
|
+
"""This is an alternative flow that can be activated in the full
|
2828
|
+
value processing if a value does not exist. It will prompt the
|
2829
|
+
user until a valid value exists and then returns the processed
|
2830
|
+
value as result.
|
2831
|
+
"""
|
2832
|
+
assert self.prompt is not None
|
2833
|
+
|
2834
|
+
# Calculate the default before prompting anything to be stable.
|
2835
|
+
default = self.get_default(ctx)
|
2836
|
+
|
2837
|
+
# If this is a prompt for a flag we need to handle this
|
2838
|
+
# differently.
|
2839
|
+
if self.is_bool_flag:
|
2840
|
+
return confirm(self.prompt, default)
|
2841
|
+
|
2842
|
+
return prompt(
|
2843
|
+
self.prompt,
|
2844
|
+
default=default,
|
2845
|
+
type=self.type,
|
2846
|
+
hide_input=self.hide_input,
|
2847
|
+
show_choices=self.show_choices,
|
2848
|
+
confirmation_prompt=self.confirmation_prompt,
|
2849
|
+
value_proc=lambda x: self.process_value(ctx, x),
|
2850
|
+
)
|
2851
|
+
|
2852
|
+
def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]:
|
2853
|
+
rv = super().resolve_envvar_value(ctx)
|
2854
|
+
|
2855
|
+
if rv is not None:
|
2856
|
+
return rv
|
2857
|
+
|
2858
|
+
if (
|
2859
|
+
self.allow_from_autoenv
|
2860
|
+
and ctx.auto_envvar_prefix is not None
|
2861
|
+
and self.name is not None
|
2862
|
+
):
|
2863
|
+
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"
|
2864
|
+
rv = os.environ.get(envvar)
|
2865
|
+
|
2866
|
+
if rv:
|
2867
|
+
return rv
|
2868
|
+
|
2869
|
+
return None
|
2870
|
+
|
2871
|
+
def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]:
|
2872
|
+
rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx)
|
2873
|
+
|
2874
|
+
if rv is None:
|
2875
|
+
return None
|
2876
|
+
|
2877
|
+
value_depth = (self.nargs != 1) + bool(self.multiple)
|
2878
|
+
|
2879
|
+
if value_depth > 0:
|
2880
|
+
rv = self.type.split_envvar_value(rv)
|
2881
|
+
|
2882
|
+
if self.multiple and self.nargs != 1:
|
2883
|
+
rv = batch(rv, self.nargs)
|
2884
|
+
|
2885
|
+
return rv
|
2886
|
+
|
2887
|
+
def consume_value(
|
2888
|
+
self, ctx: Context, opts: t.Mapping[str, "Parameter"]
|
2889
|
+
) -> t.Tuple[t.Any, ParameterSource]:
|
2890
|
+
value, source = super().consume_value(ctx, opts)
|
2891
|
+
|
2892
|
+
# The parser will emit a sentinel value if the option can be
|
2893
|
+
# given as a flag without a value. This is different from None
|
2894
|
+
# to distinguish from the flag not being given at all.
|
2895
|
+
if value is _flag_needs_value:
|
2896
|
+
if self.prompt is not None and not ctx.resilient_parsing:
|
2897
|
+
value = self.prompt_for_value(ctx)
|
2898
|
+
source = ParameterSource.PROMPT
|
2899
|
+
else:
|
2900
|
+
value = self.flag_value
|
2901
|
+
source = ParameterSource.COMMANDLINE
|
2902
|
+
|
2903
|
+
elif (
|
2904
|
+
self.multiple
|
2905
|
+
and value is not None
|
2906
|
+
and any(v is _flag_needs_value for v in value)
|
2907
|
+
):
|
2908
|
+
value = [self.flag_value if v is _flag_needs_value else v for v in value]
|
2909
|
+
source = ParameterSource.COMMANDLINE
|
2910
|
+
|
2911
|
+
# The value wasn't set, or used the param's default, prompt if
|
2912
|
+
# prompting is enabled.
|
2913
|
+
elif (
|
2914
|
+
source in {None, ParameterSource.DEFAULT}
|
2915
|
+
and self.prompt is not None
|
2916
|
+
and (self.required or self.prompt_required)
|
2917
|
+
and not ctx.resilient_parsing
|
2918
|
+
):
|
2919
|
+
value = self.prompt_for_value(ctx)
|
2920
|
+
source = ParameterSource.PROMPT
|
2921
|
+
|
2922
|
+
return value, source
|
2923
|
+
|
2924
|
+
|
2925
|
+
class Argument(Parameter):
|
2926
|
+
"""Arguments are positional parameters to a command. They generally
|
2927
|
+
provide fewer features than options but can have infinite ``nargs``
|
2928
|
+
and are required by default.
|
2929
|
+
|
2930
|
+
All parameters are passed onwards to the parameter constructor.
|
2931
|
+
"""
|
2932
|
+
|
2933
|
+
param_type_name = "argument"
|
2934
|
+
|
2935
|
+
def __init__(
|
2936
|
+
self,
|
2937
|
+
param_decls: t.Sequence[str],
|
2938
|
+
required: t.Optional[bool] = None,
|
2939
|
+
**attrs: t.Any,
|
2940
|
+
) -> None:
|
2941
|
+
if required is None:
|
2942
|
+
if attrs.get("default") is not None:
|
2943
|
+
required = False
|
2944
|
+
else:
|
2945
|
+
required = attrs.get("nargs", 1) > 0
|
2946
|
+
|
2947
|
+
if "multiple" in attrs:
|
2948
|
+
raise TypeError("__init__() got an unexpected keyword argument 'multiple'.")
|
2949
|
+
|
2950
|
+
super().__init__(param_decls, required=required, **attrs)
|
2951
|
+
|
2952
|
+
if __debug__:
|
2953
|
+
if self.default is not None and self.nargs == -1:
|
2954
|
+
raise TypeError("'default' is not supported for nargs=-1.")
|
2955
|
+
|
2956
|
+
@property
|
2957
|
+
def human_readable_name(self) -> str:
|
2958
|
+
if self.metavar is not None:
|
2959
|
+
return self.metavar
|
2960
|
+
return self.name.upper() # type: ignore
|
2961
|
+
|
2962
|
+
def make_metavar(self) -> str:
|
2963
|
+
if self.metavar is not None:
|
2964
|
+
return self.metavar
|
2965
|
+
var = self.type.get_metavar(self)
|
2966
|
+
if not var:
|
2967
|
+
var = self.name.upper() # type: ignore
|
2968
|
+
if not self.required:
|
2969
|
+
var = f"[{var}]"
|
2970
|
+
if self.nargs != 1:
|
2971
|
+
var += "..."
|
2972
|
+
return var
|
2973
|
+
|
2974
|
+
def _parse_decls(
|
2975
|
+
self, decls: t.Sequence[str], expose_value: bool
|
2976
|
+
) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]:
|
2977
|
+
if not decls:
|
2978
|
+
if not expose_value:
|
2979
|
+
return None, [], []
|
2980
|
+
raise TypeError("Could not determine name for argument")
|
2981
|
+
if len(decls) == 1:
|
2982
|
+
name = arg = decls[0]
|
2983
|
+
name = name.replace("-", "_").lower()
|
2984
|
+
else:
|
2985
|
+
raise TypeError(
|
2986
|
+
"Arguments take exactly one parameter declaration, got"
|
2987
|
+
f" {len(decls)}."
|
2988
|
+
)
|
2989
|
+
return name, [arg], []
|
2990
|
+
|
2991
|
+
def get_usage_pieces(self, ctx: Context) -> t.List[str]:
|
2992
|
+
return [self.make_metavar()]
|
2993
|
+
|
2994
|
+
def get_error_hint(self, ctx: Context) -> str:
|
2995
|
+
return f"'{self.make_metavar()}'"
|
2996
|
+
|
2997
|
+
def add_to_parser(self, parser: OptionParser, ctx: Context) -> None:
|
2998
|
+
parser.add_argument(dest=self.name, nargs=self.nargs, obj=self)
|