Spaces:
Runtime error
Runtime error
import enum | |
import errno | |
import inspect | |
import os | |
import sys | |
import typing as t | |
from collections import abc | |
from contextlib import contextmanager | |
from contextlib import ExitStack | |
from functools import partial | |
from functools import update_wrapper | |
from gettext import gettext as _ | |
from gettext import ngettext | |
from itertools import repeat | |
from . import types | |
from .exceptions import Abort | |
from .exceptions import BadParameter | |
from .exceptions import ClickException | |
from .exceptions import Exit | |
from .exceptions import MissingParameter | |
from .exceptions import UsageError | |
from .formatting import HelpFormatter | |
from .formatting import join_options | |
from .globals import pop_context | |
from .globals import push_context | |
from .parser import _flag_needs_value | |
from .parser import OptionParser | |
from .parser import split_opt | |
from .termui import confirm | |
from .termui import prompt | |
from .termui import style | |
from .utils import _detect_program_name | |
from .utils import _expand_args | |
from .utils import echo | |
from .utils import make_default_short_help | |
from .utils import make_str | |
from .utils import PacifyFlushWrapper | |
if t.TYPE_CHECKING: | |
import typing_extensions as te | |
from .shell_completion import CompletionItem | |
F = t.TypeVar("F", bound=t.Callable[..., t.Any]) | |
V = t.TypeVar("V") | |
def _complete_visible_commands( | |
ctx: "Context", incomplete: str | |
) -> t.Iterator[t.Tuple[str, "Command"]]: | |
"""List all the subcommands of a group that start with the | |
incomplete value and aren't hidden. | |
:param ctx: Invocation context for the group. | |
:param incomplete: Value being completed. May be empty. | |
""" | |
multi = t.cast(MultiCommand, ctx.command) | |
for name in multi.list_commands(ctx): | |
if name.startswith(incomplete): | |
command = multi.get_command(ctx, name) | |
if command is not None and not command.hidden: | |
yield name, command | |
def _check_multicommand( | |
base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False | |
) -> None: | |
if not base_command.chain or not isinstance(cmd, MultiCommand): | |
return | |
if register: | |
hint = ( | |
"It is not possible to add multi commands as children to" | |
" another multi command that is in chain mode." | |
) | |
else: | |
hint = ( | |
"Found a multi command as subcommand to a multi command" | |
" that is in chain mode. This is not supported." | |
) | |
raise RuntimeError( | |
f"{hint}. Command {base_command.name!r} is set to chain and" | |
f" {cmd_name!r} was added as a subcommand but it in itself is a" | |
f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" | |
f" within a chained {type(base_command).__name__} named" | |
f" {base_command.name!r})." | |
) | |
def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: | |
return list(zip(*repeat(iter(iterable), batch_size))) | |
def augment_usage_errors( | |
ctx: "Context", param: t.Optional["Parameter"] = None | |
) -> t.Iterator[None]: | |
"""Context manager that attaches extra information to exceptions.""" | |
try: | |
yield | |
except BadParameter as e: | |
if e.ctx is None: | |
e.ctx = ctx | |
if param is not None and e.param is None: | |
e.param = param | |
raise | |
except UsageError as e: | |
if e.ctx is None: | |
e.ctx = ctx | |
raise | |
def iter_params_for_processing( | |
invocation_order: t.Sequence["Parameter"], | |
declaration_order: t.Sequence["Parameter"], | |
) -> t.List["Parameter"]: | |
"""Given a sequence of parameters in the order as should be considered | |
for processing and an iterable of parameters that exist, this returns | |
a list in the correct order as they should be processed. | |
""" | |
def sort_key(item: "Parameter") -> t.Tuple[bool, float]: | |
try: | |
idx: float = invocation_order.index(item) | |
except ValueError: | |
idx = float("inf") | |
return not item.is_eager, idx | |
return sorted(declaration_order, key=sort_key) | |
class ParameterSource(enum.Enum): | |
"""This is an :class:`~enum.Enum` that indicates the source of a | |
parameter's value. | |
Use :meth:`click.Context.get_parameter_source` to get the | |
source for a parameter by name. | |
.. versionchanged:: 8.0 | |
Use :class:`~enum.Enum` and drop the ``validate`` method. | |
.. versionchanged:: 8.0 | |
Added the ``PROMPT`` value. | |
""" | |
COMMANDLINE = enum.auto() | |
"""The value was provided by the command line args.""" | |
ENVIRONMENT = enum.auto() | |
"""The value was provided with an environment variable.""" | |
DEFAULT = enum.auto() | |
"""Used the default specified by the parameter.""" | |
DEFAULT_MAP = enum.auto() | |
"""Used a default provided by :attr:`Context.default_map`.""" | |
PROMPT = enum.auto() | |
"""Used a prompt to confirm a default or provide a value.""" | |
class Context: | |
"""The context is a special internal object that holds state relevant | |
for the script execution at every single level. It's normally invisible | |
to commands unless they opt-in to getting access to it. | |
The context is useful as it can pass internal objects around and can | |
control special execution features such as reading data from | |
environment variables. | |
A context can be used as context manager in which case it will call | |
:meth:`close` on teardown. | |
:param command: the command class for this context. | |
:param parent: the parent context. | |
:param info_name: the info name for this invocation. Generally this | |
is the most descriptive name for the script or | |
command. For the toplevel script it is usually | |
the name of the script, for commands below it it's | |
the name of the script. | |
:param obj: an arbitrary object of user data. | |
:param auto_envvar_prefix: the prefix to use for automatic environment | |
variables. If this is `None` then reading | |
from environment variables is disabled. This | |
does not affect manually set environment | |
variables which are always read. | |
:param default_map: a dictionary (like object) with default values | |
for parameters. | |
:param terminal_width: the width of the terminal. The default is | |
inherit from parent context. If no context | |
defines the terminal width then auto | |
detection will be applied. | |
:param max_content_width: the maximum width for content rendered by | |
Click (this currently only affects help | |
pages). This defaults to 80 characters if | |
not overridden. In other words: even if the | |
terminal is larger than that, Click will not | |
format things wider than 80 characters by | |
default. In addition to that, formatters might | |
add some safety mapping on the right. | |
:param resilient_parsing: if this flag is enabled then Click will | |
parse without any interactivity or callback | |
invocation. Default values will also be | |
ignored. This is useful for implementing | |
things such as completion support. | |
:param allow_extra_args: if this is set to `True` then extra arguments | |
at the end will not raise an error and will be | |
kept on the context. The default is to inherit | |
from the command. | |
:param allow_interspersed_args: if this is set to `False` then options | |
and arguments cannot be mixed. The | |
default is to inherit from the command. | |
:param ignore_unknown_options: instructs click to ignore options it does | |
not know and keeps them for later | |
processing. | |
:param help_option_names: optionally a list of strings that define how | |
the default help parameter is named. The | |
default is ``['--help']``. | |
:param token_normalize_func: an optional function that is used to | |
normalize tokens (options, choices, | |
etc.). This for instance can be used to | |
implement case insensitive behavior. | |
:param color: controls if the terminal supports ANSI colors or not. The | |
default is autodetection. This is only needed if ANSI | |
codes are used in texts that Click prints which is by | |
default not the case. This for instance would affect | |
help output. | |
:param show_default: Show the default value for commands. If this | |
value is not set, it defaults to the value from the parent | |
context. ``Command.show_default`` overrides this default for the | |
specific command. | |
.. versionchanged:: 8.1 | |
The ``show_default`` parameter is overridden by | |
``Command.show_default``, instead of the other way around. | |
.. versionchanged:: 8.0 | |
The ``show_default`` parameter defaults to the value from the | |
parent context. | |
.. versionchanged:: 7.1 | |
Added the ``show_default`` parameter. | |
.. versionchanged:: 4.0 | |
Added the ``color``, ``ignore_unknown_options``, and | |
``max_content_width`` parameters. | |
.. versionchanged:: 3.0 | |
Added the ``allow_extra_args`` and ``allow_interspersed_args`` | |
parameters. | |
.. versionchanged:: 2.0 | |
Added the ``resilient_parsing``, ``help_option_names``, and | |
``token_normalize_func`` parameters. | |
""" | |
#: The formatter class to create with :meth:`make_formatter`. | |
#: | |
#: .. versionadded:: 8.0 | |
formatter_class: t.Type["HelpFormatter"] = HelpFormatter | |
def __init__( | |
self, | |
command: "Command", | |
parent: t.Optional["Context"] = None, | |
info_name: t.Optional[str] = None, | |
obj: t.Optional[t.Any] = None, | |
auto_envvar_prefix: t.Optional[str] = None, | |
default_map: t.Optional[t.Dict[str, t.Any]] = None, | |
terminal_width: t.Optional[int] = None, | |
max_content_width: t.Optional[int] = None, | |
resilient_parsing: bool = False, | |
allow_extra_args: t.Optional[bool] = None, | |
allow_interspersed_args: t.Optional[bool] = None, | |
ignore_unknown_options: t.Optional[bool] = None, | |
help_option_names: t.Optional[t.List[str]] = None, | |
token_normalize_func: t.Optional[t.Callable[[str], str]] = None, | |
color: t.Optional[bool] = None, | |
show_default: t.Optional[bool] = None, | |
) -> None: | |
#: the parent context or `None` if none exists. | |
self.parent = parent | |
#: the :class:`Command` for this context. | |
self.command = command | |
#: the descriptive information name | |
self.info_name = info_name | |
#: Map of parameter names to their parsed values. Parameters | |
#: with ``expose_value=False`` are not stored. | |
self.params: t.Dict[str, t.Any] = {} | |
#: the leftover arguments. | |
self.args: t.List[str] = [] | |
#: protected arguments. These are arguments that are prepended | |
#: to `args` when certain parsing scenarios are encountered but | |
#: must be never propagated to another arguments. This is used | |
#: to implement nested parsing. | |
self.protected_args: t.List[str] = [] | |
#: the collected prefixes of the command's options. | |
self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() | |
if obj is None and parent is not None: | |
obj = parent.obj | |
#: the user object stored. | |
self.obj: t.Any = obj | |
self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) | |
#: A dictionary (-like object) with defaults for parameters. | |
if ( | |
default_map is None | |
and info_name is not None | |
and parent is not None | |
and parent.default_map is not None | |
): | |
default_map = parent.default_map.get(info_name) | |
self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map | |
#: This flag indicates if a subcommand is going to be executed. A | |
#: group callback can use this information to figure out if it's | |
#: being executed directly or because the execution flow passes | |
#: onwards to a subcommand. By default it's None, but it can be | |
#: the name of the subcommand to execute. | |
#: | |
#: If chaining is enabled this will be set to ``'*'`` in case | |
#: any commands are executed. It is however not possible to | |
#: figure out which ones. If you require this knowledge you | |
#: should use a :func:`result_callback`. | |
self.invoked_subcommand: t.Optional[str] = None | |
if terminal_width is None and parent is not None: | |
terminal_width = parent.terminal_width | |
#: The width of the terminal (None is autodetection). | |
self.terminal_width: t.Optional[int] = terminal_width | |
if max_content_width is None and parent is not None: | |
max_content_width = parent.max_content_width | |
#: The maximum width of formatted content (None implies a sensible | |
#: default which is 80 for most things). | |
self.max_content_width: t.Optional[int] = max_content_width | |
if allow_extra_args is None: | |
allow_extra_args = command.allow_extra_args | |
#: Indicates if the context allows extra args or if it should | |
#: fail on parsing. | |
#: | |
#: .. versionadded:: 3.0 | |
self.allow_extra_args = allow_extra_args | |
if allow_interspersed_args is None: | |
allow_interspersed_args = command.allow_interspersed_args | |
#: Indicates if the context allows mixing of arguments and | |
#: options or not. | |
#: | |
#: .. versionadded:: 3.0 | |
self.allow_interspersed_args: bool = allow_interspersed_args | |
if ignore_unknown_options is None: | |
ignore_unknown_options = command.ignore_unknown_options | |
#: Instructs click to ignore options that a command does not | |
#: understand and will store it on the context for later | |
#: processing. This is primarily useful for situations where you | |
#: want to call into external programs. Generally this pattern is | |
#: strongly discouraged because it's not possibly to losslessly | |
#: forward all arguments. | |
#: | |
#: .. versionadded:: 4.0 | |
self.ignore_unknown_options: bool = ignore_unknown_options | |
if help_option_names is None: | |
if parent is not None: | |
help_option_names = parent.help_option_names | |
else: | |
help_option_names = ["--help"] | |
#: The names for the help options. | |
self.help_option_names: t.List[str] = help_option_names | |
if token_normalize_func is None and parent is not None: | |
token_normalize_func = parent.token_normalize_func | |
#: An optional normalization function for tokens. This is | |
#: options, choices, commands etc. | |
self.token_normalize_func: t.Optional[ | |
t.Callable[[str], str] | |
] = token_normalize_func | |
#: Indicates if resilient parsing is enabled. In that case Click | |
#: will do its best to not cause any failures and default values | |
#: will be ignored. Useful for completion. | |
self.resilient_parsing: bool = resilient_parsing | |
# If there is no envvar prefix yet, but the parent has one and | |
# the command on this level has a name, we can expand the envvar | |
# prefix automatically. | |
if auto_envvar_prefix is None: | |
if ( | |
parent is not None | |
and parent.auto_envvar_prefix is not None | |
and self.info_name is not None | |
): | |
auto_envvar_prefix = ( | |
f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" | |
) | |
else: | |
auto_envvar_prefix = auto_envvar_prefix.upper() | |
if auto_envvar_prefix is not None: | |
auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") | |
self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix | |
if color is None and parent is not None: | |
color = parent.color | |
#: Controls if styling output is wanted or not. | |
self.color: t.Optional[bool] = color | |
if show_default is None and parent is not None: | |
show_default = parent.show_default | |
#: Show option default values when formatting help text. | |
self.show_default: t.Optional[bool] = show_default | |
self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] | |
self._depth = 0 | |
self._parameter_source: t.Dict[str, ParameterSource] = {} | |
self._exit_stack = ExitStack() | |
def to_info_dict(self) -> t.Dict[str, t.Any]: | |
"""Gather information that could be useful for a tool generating | |
user-facing documentation. This traverses the entire CLI | |
structure. | |
.. code-block:: python | |
with Context(cli) as ctx: | |
info = ctx.to_info_dict() | |
.. versionadded:: 8.0 | |
""" | |
return { | |
"command": self.command.to_info_dict(self), | |
"info_name": self.info_name, | |
"allow_extra_args": self.allow_extra_args, | |
"allow_interspersed_args": self.allow_interspersed_args, | |
"ignore_unknown_options": self.ignore_unknown_options, | |
"auto_envvar_prefix": self.auto_envvar_prefix, | |
} | |
def __enter__(self) -> "Context": | |
self._depth += 1 | |
push_context(self) | |
return self | |
def __exit__(self, exc_type, exc_value, tb): # type: ignore | |
self._depth -= 1 | |
if self._depth == 0: | |
self.close() | |
pop_context() | |
def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: | |
"""This helper method can be used with the context object to promote | |
it to the current thread local (see :func:`get_current_context`). | |
The default behavior of this is to invoke the cleanup functions which | |
can be disabled by setting `cleanup` to `False`. The cleanup | |
functions are typically used for things such as closing file handles. | |
If the cleanup is intended the context object can also be directly | |
used as a context manager. | |
Example usage:: | |
with ctx.scope(): | |
assert get_current_context() is ctx | |
This is equivalent:: | |
with ctx: | |
assert get_current_context() is ctx | |
.. versionadded:: 5.0 | |
:param cleanup: controls if the cleanup functions should be run or | |
not. The default is to run these functions. In | |
some situations the context only wants to be | |
temporarily pushed in which case this can be disabled. | |
Nested pushes automatically defer the cleanup. | |
""" | |
if not cleanup: | |
self._depth += 1 | |
try: | |
with self as rv: | |
yield rv | |
finally: | |
if not cleanup: | |
self._depth -= 1 | |
def meta(self) -> t.Dict[str, t.Any]: | |
"""This is a dictionary which is shared with all the contexts | |
that are nested. It exists so that click utilities can store some | |
state here if they need to. It is however the responsibility of | |
that code to manage this dictionary well. | |
The keys are supposed to be unique dotted strings. For instance | |
module paths are a good choice for it. What is stored in there is | |
irrelevant for the operation of click. However what is important is | |
that code that places data here adheres to the general semantics of | |
the system. | |
Example usage:: | |
LANG_KEY = f'{__name__}.lang' | |
def set_language(value): | |
ctx = get_current_context() | |
ctx.meta[LANG_KEY] = value | |
def get_language(): | |
return get_current_context().meta.get(LANG_KEY, 'en_US') | |
.. versionadded:: 5.0 | |
""" | |
return self._meta | |
def make_formatter(self) -> HelpFormatter: | |
"""Creates the :class:`~click.HelpFormatter` for the help and | |
usage output. | |
To quickly customize the formatter class used without overriding | |
this method, set the :attr:`formatter_class` attribute. | |
.. versionchanged:: 8.0 | |
Added the :attr:`formatter_class` attribute. | |
""" | |
return self.formatter_class( | |
width=self.terminal_width, max_width=self.max_content_width | |
) | |
def with_resource(self, context_manager: t.ContextManager[V]) -> V: | |
"""Register a resource as if it were used in a ``with`` | |
statement. The resource will be cleaned up when the context is | |
popped. | |
Uses :meth:`contextlib.ExitStack.enter_context`. It calls the | |
resource's ``__enter__()`` method and returns the result. When | |
the context is popped, it closes the stack, which calls the | |
resource's ``__exit__()`` method. | |
To register a cleanup function for something that isn't a | |
context manager, use :meth:`call_on_close`. Or use something | |
from :mod:`contextlib` to turn it into a context manager first. | |
.. code-block:: python | |
@click.group() | |
@click.option("--name") | |
@click.pass_context | |
def cli(ctx): | |
ctx.obj = ctx.with_resource(connect_db(name)) | |
:param context_manager: The context manager to enter. | |
:return: Whatever ``context_manager.__enter__()`` returns. | |
.. versionadded:: 8.0 | |
""" | |
return self._exit_stack.enter_context(context_manager) | |
def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: | |
"""Register a function to be called when the context tears down. | |
This can be used to close resources opened during the script | |
execution. Resources that support Python's context manager | |
protocol which would be used in a ``with`` statement should be | |
registered with :meth:`with_resource` instead. | |
:param f: The function to execute on teardown. | |
""" | |
return self._exit_stack.callback(f) | |
def close(self) -> None: | |
"""Invoke all close callbacks registered with | |
:meth:`call_on_close`, and exit all context managers entered | |
with :meth:`with_resource`. | |
""" | |
self._exit_stack.close() | |
# In case the context is reused, create a new exit stack. | |
self._exit_stack = ExitStack() | |
def command_path(self) -> str: | |
"""The computed command path. This is used for the ``usage`` | |
information on the help page. It's automatically created by | |
combining the info names of the chain of contexts to the root. | |
""" | |
rv = "" | |
if self.info_name is not None: | |
rv = self.info_name | |
if self.parent is not None: | |
parent_command_path = [self.parent.command_path] | |
if isinstance(self.parent.command, Command): | |
for param in self.parent.command.get_params(self): | |
parent_command_path.extend(param.get_usage_pieces(self)) | |
rv = f"{' '.join(parent_command_path)} {rv}" | |
return rv.lstrip() | |
def find_root(self) -> "Context": | |
"""Finds the outermost context.""" | |
node = self | |
while node.parent is not None: | |
node = node.parent | |
return node | |
def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: | |
"""Finds the closest object of a given type.""" | |
node: t.Optional["Context"] = self | |
while node is not None: | |
if isinstance(node.obj, object_type): | |
return node.obj | |
node = node.parent | |
return None | |
def ensure_object(self, object_type: t.Type[V]) -> V: | |
"""Like :meth:`find_object` but sets the innermost object to a | |
new instance of `object_type` if it does not exist. | |
""" | |
rv = self.find_object(object_type) | |
if rv is None: | |
self.obj = rv = object_type() | |
return rv | |
def lookup_default( | |
self, name: str, call: "te.Literal[True]" = True | |
) -> t.Optional[t.Any]: | |
... | |
def lookup_default( | |
self, name: str, call: "te.Literal[False]" = ... | |
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: | |
... | |
def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: | |
"""Get the default for a parameter from :attr:`default_map`. | |
:param name: Name of the parameter. | |
:param call: If the default is a callable, call it. Disable to | |
return the callable instead. | |
.. versionchanged:: 8.0 | |
Added the ``call`` parameter. | |
""" | |
if self.default_map is not None: | |
value = self.default_map.get(name) | |
if call and callable(value): | |
return value() | |
return value | |
return None | |
def fail(self, message: str) -> "te.NoReturn": | |
"""Aborts the execution of the program with a specific error | |
message. | |
:param message: the error message to fail with. | |
""" | |
raise UsageError(message, self) | |
def abort(self) -> "te.NoReturn": | |
"""Aborts the script.""" | |
raise Abort() | |
def exit(self, code: int = 0) -> "te.NoReturn": | |
"""Exits the application with a given exit code.""" | |
raise Exit(code) | |
def get_usage(self) -> str: | |
"""Helper method to get formatted usage string for the current | |
context and command. | |
""" | |
return self.command.get_usage(self) | |
def get_help(self) -> str: | |
"""Helper method to get formatted help page for the current | |
context and command. | |
""" | |
return self.command.get_help(self) | |
def _make_sub_context(self, command: "Command") -> "Context": | |
"""Create a new context of the same type as this context, but | |
for a new command. | |
:meta private: | |
""" | |
return type(self)(command, info_name=command.name, parent=self) | |
def invoke( | |
__self, # noqa: B902 | |
__callback: t.Union["Command", t.Callable[..., t.Any]], | |
*args: t.Any, | |
**kwargs: t.Any, | |
) -> t.Any: | |
"""Invokes a command callback in exactly the way it expects. There | |
are two ways to invoke this method: | |
1. the first argument can be a callback and all other arguments and | |
keyword arguments are forwarded directly to the function. | |
2. the first argument is a click command object. In that case all | |
arguments are forwarded as well but proper click parameters | |
(options and click arguments) must be keyword arguments and Click | |
will fill in defaults. | |
Note that before Click 3.2 keyword arguments were not properly filled | |
in against the intention of this code and no context was created. For | |
more information about this change and why it was done in a bugfix | |
release see :ref:`upgrade-to-3.2`. | |
.. versionchanged:: 8.0 | |
All ``kwargs`` are tracked in :attr:`params` so they will be | |
passed if :meth:`forward` is called at multiple levels. | |
""" | |
if isinstance(__callback, Command): | |
other_cmd = __callback | |
if other_cmd.callback is None: | |
raise TypeError( | |
"The given command does not have a callback that can be invoked." | |
) | |
else: | |
__callback = other_cmd.callback | |
ctx = __self._make_sub_context(other_cmd) | |
for param in other_cmd.params: | |
if param.name not in kwargs and param.expose_value: | |
kwargs[param.name] = param.type_cast_value( # type: ignore | |
ctx, param.get_default(ctx) | |
) | |
# Track all kwargs as params, so that forward() will pass | |
# them on in subsequent calls. | |
ctx.params.update(kwargs) | |
else: | |
ctx = __self | |
with augment_usage_errors(__self): | |
with ctx: | |
return __callback(*args, **kwargs) | |
def forward( | |
__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 | |
) -> t.Any: | |
"""Similar to :meth:`invoke` but fills in default keyword | |
arguments from the current context if the other command expects | |
it. This cannot invoke callbacks directly, only other commands. | |
.. versionchanged:: 8.0 | |
All ``kwargs`` are tracked in :attr:`params` so they will be | |
passed if ``forward`` is called at multiple levels. | |
""" | |
# Can only forward to other commands, not direct callbacks. | |
if not isinstance(__cmd, Command): | |
raise TypeError("Callback is not a command.") | |
for param in __self.params: | |
if param not in kwargs: | |
kwargs[param] = __self.params[param] | |
return __self.invoke(__cmd, *args, **kwargs) | |
def set_parameter_source(self, name: str, source: ParameterSource) -> None: | |
"""Set the source of a parameter. This indicates the location | |
from which the value of the parameter was obtained. | |
:param name: The name of the parameter. | |
:param source: A member of :class:`~click.core.ParameterSource`. | |
""" | |
self._parameter_source[name] = source | |
def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: | |
"""Get the source of a parameter. This indicates the location | |
from which the value of the parameter was obtained. | |
This can be useful for determining when a user specified a value | |
on the command line that is the same as the default value. It | |
will be :attr:`~click.core.ParameterSource.DEFAULT` only if the | |
value was actually taken from the default. | |
:param name: The name of the parameter. | |
:rtype: ParameterSource | |
.. versionchanged:: 8.0 | |
Returns ``None`` if the parameter was not provided from any | |
source. | |
""" | |
return self._parameter_source.get(name) | |
class BaseCommand: | |
"""The base command implements the minimal API contract of commands. | |
Most code will never use this as it does not implement a lot of useful | |
functionality but it can act as the direct subclass of alternative | |
parsing methods that do not depend on the Click parser. | |
For instance, this can be used to bridge Click and other systems like | |
argparse or docopt. | |
Because base commands do not implement a lot of the API that other | |
parts of Click take for granted, they are not supported for all | |
operations. For instance, they cannot be used with the decorators | |
usually and they have no built-in callback system. | |
.. versionchanged:: 2.0 | |
Added the `context_settings` parameter. | |
:param name: the name of the command to use unless a group overrides it. | |
:param context_settings: an optional dictionary with defaults that are | |
passed to the context object. | |
""" | |
#: The context class to create with :meth:`make_context`. | |
#: | |
#: .. versionadded:: 8.0 | |
context_class: t.Type[Context] = Context | |
#: the default for the :attr:`Context.allow_extra_args` flag. | |
allow_extra_args = False | |
#: the default for the :attr:`Context.allow_interspersed_args` flag. | |
allow_interspersed_args = True | |
#: the default for the :attr:`Context.ignore_unknown_options` flag. | |
ignore_unknown_options = False | |
def __init__( | |
self, | |
name: t.Optional[str], | |
context_settings: t.Optional[t.Dict[str, t.Any]] = None, | |
) -> None: | |
#: the name the command thinks it has. Upon registering a command | |
#: on a :class:`Group` the group will default the command name | |
#: with this information. You should instead use the | |
#: :class:`Context`\'s :attr:`~Context.info_name` attribute. | |
self.name = name | |
if context_settings is None: | |
context_settings = {} | |
#: an optional dictionary with defaults passed to the context. | |
self.context_settings: t.Dict[str, t.Any] = context_settings | |
def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: | |
"""Gather information that could be useful for a tool generating | |
user-facing documentation. This traverses the entire structure | |
below this command. | |
Use :meth:`click.Context.to_info_dict` to traverse the entire | |
CLI structure. | |
:param ctx: A :class:`Context` representing this command. | |
.. versionadded:: 8.0 | |
""" | |
return {"name": self.name} | |
def __repr__(self) -> str: | |
return f"<{self.__class__.__name__} {self.name}>" | |
def get_usage(self, ctx: Context) -> str: | |
raise NotImplementedError("Base commands cannot get usage") | |
def get_help(self, ctx: Context) -> str: | |
raise NotImplementedError("Base commands cannot get help") | |
def make_context( | |
self, | |
info_name: t.Optional[str], | |
args: t.List[str], | |
parent: t.Optional[Context] = None, | |
**extra: t.Any, | |
) -> Context: | |
"""This function when given an info name and arguments will kick | |
off the parsing and create a new :class:`Context`. It does not | |
invoke the actual command callback though. | |
To quickly customize the context class used without overriding | |
this method, set the :attr:`context_class` attribute. | |
:param info_name: the info name for this invocation. Generally this | |
is the most descriptive name for the script or | |
command. For the toplevel script it's usually | |
the name of the script, for commands below it it's | |
the name of the command. | |
:param args: the arguments to parse as list of strings. | |
:param parent: the parent context if available. | |
:param extra: extra keyword arguments forwarded to the context | |
constructor. | |
.. versionchanged:: 8.0 | |
Added the :attr:`context_class` attribute. | |
""" | |
for key, value in self.context_settings.items(): | |
if key not in extra: | |
extra[key] = value | |
ctx = self.context_class( | |
self, info_name=info_name, parent=parent, **extra # type: ignore | |
) | |
with ctx.scope(cleanup=False): | |
self.parse_args(ctx, args) | |
return ctx | |
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: | |
"""Given a context and a list of arguments this creates the parser | |
and parses the arguments, then modifies the context as necessary. | |
This is automatically invoked by :meth:`make_context`. | |
""" | |
raise NotImplementedError("Base commands do not know how to parse arguments.") | |
def invoke(self, ctx: Context) -> t.Any: | |
"""Given a context, this invokes the command. The default | |
implementation is raising a not implemented error. | |
""" | |
raise NotImplementedError("Base commands are not invokable by default") | |
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: | |
"""Return a list of completions for the incomplete value. Looks | |
at the names of chained multi-commands. | |
Any command could be part of a chained multi-command, so sibling | |
commands are valid at any point during command completion. Other | |
command classes will return more completions. | |
:param ctx: Invocation context for this command. | |
:param incomplete: Value being completed. May be empty. | |
.. versionadded:: 8.0 | |
""" | |
from click.shell_completion import CompletionItem | |
results: t.List["CompletionItem"] = [] | |
while ctx.parent is not None: | |
ctx = ctx.parent | |
if isinstance(ctx.command, MultiCommand) and ctx.command.chain: | |
results.extend( | |
CompletionItem(name, help=command.get_short_help_str()) | |
for name, command in _complete_visible_commands(ctx, incomplete) | |
if name not in ctx.protected_args | |
) | |
return results | |
def main( | |
self, | |
args: t.Optional[t.Sequence[str]] = None, | |
prog_name: t.Optional[str] = None, | |
complete_var: t.Optional[str] = None, | |
standalone_mode: "te.Literal[True]" = True, | |
**extra: t.Any, | |
) -> "te.NoReturn": | |
... | |
def main( | |
self, | |
args: t.Optional[t.Sequence[str]] = None, | |
prog_name: t.Optional[str] = None, | |
complete_var: t.Optional[str] = None, | |
standalone_mode: bool = ..., | |
**extra: t.Any, | |
) -> t.Any: | |
... | |
def main( | |
self, | |
args: t.Optional[t.Sequence[str]] = None, | |
prog_name: t.Optional[str] = None, | |
complete_var: t.Optional[str] = None, | |
standalone_mode: bool = True, | |
windows_expand_args: bool = True, | |
**extra: t.Any, | |
) -> t.Any: | |
"""This is the way to invoke a script with all the bells and | |
whistles as a command line application. This will always terminate | |
the application after a call. If this is not wanted, ``SystemExit`` | |
needs to be caught. | |
This method is also available by directly calling the instance of | |
a :class:`Command`. | |
:param args: the arguments that should be used for parsing. If not | |
provided, ``sys.argv[1:]`` is used. | |
:param prog_name: the program name that should be used. By default | |
the program name is constructed by taking the file | |
name from ``sys.argv[0]``. | |
:param complete_var: the environment variable that controls the | |
bash completion support. The default is | |
``"_<prog_name>_COMPLETE"`` with prog_name in | |
uppercase. | |
:param standalone_mode: the default behavior is to invoke the script | |
in standalone mode. Click will then | |
handle exceptions and convert them into | |
error messages and the function will never | |
return but shut down the interpreter. If | |
this is set to `False` they will be | |
propagated to the caller and the return | |
value of this function is the return value | |
of :meth:`invoke`. | |
:param windows_expand_args: Expand glob patterns, user dir, and | |
env vars in command line args on Windows. | |
:param extra: extra keyword arguments are forwarded to the context | |
constructor. See :class:`Context` for more information. | |
.. versionchanged:: 8.0.1 | |
Added the ``windows_expand_args`` parameter to allow | |
disabling command line arg expansion on Windows. | |
.. versionchanged:: 8.0 | |
When taking arguments from ``sys.argv`` on Windows, glob | |
patterns, user dir, and env vars are expanded. | |
.. versionchanged:: 3.0 | |
Added the ``standalone_mode`` parameter. | |
""" | |
if args is None: | |
args = sys.argv[1:] | |
if os.name == "nt" and windows_expand_args: | |
args = _expand_args(args) | |
else: | |
args = list(args) | |
if prog_name is None: | |
prog_name = _detect_program_name() | |
# Process shell completion requests and exit early. | |
self._main_shell_completion(extra, prog_name, complete_var) | |
try: | |
try: | |
with self.make_context(prog_name, args, **extra) as ctx: | |
rv = self.invoke(ctx) | |
if not standalone_mode: | |
return rv | |
# it's not safe to `ctx.exit(rv)` here! | |
# note that `rv` may actually contain data like "1" which | |
# has obvious effects | |
# more subtle case: `rv=[None, None]` can come out of | |
# chained commands which all returned `None` -- so it's not | |
# even always obvious that `rv` indicates success/failure | |
# by its truthiness/falsiness | |
ctx.exit() | |
except (EOFError, KeyboardInterrupt): | |
echo(file=sys.stderr) | |
raise Abort() from None | |
except ClickException as e: | |
if not standalone_mode: | |
raise | |
e.show() | |
sys.exit(e.exit_code) | |
except OSError as e: | |
if e.errno == errno.EPIPE: | |
sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) | |
sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) | |
sys.exit(1) | |
else: | |
raise | |
except Exit as e: | |
if standalone_mode: | |
sys.exit(e.exit_code) | |
else: | |
# in non-standalone mode, return the exit code | |
# note that this is only reached if `self.invoke` above raises | |
# an Exit explicitly -- thus bypassing the check there which | |
# would return its result | |
# the results of non-standalone execution may therefore be | |
# somewhat ambiguous: if there are codepaths which lead to | |
# `ctx.exit(1)` and to `return 1`, the caller won't be able to | |
# tell the difference between the two | |
return e.exit_code | |
except Abort: | |
if not standalone_mode: | |
raise | |
echo(_("Aborted!"), file=sys.stderr) | |
sys.exit(1) | |
def _main_shell_completion( | |
self, | |
ctx_args: t.Dict[str, t.Any], | |
prog_name: str, | |
complete_var: t.Optional[str] = None, | |
) -> None: | |
"""Check if the shell is asking for tab completion, process | |
that, then exit early. Called from :meth:`main` before the | |
program is invoked. | |
:param prog_name: Name of the executable in the shell. | |
:param complete_var: Name of the environment variable that holds | |
the completion instruction. Defaults to | |
``_{PROG_NAME}_COMPLETE``. | |
""" | |
if complete_var is None: | |
complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper() | |
instruction = os.environ.get(complete_var) | |
if not instruction: | |
return | |
from .shell_completion import shell_complete | |
rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) | |
sys.exit(rv) | |
def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: | |
"""Alias for :meth:`main`.""" | |
return self.main(*args, **kwargs) | |
class Command(BaseCommand): | |
"""Commands are the basic building block of command line interfaces in | |
Click. A basic command handles command line parsing and might dispatch | |
more parsing to commands nested below it. | |
:param name: the name of the command to use unless a group overrides it. | |
:param context_settings: an optional dictionary with defaults that are | |
passed to the context object. | |
:param callback: the callback to invoke. This is optional. | |
:param params: the parameters to register with this command. This can | |
be either :class:`Option` or :class:`Argument` objects. | |
:param help: the help string to use for this command. | |
:param epilog: like the help string but it's printed at the end of the | |
help page after everything else. | |
:param short_help: the short help to use for this command. This is | |
shown on the command listing of the parent command. | |
:param add_help_option: by default each command registers a ``--help`` | |
option. This can be disabled by this parameter. | |
:param no_args_is_help: this controls what happens if no arguments are | |
provided. This option is disabled by default. | |
If enabled this will add ``--help`` as argument | |
if no arguments are passed | |
:param hidden: hide this command from help outputs. | |
:param deprecated: issues a message indicating that | |
the command is deprecated. | |
.. versionchanged:: 8.1 | |
``help``, ``epilog``, and ``short_help`` are stored unprocessed, | |
all formatting is done when outputting help text, not at init, | |
and is done even if not using the ``@command`` decorator. | |
.. versionchanged:: 8.0 | |
Added a ``repr`` showing the command name. | |
.. versionchanged:: 7.1 | |
Added the ``no_args_is_help`` parameter. | |
.. versionchanged:: 2.0 | |
Added the ``context_settings`` parameter. | |
""" | |
def __init__( | |
self, | |
name: t.Optional[str], | |
context_settings: t.Optional[t.Dict[str, t.Any]] = None, | |
callback: t.Optional[t.Callable[..., t.Any]] = None, | |
params: t.Optional[t.List["Parameter"]] = None, | |
help: t.Optional[str] = None, | |
epilog: t.Optional[str] = None, | |
short_help: t.Optional[str] = None, | |
options_metavar: t.Optional[str] = "[OPTIONS]", | |
add_help_option: bool = True, | |
no_args_is_help: bool = False, | |
hidden: bool = False, | |
deprecated: bool = False, | |
) -> None: | |
super().__init__(name, context_settings) | |
#: the callback to execute when the command fires. This might be | |
#: `None` in which case nothing happens. | |
self.callback = callback | |
#: the list of parameters for this command in the order they | |
#: should show up in the help page and execute. Eager parameters | |
#: will automatically be handled before non eager ones. | |
self.params: t.List["Parameter"] = params or [] | |
self.help = help | |
self.epilog = epilog | |
self.options_metavar = options_metavar | |
self.short_help = short_help | |
self.add_help_option = add_help_option | |
self.no_args_is_help = no_args_is_help | |
self.hidden = hidden | |
self.deprecated = deprecated | |
def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: | |
info_dict = super().to_info_dict(ctx) | |
info_dict.update( | |
params=[param.to_info_dict() for param in self.get_params(ctx)], | |
help=self.help, | |
epilog=self.epilog, | |
short_help=self.short_help, | |
hidden=self.hidden, | |
deprecated=self.deprecated, | |
) | |
return info_dict | |
def get_usage(self, ctx: Context) -> str: | |
"""Formats the usage line into a string and returns it. | |
Calls :meth:`format_usage` internally. | |
""" | |
formatter = ctx.make_formatter() | |
self.format_usage(ctx, formatter) | |
return formatter.getvalue().rstrip("\n") | |
def get_params(self, ctx: Context) -> t.List["Parameter"]: | |
rv = self.params | |
help_option = self.get_help_option(ctx) | |
if help_option is not None: | |
rv = [*rv, help_option] | |
return rv | |
def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: | |
"""Writes the usage line into the formatter. | |
This is a low-level method called by :meth:`get_usage`. | |
""" | |
pieces = self.collect_usage_pieces(ctx) | |
formatter.write_usage(ctx.command_path, " ".join(pieces)) | |
def collect_usage_pieces(self, ctx: Context) -> t.List[str]: | |
"""Returns all the pieces that go into the usage line and returns | |
it as a list of strings. | |
""" | |
rv = [self.options_metavar] if self.options_metavar else [] | |
for param in self.get_params(ctx): | |
rv.extend(param.get_usage_pieces(ctx)) | |
return rv | |
def get_help_option_names(self, ctx: Context) -> t.List[str]: | |
"""Returns the names for the help option.""" | |
all_names = set(ctx.help_option_names) | |
for param in self.params: | |
all_names.difference_update(param.opts) | |
all_names.difference_update(param.secondary_opts) | |
return list(all_names) | |
def get_help_option(self, ctx: Context) -> t.Optional["Option"]: | |
"""Returns the help option object.""" | |
help_options = self.get_help_option_names(ctx) | |
if not help_options or not self.add_help_option: | |
return None | |
def show_help(ctx: Context, param: "Parameter", value: str) -> None: | |
if value and not ctx.resilient_parsing: | |
echo(ctx.get_help(), color=ctx.color) | |
ctx.exit() | |
return Option( | |
help_options, | |
is_flag=True, | |
is_eager=True, | |
expose_value=False, | |
callback=show_help, | |
help=_("Show this message and exit."), | |
) | |
def make_parser(self, ctx: Context) -> OptionParser: | |
"""Creates the underlying option parser for this command.""" | |
parser = OptionParser(ctx) | |
for param in self.get_params(ctx): | |
param.add_to_parser(parser, ctx) | |
return parser | |
def get_help(self, ctx: Context) -> str: | |
"""Formats the help into a string and returns it. | |
Calls :meth:`format_help` internally. | |
""" | |
formatter = ctx.make_formatter() | |
self.format_help(ctx, formatter) | |
return formatter.getvalue().rstrip("\n") | |
def get_short_help_str(self, limit: int = 45) -> str: | |
"""Gets short help for the command or makes it by shortening the | |
long help string. | |
""" | |
if self.short_help: | |
text = inspect.cleandoc(self.short_help) | |
elif self.help: | |
text = make_default_short_help(self.help, limit) | |
else: | |
text = "" | |
if self.deprecated: | |
text = _("(Deprecated) {text}").format(text=text) | |
return text.strip() | |
def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: | |
"""Writes the help into the formatter if it exists. | |
This is a low-level method called by :meth:`get_help`. | |
This calls the following methods: | |
- :meth:`format_usage` | |
- :meth:`format_help_text` | |
- :meth:`format_options` | |
- :meth:`format_epilog` | |
""" | |
self.format_usage(ctx, formatter) | |
self.format_help_text(ctx, formatter) | |
self.format_options(ctx, formatter) | |
self.format_epilog(ctx, formatter) | |
def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: | |
"""Writes the help text to the formatter if it exists.""" | |
text = self.help if self.help is not None else "" | |
if self.deprecated: | |
text = _("(Deprecated) {text}").format(text=text) | |
if text: | |
text = inspect.cleandoc(text).partition("\f")[0] | |
formatter.write_paragraph() | |
with formatter.indentation(): | |
formatter.write_text(text) | |
def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: | |
"""Writes all the options into the formatter if they exist.""" | |
opts = [] | |
for param in self.get_params(ctx): | |
rv = param.get_help_record(ctx) | |
if rv is not None: | |
opts.append(rv) | |
if opts: | |
with formatter.section(_("Options")): | |
formatter.write_dl(opts) | |
def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: | |
"""Writes the epilog into the formatter if it exists.""" | |
if self.epilog: | |
epilog = inspect.cleandoc(self.epilog) | |
formatter.write_paragraph() | |
with formatter.indentation(): | |
formatter.write_text(epilog) | |
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: | |
if not args and self.no_args_is_help and not ctx.resilient_parsing: | |
echo(ctx.get_help(), color=ctx.color) | |
ctx.exit() | |
parser = self.make_parser(ctx) | |
opts, args, param_order = parser.parse_args(args=args) | |
for param in iter_params_for_processing(param_order, self.get_params(ctx)): | |
value, args = param.handle_parse_result(ctx, opts, args) | |
if args and not ctx.allow_extra_args and not ctx.resilient_parsing: | |
ctx.fail( | |
ngettext( | |
"Got unexpected extra argument ({args})", | |
"Got unexpected extra arguments ({args})", | |
len(args), | |
).format(args=" ".join(map(str, args))) | |
) | |
ctx.args = args | |
ctx._opt_prefixes.update(parser._opt_prefixes) | |
return args | |
def invoke(self, ctx: Context) -> t.Any: | |
"""Given a context, this invokes the attached callback (if it exists) | |
in the right way. | |
""" | |
if self.deprecated: | |
message = _( | |
"DeprecationWarning: The command {name!r} is deprecated." | |
).format(name=self.name) | |
echo(style(message, fg="red"), err=True) | |
if self.callback is not None: | |
return ctx.invoke(self.callback, **ctx.params) | |
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: | |
"""Return a list of completions for the incomplete value. Looks | |
at the names of options and chained multi-commands. | |
:param ctx: Invocation context for this command. | |
:param incomplete: Value being completed. May be empty. | |
.. versionadded:: 8.0 | |
""" | |
from click.shell_completion import CompletionItem | |
results: t.List["CompletionItem"] = [] | |
if incomplete and not incomplete[0].isalnum(): | |
for param in self.get_params(ctx): | |
if ( | |
not isinstance(param, Option) | |
or param.hidden | |
or ( | |
not param.multiple | |
and ctx.get_parameter_source(param.name) # type: ignore | |
is ParameterSource.COMMANDLINE | |
) | |
): | |
continue | |
results.extend( | |
CompletionItem(name, help=param.help) | |
for name in [*param.opts, *param.secondary_opts] | |
if name.startswith(incomplete) | |
) | |
results.extend(super().shell_complete(ctx, incomplete)) | |
return results | |
class MultiCommand(Command): | |
"""A multi command is the basic implementation of a command that | |
dispatches to subcommands. The most common version is the | |
:class:`Group`. | |
:param invoke_without_command: this controls how the multi command itself | |
is invoked. By default it's only invoked | |
if a subcommand is provided. | |
:param no_args_is_help: this controls what happens if no arguments are | |
provided. This option is enabled by default if | |
`invoke_without_command` is disabled or disabled | |
if it's enabled. If enabled this will add | |
``--help`` as argument if no arguments are | |
passed. | |
:param subcommand_metavar: the string that is used in the documentation | |
to indicate the subcommand place. | |
:param chain: if this is set to `True` chaining of multiple subcommands | |
is enabled. This restricts the form of commands in that | |
they cannot have optional arguments but it allows | |
multiple commands to be chained together. | |
:param result_callback: The result callback to attach to this multi | |
command. This can be set or changed later with the | |
:meth:`result_callback` decorator. | |
""" | |
allow_extra_args = True | |
allow_interspersed_args = False | |
def __init__( | |
self, | |
name: t.Optional[str] = None, | |
invoke_without_command: bool = False, | |
no_args_is_help: t.Optional[bool] = None, | |
subcommand_metavar: t.Optional[str] = None, | |
chain: bool = False, | |
result_callback: t.Optional[t.Callable[..., t.Any]] = None, | |
**attrs: t.Any, | |
) -> None: | |
super().__init__(name, **attrs) | |
if no_args_is_help is None: | |
no_args_is_help = not invoke_without_command | |
self.no_args_is_help = no_args_is_help | |
self.invoke_without_command = invoke_without_command | |
if subcommand_metavar is None: | |
if chain: | |
subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." | |
else: | |
subcommand_metavar = "COMMAND [ARGS]..." | |
self.subcommand_metavar = subcommand_metavar | |
self.chain = chain | |
# The result callback that is stored. This can be set or | |
# overridden with the :func:`result_callback` decorator. | |
self._result_callback = result_callback | |
if self.chain: | |
for param in self.params: | |
if isinstance(param, Argument) and not param.required: | |
raise RuntimeError( | |
"Multi commands in chain mode cannot have" | |
" optional arguments." | |
) | |
def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: | |
info_dict = super().to_info_dict(ctx) | |
commands = {} | |
for name in self.list_commands(ctx): | |
command = self.get_command(ctx, name) | |
if command is None: | |
continue | |
sub_ctx = ctx._make_sub_context(command) | |
with sub_ctx.scope(cleanup=False): | |
commands[name] = command.to_info_dict(sub_ctx) | |
info_dict.update(commands=commands, chain=self.chain) | |
return info_dict | |
def collect_usage_pieces(self, ctx: Context) -> t.List[str]: | |
rv = super().collect_usage_pieces(ctx) | |
rv.append(self.subcommand_metavar) | |
return rv | |
def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: | |
super().format_options(ctx, formatter) | |
self.format_commands(ctx, formatter) | |
def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: | |
"""Adds a result callback to the command. By default if a | |
result callback is already registered this will chain them but | |
this can be disabled with the `replace` parameter. The result | |
callback is invoked with the return value of the subcommand | |
(or the list of return values from all subcommands if chaining | |
is enabled) as well as the parameters as they would be passed | |
to the main callback. | |
Example:: | |
@click.group() | |
@click.option('-i', '--input', default=23) | |
def cli(input): | |
return 42 | |
@cli.result_callback() | |
def process_result(result, input): | |
return result + input | |
:param replace: if set to `True` an already existing result | |
callback will be removed. | |
.. versionchanged:: 8.0 | |
Renamed from ``resultcallback``. | |
.. versionadded:: 3.0 | |
""" | |
def decorator(f: F) -> F: | |
old_callback = self._result_callback | |
if old_callback is None or replace: | |
self._result_callback = f | |
return f | |
def function(__value, *args, **kwargs): # type: ignore | |
inner = old_callback(__value, *args, **kwargs) # type: ignore | |
return f(inner, *args, **kwargs) | |
self._result_callback = rv = update_wrapper(t.cast(F, function), f) | |
return rv | |
return decorator | |
def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: | |
"""Extra format methods for multi methods that adds all the commands | |
after the options. | |
""" | |
commands = [] | |
for subcommand in self.list_commands(ctx): | |
cmd = self.get_command(ctx, subcommand) | |
# What is this, the tool lied about a command. Ignore it | |
if cmd is None: | |
continue | |
if cmd.hidden: | |
continue | |
commands.append((subcommand, cmd)) | |
# allow for 3 times the default spacing | |
if len(commands): | |
limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) | |
rows = [] | |
for subcommand, cmd in commands: | |
help = cmd.get_short_help_str(limit) | |
rows.append((subcommand, help)) | |
if rows: | |
with formatter.section(_("Commands")): | |
formatter.write_dl(rows) | |
def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: | |
if not args and self.no_args_is_help and not ctx.resilient_parsing: | |
echo(ctx.get_help(), color=ctx.color) | |
ctx.exit() | |
rest = super().parse_args(ctx, args) | |
if self.chain: | |
ctx.protected_args = rest | |
ctx.args = [] | |
elif rest: | |
ctx.protected_args, ctx.args = rest[:1], rest[1:] | |
return ctx.args | |
def invoke(self, ctx: Context) -> t.Any: | |
def _process_result(value: t.Any) -> t.Any: | |
if self._result_callback is not None: | |
value = ctx.invoke(self._result_callback, value, **ctx.params) | |
return value | |
if not ctx.protected_args: | |
if self.invoke_without_command: | |
# No subcommand was invoked, so the result callback is | |
# invoked with the group return value for regular | |
# groups, or an empty list for chained groups. | |
with ctx: | |
rv = super().invoke(ctx) | |
return _process_result([] if self.chain else rv) | |
ctx.fail(_("Missing command.")) | |
# Fetch args back out | |
args = [*ctx.protected_args, *ctx.args] | |
ctx.args = [] | |
ctx.protected_args = [] | |
# If we're not in chain mode, we only allow the invocation of a | |
# single command but we also inform the current context about the | |
# name of the command to invoke. | |
if not self.chain: | |
# Make sure the context is entered so we do not clean up | |
# resources until the result processor has worked. | |
with ctx: | |
cmd_name, cmd, args = self.resolve_command(ctx, args) | |
assert cmd is not None | |
ctx.invoked_subcommand = cmd_name | |
super().invoke(ctx) | |
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) | |
with sub_ctx: | |
return _process_result(sub_ctx.command.invoke(sub_ctx)) | |
# In chain mode we create the contexts step by step, but after the | |
# base command has been invoked. Because at that point we do not | |
# know the subcommands yet, the invoked subcommand attribute is | |
# set to ``*`` to inform the command that subcommands are executed | |
# but nothing else. | |
with ctx: | |
ctx.invoked_subcommand = "*" if args else None | |
super().invoke(ctx) | |
# Otherwise we make every single context and invoke them in a | |
# chain. In that case the return value to the result processor | |
# is the list of all invoked subcommand's results. | |
contexts = [] | |
while args: | |
cmd_name, cmd, args = self.resolve_command(ctx, args) | |
assert cmd is not None | |
sub_ctx = cmd.make_context( | |
cmd_name, | |
args, | |
parent=ctx, | |
allow_extra_args=True, | |
allow_interspersed_args=False, | |
) | |
contexts.append(sub_ctx) | |
args, sub_ctx.args = sub_ctx.args, [] | |
rv = [] | |
for sub_ctx in contexts: | |
with sub_ctx: | |
rv.append(sub_ctx.command.invoke(sub_ctx)) | |
return _process_result(rv) | |
def resolve_command( | |
self, ctx: Context, args: t.List[str] | |
) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: | |
cmd_name = make_str(args[0]) | |
original_cmd_name = cmd_name | |
# Get the command | |
cmd = self.get_command(ctx, cmd_name) | |
# If we can't find the command but there is a normalization | |
# function available, we try with that one. | |
if cmd is None and ctx.token_normalize_func is not None: | |
cmd_name = ctx.token_normalize_func(cmd_name) | |
cmd = self.get_command(ctx, cmd_name) | |
# If we don't find the command we want to show an error message | |
# to the user that it was not provided. However, there is | |
# something else we should do: if the first argument looks like | |
# an option we want to kick off parsing again for arguments to | |
# resolve things like --help which now should go to the main | |
# place. | |
if cmd is None and not ctx.resilient_parsing: | |
if split_opt(cmd_name)[0]: | |
self.parse_args(ctx, ctx.args) | |
ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) | |
return cmd_name if cmd else None, cmd, args[1:] | |
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: | |
"""Given a context and a command name, this returns a | |
:class:`Command` object if it exists or returns `None`. | |
""" | |
raise NotImplementedError | |
def list_commands(self, ctx: Context) -> t.List[str]: | |
"""Returns a list of subcommand names in the order they should | |
appear. | |
""" | |
return [] | |
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: | |
"""Return a list of completions for the incomplete value. Looks | |
at the names of options, subcommands, and chained | |
multi-commands. | |
:param ctx: Invocation context for this command. | |
:param incomplete: Value being completed. May be empty. | |
.. versionadded:: 8.0 | |
""" | |
from click.shell_completion import CompletionItem | |
results = [ | |
CompletionItem(name, help=command.get_short_help_str()) | |
for name, command in _complete_visible_commands(ctx, incomplete) | |
] | |
results.extend(super().shell_complete(ctx, incomplete)) | |
return results | |
class Group(MultiCommand): | |
"""A group allows a command to have subcommands attached. This is | |
the most common way to implement nesting in Click. | |
:param name: The name of the group command. | |
:param commands: A dict mapping names to :class:`Command` objects. | |
Can also be a list of :class:`Command`, which will use | |
:attr:`Command.name` to create the dict. | |
:param attrs: Other command arguments described in | |
:class:`MultiCommand`, :class:`Command`, and | |
:class:`BaseCommand`. | |
.. versionchanged:: 8.0 | |
The ``commmands`` argument can be a list of command objects. | |
""" | |
#: If set, this is used by the group's :meth:`command` decorator | |
#: as the default :class:`Command` class. This is useful to make all | |
#: subcommands use a custom command class. | |
#: | |
#: .. versionadded:: 8.0 | |
command_class: t.Optional[t.Type[Command]] = None | |
#: If set, this is used by the group's :meth:`group` decorator | |
#: as the default :class:`Group` class. This is useful to make all | |
#: subgroups use a custom group class. | |
#: | |
#: If set to the special value :class:`type` (literally | |
#: ``group_class = type``), this group's class will be used as the | |
#: default class. This makes a custom group class continue to make | |
#: custom groups. | |
#: | |
#: .. versionadded:: 8.0 | |
group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None | |
# Literal[type] isn't valid, so use Type[type] | |
def __init__( | |
self, | |
name: t.Optional[str] = None, | |
commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None, | |
**attrs: t.Any, | |
) -> None: | |
super().__init__(name, **attrs) | |
if commands is None: | |
commands = {} | |
elif isinstance(commands, abc.Sequence): | |
commands = {c.name: c for c in commands if c.name is not None} | |
#: The registered subcommands by their exported names. | |
self.commands: t.Dict[str, Command] = commands | |
def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: | |
"""Registers another :class:`Command` with this group. If the name | |
is not provided, the name of the command is used. | |
""" | |
name = name or cmd.name | |
if name is None: | |
raise TypeError("Command has no name.") | |
_check_multicommand(self, name, cmd, register=True) | |
self.commands[name] = cmd | |
def command(self, __func: t.Callable[..., t.Any]) -> Command: | |
... | |
def command( | |
self, *args: t.Any, **kwargs: t.Any | |
) -> t.Callable[[t.Callable[..., t.Any]], Command]: | |
... | |
def command( | |
self, *args: t.Any, **kwargs: t.Any | |
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: | |
"""A shortcut decorator for declaring and attaching a command to | |
the group. This takes the same arguments as :func:`command` and | |
immediately registers the created command with this group by | |
calling :meth:`add_command`. | |
To customize the command class used, set the | |
:attr:`command_class` attribute. | |
.. versionchanged:: 8.1 | |
This decorator can be applied without parentheses. | |
.. versionchanged:: 8.0 | |
Added the :attr:`command_class` attribute. | |
""" | |
from .decorators import command | |
if self.command_class and kwargs.get("cls") is None: | |
kwargs["cls"] = self.command_class | |
func: t.Optional[t.Callable] = None | |
if args and callable(args[0]): | |
assert ( | |
len(args) == 1 and not kwargs | |
), "Use 'command(**kwargs)(callable)' to provide arguments." | |
(func,) = args | |
args = () | |
def decorator(f: t.Callable[..., t.Any]) -> Command: | |
cmd: Command = command(*args, **kwargs)(f) | |
self.add_command(cmd) | |
return cmd | |
if func is not None: | |
return decorator(func) | |
return decorator | |
def group(self, __func: t.Callable[..., t.Any]) -> "Group": | |
... | |
def group( | |
self, *args: t.Any, **kwargs: t.Any | |
) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: | |
... | |
def group( | |
self, *args: t.Any, **kwargs: t.Any | |
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: | |
"""A shortcut decorator for declaring and attaching a group to | |
the group. This takes the same arguments as :func:`group` and | |
immediately registers the created group with this group by | |
calling :meth:`add_command`. | |
To customize the group class used, set the :attr:`group_class` | |
attribute. | |
.. versionchanged:: 8.1 | |
This decorator can be applied without parentheses. | |
.. versionchanged:: 8.0 | |
Added the :attr:`group_class` attribute. | |
""" | |
from .decorators import group | |
func: t.Optional[t.Callable] = None | |
if args and callable(args[0]): | |
assert ( | |
len(args) == 1 and not kwargs | |
), "Use 'group(**kwargs)(callable)' to provide arguments." | |
(func,) = args | |
args = () | |
if self.group_class is not None and kwargs.get("cls") is None: | |
if self.group_class is type: | |
kwargs["cls"] = type(self) | |
else: | |
kwargs["cls"] = self.group_class | |
def decorator(f: t.Callable[..., t.Any]) -> "Group": | |
cmd: Group = group(*args, **kwargs)(f) | |
self.add_command(cmd) | |
return cmd | |
if func is not None: | |
return decorator(func) | |
return decorator | |
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: | |
return self.commands.get(cmd_name) | |
def list_commands(self, ctx: Context) -> t.List[str]: | |
return sorted(self.commands) | |
class CommandCollection(MultiCommand): | |
"""A command collection is a multi command that merges multiple multi | |
commands together into one. This is a straightforward implementation | |
that accepts a list of different multi commands as sources and | |
provides all the commands for each of them. | |
""" | |
def __init__( | |
self, | |
name: t.Optional[str] = None, | |
sources: t.Optional[t.List[MultiCommand]] = None, | |
**attrs: t.Any, | |
) -> None: | |
super().__init__(name, **attrs) | |
#: The list of registered multi commands. | |
self.sources: t.List[MultiCommand] = sources or [] | |
def add_source(self, multi_cmd: MultiCommand) -> None: | |
"""Adds a new multi command to the chain dispatcher.""" | |
self.sources.append(multi_cmd) | |
def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: | |
for source in self.sources: | |
rv = source.get_command(ctx, cmd_name) | |
if rv is not None: | |
if self.chain: | |
_check_multicommand(self, cmd_name, rv) | |
return rv | |
return None | |
def list_commands(self, ctx: Context) -> t.List[str]: | |
rv: t.Set[str] = set() | |
for source in self.sources: | |
rv.update(source.list_commands(ctx)) | |
return sorted(rv) | |
def _check_iter(value: t.Any) -> t.Iterator[t.Any]: | |
"""Check if the value is iterable but not a string. Raises a type | |
error, or return an iterator over the value. | |
""" | |
if isinstance(value, str): | |
raise TypeError | |
return iter(value) | |
class Parameter: | |
r"""A parameter to a command comes in two versions: they are either | |
:class:`Option`\s or :class:`Argument`\s. Other subclasses are currently | |
not supported by design as some of the internals for parsing are | |
intentionally not finalized. | |
Some settings are supported by both options and arguments. | |
:param param_decls: the parameter declarations for this option or | |
argument. This is a list of flags or argument | |
names. | |
:param type: the type that should be used. Either a :class:`ParamType` | |
or a Python type. The later is converted into the former | |
automatically if supported. | |
:param required: controls if this is optional or not. | |
:param default: the default value if omitted. This can also be a callable, | |
in which case it's invoked when the default is needed | |
without any arguments. | |
:param callback: A function to further process or validate the value | |
after type conversion. It is called as ``f(ctx, param, value)`` | |
and must return the value. It is called for all sources, | |
including prompts. | |
:param nargs: the number of arguments to match. If not ``1`` the return | |
value is a tuple instead of single value. The default for | |
nargs is ``1`` (except if the type is a tuple, then it's | |
the arity of the tuple). If ``nargs=-1``, all remaining | |
parameters are collected. | |
:param metavar: how the value is represented in the help page. | |
:param expose_value: if this is `True` then the value is passed onwards | |
to the command callback and stored on the context, | |
otherwise it's skipped. | |
:param is_eager: eager values are processed before non eager ones. This | |
should not be set for arguments or it will inverse the | |
order of processing. | |
:param envvar: a string or list of strings that are environment variables | |
that should be checked. | |
:param shell_complete: A function that returns custom shell | |
completions. Used instead of the param's type completion if | |
given. Takes ``ctx, param, incomplete`` and must return a list | |
of :class:`~click.shell_completion.CompletionItem` or a list of | |
strings. | |
.. versionchanged:: 8.0 | |
``process_value`` validates required parameters and bounded | |
``nargs``, and invokes the parameter callback before returning | |
the value. This allows the callback to validate prompts. | |
``full_process_value`` is removed. | |
.. versionchanged:: 8.0 | |
``autocompletion`` is renamed to ``shell_complete`` and has new | |
semantics described above. The old name is deprecated and will | |
be removed in 8.1, until then it will be wrapped to match the | |
new requirements. | |
.. versionchanged:: 8.0 | |
For ``multiple=True, nargs>1``, the default must be a list of | |
tuples. | |
.. versionchanged:: 8.0 | |
Setting a default is no longer required for ``nargs>1``, it will | |
default to ``None``. ``multiple=True`` or ``nargs=-1`` will | |
default to ``()``. | |
.. versionchanged:: 7.1 | |
Empty environment variables are ignored rather than taking the | |
empty string value. This makes it possible for scripts to clear | |
variables if they can't unset them. | |
.. versionchanged:: 2.0 | |
Changed signature for parameter callback to also be passed the | |
parameter. The old callback format will still work, but it will | |
raise a warning to give you a chance to migrate the code easier. | |
""" | |
param_type_name = "parameter" | |
def __init__( | |
self, | |
param_decls: t.Optional[t.Sequence[str]] = None, | |
type: t.Optional[t.Union[types.ParamType, t.Any]] = None, | |
required: bool = False, | |
default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, | |
callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, | |
nargs: t.Optional[int] = None, | |
multiple: bool = False, | |
metavar: t.Optional[str] = None, | |
expose_value: bool = True, | |
is_eager: bool = False, | |
envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, | |
shell_complete: t.Optional[ | |
t.Callable[ | |
[Context, "Parameter", str], | |
t.Union[t.List["CompletionItem"], t.List[str]], | |
] | |
] = None, | |
) -> None: | |
self.name, self.opts, self.secondary_opts = self._parse_decls( | |
param_decls or (), expose_value | |
) | |
self.type = types.convert_type(type, default) | |
# Default nargs to what the type tells us if we have that | |
# information available. | |
if nargs is None: | |
if self.type.is_composite: | |
nargs = self.type.arity | |
else: | |
nargs = 1 | |
self.required = required | |
self.callback = callback | |
self.nargs = nargs | |
self.multiple = multiple | |
self.expose_value = expose_value | |
self.default = default | |
self.is_eager = is_eager | |
self.metavar = metavar | |
self.envvar = envvar | |
self._custom_shell_complete = shell_complete | |
if __debug__: | |
if self.type.is_composite and nargs != self.type.arity: | |
raise ValueError( | |
f"'nargs' must be {self.type.arity} (or None) for" | |
f" type {self.type!r}, but it was {nargs}." | |
) | |
# Skip no default or callable default. | |
check_default = default if not callable(default) else None | |
if check_default is not None: | |
if multiple: | |
try: | |
# Only check the first value against nargs. | |
check_default = next(_check_iter(check_default), None) | |
except TypeError: | |
raise ValueError( | |
"'default' must be a list when 'multiple' is true." | |
) from None | |
# Can be None for multiple with empty default. | |
if nargs != 1 and check_default is not None: | |
try: | |
_check_iter(check_default) | |
except TypeError: | |
if multiple: | |
message = ( | |
"'default' must be a list of lists when 'multiple' is" | |
" true and 'nargs' != 1." | |
) | |
else: | |
message = "'default' must be a list when 'nargs' != 1." | |
raise ValueError(message) from None | |
if nargs > 1 and len(check_default) != nargs: | |
subject = "item length" if multiple else "length" | |
raise ValueError( | |
f"'default' {subject} must match nargs={nargs}." | |
) | |
def to_info_dict(self) -> t.Dict[str, t.Any]: | |
"""Gather information that could be useful for a tool generating | |
user-facing documentation. | |
Use :meth:`click.Context.to_info_dict` to traverse the entire | |
CLI structure. | |
.. versionadded:: 8.0 | |
""" | |
return { | |
"name": self.name, | |
"param_type_name": self.param_type_name, | |
"opts": self.opts, | |
"secondary_opts": self.secondary_opts, | |
"type": self.type.to_info_dict(), | |
"required": self.required, | |
"nargs": self.nargs, | |
"multiple": self.multiple, | |
"default": self.default, | |
"envvar": self.envvar, | |
} | |
def __repr__(self) -> str: | |
return f"<{self.__class__.__name__} {self.name}>" | |
def _parse_decls( | |
self, decls: t.Sequence[str], expose_value: bool | |
) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: | |
raise NotImplementedError() | |
def human_readable_name(self) -> str: | |
"""Returns the human readable name of this parameter. This is the | |
same as the name for options, but the metavar for arguments. | |
""" | |
return self.name # type: ignore | |
def make_metavar(self) -> str: | |
if self.metavar is not None: | |
return self.metavar | |
metavar = self.type.get_metavar(self) | |
if metavar is None: | |
metavar = self.type.name.upper() | |
if self.nargs != 1: | |
metavar += "..." | |
return metavar | |
def get_default( | |
self, ctx: Context, call: "te.Literal[True]" = True | |
) -> t.Optional[t.Any]: | |
... | |
def get_default( | |
self, ctx: Context, call: bool = ... | |
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: | |
... | |
def get_default( | |
self, ctx: Context, call: bool = True | |
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: | |
"""Get the default for the parameter. Tries | |
:meth:`Context.lookup_default` first, then the local default. | |
:param ctx: Current context. | |
:param call: If the default is a callable, call it. Disable to | |
return the callable instead. | |
.. versionchanged:: 8.0.2 | |
Type casting is no longer performed when getting a default. | |
.. versionchanged:: 8.0.1 | |
Type casting can fail in resilient parsing mode. Invalid | |
defaults will not prevent showing help text. | |
.. versionchanged:: 8.0 | |
Looks at ``ctx.default_map`` first. | |
.. versionchanged:: 8.0 | |
Added the ``call`` parameter. | |
""" | |
value = ctx.lookup_default(self.name, call=False) # type: ignore | |
if value is None: | |
value = self.default | |
if call and callable(value): | |
value = value() | |
return value | |
def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: | |
raise NotImplementedError() | |
def consume_value( | |
self, ctx: Context, opts: t.Mapping[str, t.Any] | |
) -> t.Tuple[t.Any, ParameterSource]: | |
value = opts.get(self.name) # type: ignore | |
source = ParameterSource.COMMANDLINE | |
if value is None: | |
value = self.value_from_envvar(ctx) | |
source = ParameterSource.ENVIRONMENT | |
if value is None: | |
value = ctx.lookup_default(self.name) # type: ignore | |
source = ParameterSource.DEFAULT_MAP | |
if value is None: | |
value = self.get_default(ctx) | |
source = ParameterSource.DEFAULT | |
return value, source | |
def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: | |
"""Convert and validate a value against the option's | |
:attr:`type`, :attr:`multiple`, and :attr:`nargs`. | |
""" | |
if value is None: | |
return () if self.multiple or self.nargs == -1 else None | |
def check_iter(value: t.Any) -> t.Iterator: | |
try: | |
return _check_iter(value) | |
except TypeError: | |
# This should only happen when passing in args manually, | |
# the parser should construct an iterable when parsing | |
# the command line. | |
raise BadParameter( | |
_("Value must be an iterable."), ctx=ctx, param=self | |
) from None | |
if self.nargs == 1 or self.type.is_composite: | |
convert: t.Callable[[t.Any], t.Any] = partial( | |
self.type, param=self, ctx=ctx | |
) | |
elif self.nargs == -1: | |
def convert(value: t.Any) -> t.Tuple: | |
return tuple(self.type(x, self, ctx) for x in check_iter(value)) | |
else: # nargs > 1 | |
def convert(value: t.Any) -> t.Tuple: | |
value = tuple(check_iter(value)) | |
if len(value) != self.nargs: | |
raise BadParameter( | |
ngettext( | |
"Takes {nargs} values but 1 was given.", | |
"Takes {nargs} values but {len} were given.", | |
len(value), | |
).format(nargs=self.nargs, len=len(value)), | |
ctx=ctx, | |
param=self, | |
) | |
return tuple(self.type(x, self, ctx) for x in value) | |
if self.multiple: | |
return tuple(convert(x) for x in check_iter(value)) | |
return convert(value) | |
def value_is_missing(self, value: t.Any) -> bool: | |
if value is None: | |
return True | |
if (self.nargs != 1 or self.multiple) and value == (): | |
return True | |
return False | |
def process_value(self, ctx: Context, value: t.Any) -> t.Any: | |
value = self.type_cast_value(ctx, value) | |
if self.required and self.value_is_missing(value): | |
raise MissingParameter(ctx=ctx, param=self) | |
if self.callback is not None: | |
value = self.callback(ctx, self, value) | |
return value | |
def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: | |
if self.envvar is None: | |
return None | |
if isinstance(self.envvar, str): | |
rv = os.environ.get(self.envvar) | |
if rv: | |
return rv | |
else: | |
for envvar in self.envvar: | |
rv = os.environ.get(envvar) | |
if rv: | |
return rv | |
return None | |
def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: | |
rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) | |
if rv is not None and self.nargs != 1: | |
rv = self.type.split_envvar_value(rv) | |
return rv | |
def handle_parse_result( | |
self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] | |
) -> t.Tuple[t.Any, t.List[str]]: | |
with augment_usage_errors(ctx, param=self): | |
value, source = self.consume_value(ctx, opts) | |
ctx.set_parameter_source(self.name, source) # type: ignore | |
try: | |
value = self.process_value(ctx, value) | |
except Exception: | |
if not ctx.resilient_parsing: | |
raise | |
value = None | |
if self.expose_value: | |
ctx.params[self.name] = value # type: ignore | |
return value, args | |
def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: | |
pass | |
def get_usage_pieces(self, ctx: Context) -> t.List[str]: | |
return [] | |
def get_error_hint(self, ctx: Context) -> str: | |
"""Get a stringified version of the param for use in error messages to | |
indicate which param caused the error. | |
""" | |
hint_list = self.opts or [self.human_readable_name] | |
return " / ".join(f"'{x}'" for x in hint_list) | |
def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: | |
"""Return a list of completions for the incomplete value. If a | |
``shell_complete`` function was given during init, it is used. | |
Otherwise, the :attr:`type` | |
:meth:`~click.types.ParamType.shell_complete` function is used. | |
:param ctx: Invocation context for this command. | |
:param incomplete: Value being completed. May be empty. | |
.. versionadded:: 8.0 | |
""" | |
if self._custom_shell_complete is not None: | |
results = self._custom_shell_complete(ctx, self, incomplete) | |
if results and isinstance(results[0], str): | |
from click.shell_completion import CompletionItem | |
results = [CompletionItem(c) for c in results] | |
return t.cast(t.List["CompletionItem"], results) | |
return self.type.shell_complete(ctx, self, incomplete) | |
class Option(Parameter): | |
"""Options are usually optional values on the command line and | |
have some extra features that arguments don't have. | |
All other parameters are passed onwards to the parameter constructor. | |
:param show_default: Show the default value for this option in its | |
help text. Values are not shown by default, unless | |
:attr:`Context.show_default` is ``True``. If this value is a | |
string, it shows that string in parentheses instead of the | |
actual value. This is particularly useful for dynamic options. | |
For single option boolean flags, the default remains hidden if | |
its value is ``False``. | |
:param show_envvar: Controls if an environment variable should be | |
shown on the help page. Normally, environment variables are not | |
shown. | |
:param prompt: If set to ``True`` or a non empty string then the | |
user will be prompted for input. If set to ``True`` the prompt | |
will be the option name capitalized. | |
:param confirmation_prompt: Prompt a second time to confirm the | |
value if it was prompted for. Can be set to a string instead of | |
``True`` to customize the message. | |
:param prompt_required: If set to ``False``, the user will be | |
prompted for input only when the option was specified as a flag | |
without a value. | |
:param hide_input: If this is ``True`` then the input on the prompt | |
will be hidden from the user. This is useful for password input. | |
:param is_flag: forces this option to act as a flag. The default is | |
auto detection. | |
:param flag_value: which value should be used for this flag if it's | |
enabled. This is set to a boolean automatically if | |
the option string contains a slash to mark two options. | |
:param multiple: if this is set to `True` then the argument is accepted | |
multiple times and recorded. This is similar to ``nargs`` | |
in how it works but supports arbitrary number of | |
arguments. | |
:param count: this flag makes an option increment an integer. | |
:param allow_from_autoenv: if this is enabled then the value of this | |
parameter will be pulled from an environment | |
variable in case a prefix is defined on the | |
context. | |
:param help: the help string. | |
:param hidden: hide this option from help outputs. | |
.. versionchanged:: 8.1.0 | |
Help text indentation is cleaned here instead of only in the | |
``@option`` decorator. | |
.. versionchanged:: 8.1.0 | |
The ``show_default`` parameter overrides | |
``Context.show_default``. | |
.. versionchanged:: 8.1.0 | |
The default of a single option boolean flag is not shown if the | |
default value is ``False``. | |
.. versionchanged:: 8.0.1 | |
``type`` is detected from ``flag_value`` if given. | |
""" | |
param_type_name = "option" | |
def __init__( | |
self, | |
param_decls: t.Optional[t.Sequence[str]] = None, | |
show_default: t.Union[bool, str, None] = None, | |
prompt: t.Union[bool, str] = False, | |
confirmation_prompt: t.Union[bool, str] = False, | |
prompt_required: bool = True, | |
hide_input: bool = False, | |
is_flag: t.Optional[bool] = None, | |
flag_value: t.Optional[t.Any] = None, | |
multiple: bool = False, | |
count: bool = False, | |
allow_from_autoenv: bool = True, | |
type: t.Optional[t.Union[types.ParamType, t.Any]] = None, | |
help: t.Optional[str] = None, | |
hidden: bool = False, | |
show_choices: bool = True, | |
show_envvar: bool = False, | |
**attrs: t.Any, | |
) -> None: | |
if help: | |
help = inspect.cleandoc(help) | |
default_is_missing = "default" not in attrs | |
super().__init__(param_decls, type=type, multiple=multiple, **attrs) | |
if prompt is True: | |
if self.name is None: | |
raise TypeError("'name' is required with 'prompt=True'.") | |
prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() | |
elif prompt is False: | |
prompt_text = None | |
else: | |
prompt_text = prompt | |
self.prompt = prompt_text | |
self.confirmation_prompt = confirmation_prompt | |
self.prompt_required = prompt_required | |
self.hide_input = hide_input | |
self.hidden = hidden | |
# If prompt is enabled but not required, then the option can be | |
# used as a flag to indicate using prompt or flag_value. | |
self._flag_needs_value = self.prompt is not None and not self.prompt_required | |
if is_flag is None: | |
if flag_value is not None: | |
# Implicitly a flag because flag_value was set. | |
is_flag = True | |
elif self._flag_needs_value: | |
# Not a flag, but when used as a flag it shows a prompt. | |
is_flag = False | |
else: | |
# Implicitly a flag because flag options were given. | |
is_flag = bool(self.secondary_opts) | |
elif is_flag is False and not self._flag_needs_value: | |
# Not a flag, and prompt is not enabled, can be used as a | |
# flag if flag_value is set. | |
self._flag_needs_value = flag_value is not None | |
if is_flag and default_is_missing and not self.required: | |
self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False | |
if flag_value is None: | |
flag_value = not self.default | |
if is_flag and type is None: | |
# Re-guess the type from the flag value instead of the | |
# default. | |
self.type = types.convert_type(None, flag_value) | |
self.is_flag: bool = is_flag | |
self.is_bool_flag = is_flag and isinstance(self.type, types.BoolParamType) | |
self.flag_value: t.Any = flag_value | |
# Counting | |
self.count = count | |
if count: | |
if type is None: | |
self.type = types.IntRange(min=0) | |
if default_is_missing: | |
self.default = 0 | |
self.allow_from_autoenv = allow_from_autoenv | |
self.help = help | |
self.show_default = show_default | |
self.show_choices = show_choices | |
self.show_envvar = show_envvar | |
if __debug__: | |
if self.nargs == -1: | |
raise TypeError("nargs=-1 is not supported for options.") | |
if self.prompt and self.is_flag and not self.is_bool_flag: | |
raise TypeError("'prompt' is not valid for non-boolean flag.") | |
if not self.is_bool_flag and self.secondary_opts: | |
raise TypeError("Secondary flag is not valid for non-boolean flag.") | |
if self.is_bool_flag and self.hide_input and self.prompt is not None: | |
raise TypeError( | |
"'prompt' with 'hide_input' is not valid for boolean flag." | |
) | |
if self.count: | |
if self.multiple: | |
raise TypeError("'count' is not valid with 'multiple'.") | |
if self.is_flag: | |
raise TypeError("'count' is not valid with 'is_flag'.") | |
if self.multiple and self.is_flag: | |
raise TypeError("'multiple' is not valid with 'is_flag', use 'count'.") | |
def to_info_dict(self) -> t.Dict[str, t.Any]: | |
info_dict = super().to_info_dict() | |
info_dict.update( | |
help=self.help, | |
prompt=self.prompt, | |
is_flag=self.is_flag, | |
flag_value=self.flag_value, | |
count=self.count, | |
hidden=self.hidden, | |
) | |
return info_dict | |
def _parse_decls( | |
self, decls: t.Sequence[str], expose_value: bool | |
) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: | |
opts = [] | |
secondary_opts = [] | |
name = None | |
possible_names = [] | |
for decl in decls: | |
if decl.isidentifier(): | |
if name is not None: | |
raise TypeError(f"Name '{name}' defined twice") | |
name = decl | |
else: | |
split_char = ";" if decl[:1] == "/" else "/" | |
if split_char in decl: | |
first, second = decl.split(split_char, 1) | |
first = first.rstrip() | |
if first: | |
possible_names.append(split_opt(first)) | |
opts.append(first) | |
second = second.lstrip() | |
if second: | |
secondary_opts.append(second.lstrip()) | |
if first == second: | |
raise ValueError( | |
f"Boolean option {decl!r} cannot use the" | |
" same flag for true/false." | |
) | |
else: | |
possible_names.append(split_opt(decl)) | |
opts.append(decl) | |
if name is None and possible_names: | |
possible_names.sort(key=lambda x: -len(x[0])) # group long options first | |
name = possible_names[0][1].replace("-", "_").lower() | |
if not name.isidentifier(): | |
name = None | |
if name is None: | |
if not expose_value: | |
return None, opts, secondary_opts | |
raise TypeError("Could not determine name for option") | |
if not opts and not secondary_opts: | |
raise TypeError( | |
f"No options defined but a name was passed ({name})." | |
" Did you mean to declare an argument instead? Did" | |
f" you mean to pass '--{name}'?" | |
) | |
return name, opts, secondary_opts | |
def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: | |
if self.multiple: | |
action = "append" | |
elif self.count: | |
action = "count" | |
else: | |
action = "store" | |
if self.is_flag: | |
action = f"{action}_const" | |
if self.is_bool_flag and self.secondary_opts: | |
parser.add_option( | |
obj=self, opts=self.opts, dest=self.name, action=action, const=True | |
) | |
parser.add_option( | |
obj=self, | |
opts=self.secondary_opts, | |
dest=self.name, | |
action=action, | |
const=False, | |
) | |
else: | |
parser.add_option( | |
obj=self, | |
opts=self.opts, | |
dest=self.name, | |
action=action, | |
const=self.flag_value, | |
) | |
else: | |
parser.add_option( | |
obj=self, | |
opts=self.opts, | |
dest=self.name, | |
action=action, | |
nargs=self.nargs, | |
) | |
def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: | |
if self.hidden: | |
return None | |
any_prefix_is_slash = False | |
def _write_opts(opts: t.Sequence[str]) -> str: | |
nonlocal any_prefix_is_slash | |
rv, any_slashes = join_options(opts) | |
if any_slashes: | |
any_prefix_is_slash = True | |
if not self.is_flag and not self.count: | |
rv += f" {self.make_metavar()}" | |
return rv | |
rv = [_write_opts(self.opts)] | |
if self.secondary_opts: | |
rv.append(_write_opts(self.secondary_opts)) | |
help = self.help or "" | |
extra = [] | |
if self.show_envvar: | |
envvar = self.envvar | |
if envvar is None: | |
if ( | |
self.allow_from_autoenv | |
and ctx.auto_envvar_prefix is not None | |
and self.name is not None | |
): | |
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" | |
if envvar is not None: | |
var_str = ( | |
envvar | |
if isinstance(envvar, str) | |
else ", ".join(str(d) for d in envvar) | |
) | |
extra.append(_("env var: {var}").format(var=var_str)) | |
# Temporarily enable resilient parsing to avoid type casting | |
# failing for the default. Might be possible to extend this to | |
# help formatting in general. | |
resilient = ctx.resilient_parsing | |
ctx.resilient_parsing = True | |
try: | |
default_value = self.get_default(ctx, call=False) | |
finally: | |
ctx.resilient_parsing = resilient | |
show_default = False | |
show_default_is_str = False | |
if self.show_default is not None: | |
if isinstance(self.show_default, str): | |
show_default_is_str = show_default = True | |
else: | |
show_default = self.show_default | |
elif ctx.show_default is not None: | |
show_default = ctx.show_default | |
if show_default_is_str or (show_default and (default_value is not None)): | |
if show_default_is_str: | |
default_string = f"({self.show_default})" | |
elif isinstance(default_value, (list, tuple)): | |
default_string = ", ".join(str(d) for d in default_value) | |
elif inspect.isfunction(default_value): | |
default_string = _("(dynamic)") | |
elif self.is_bool_flag and self.secondary_opts: | |
# For boolean flags that have distinct True/False opts, | |
# use the opt without prefix instead of the value. | |
default_string = split_opt( | |
(self.opts if self.default else self.secondary_opts)[0] | |
)[1] | |
elif self.is_bool_flag and not self.secondary_opts and not default_value: | |
default_string = "" | |
else: | |
default_string = str(default_value) | |
if default_string: | |
extra.append(_("default: {default}").format(default=default_string)) | |
if ( | |
isinstance(self.type, types._NumberRangeBase) | |
# skip count with default range type | |
and not (self.count and self.type.min == 0 and self.type.max is None) | |
): | |
range_str = self.type._describe_range() | |
if range_str: | |
extra.append(range_str) | |
if self.required: | |
extra.append(_("required")) | |
if extra: | |
extra_str = "; ".join(extra) | |
help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" | |
return ("; " if any_prefix_is_slash else " / ").join(rv), help | |
def get_default( | |
self, ctx: Context, call: "te.Literal[True]" = True | |
) -> t.Optional[t.Any]: | |
... | |
def get_default( | |
self, ctx: Context, call: bool = ... | |
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: | |
... | |
def get_default( | |
self, ctx: Context, call: bool = True | |
) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: | |
# If we're a non boolean flag our default is more complex because | |
# we need to look at all flags in the same group to figure out | |
# if we're the default one in which case we return the flag | |
# value as default. | |
if self.is_flag and not self.is_bool_flag: | |
for param in ctx.command.params: | |
if param.name == self.name and param.default: | |
return param.flag_value # type: ignore | |
return None | |
return super().get_default(ctx, call=call) | |
def prompt_for_value(self, ctx: Context) -> t.Any: | |
"""This is an alternative flow that can be activated in the full | |
value processing if a value does not exist. It will prompt the | |
user until a valid value exists and then returns the processed | |
value as result. | |
""" | |
assert self.prompt is not None | |
# Calculate the default before prompting anything to be stable. | |
default = self.get_default(ctx) | |
# If this is a prompt for a flag we need to handle this | |
# differently. | |
if self.is_bool_flag: | |
return confirm(self.prompt, default) | |
return prompt( | |
self.prompt, | |
default=default, | |
type=self.type, | |
hide_input=self.hide_input, | |
show_choices=self.show_choices, | |
confirmation_prompt=self.confirmation_prompt, | |
value_proc=lambda x: self.process_value(ctx, x), | |
) | |
def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: | |
rv = super().resolve_envvar_value(ctx) | |
if rv is not None: | |
return rv | |
if ( | |
self.allow_from_autoenv | |
and ctx.auto_envvar_prefix is not None | |
and self.name is not None | |
): | |
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" | |
rv = os.environ.get(envvar) | |
if rv: | |
return rv | |
return None | |
def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: | |
rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) | |
if rv is None: | |
return None | |
value_depth = (self.nargs != 1) + bool(self.multiple) | |
if value_depth > 0: | |
rv = self.type.split_envvar_value(rv) | |
if self.multiple and self.nargs != 1: | |
rv = batch(rv, self.nargs) | |
return rv | |
def consume_value( | |
self, ctx: Context, opts: t.Mapping[str, "Parameter"] | |
) -> t.Tuple[t.Any, ParameterSource]: | |
value, source = super().consume_value(ctx, opts) | |
# The parser will emit a sentinel value if the option can be | |
# given as a flag without a value. This is different from None | |
# to distinguish from the flag not being given at all. | |
if value is _flag_needs_value: | |
if self.prompt is not None and not ctx.resilient_parsing: | |
value = self.prompt_for_value(ctx) | |
source = ParameterSource.PROMPT | |
else: | |
value = self.flag_value | |
source = ParameterSource.COMMANDLINE | |
elif ( | |
self.multiple | |
and value is not None | |
and any(v is _flag_needs_value for v in value) | |
): | |
value = [self.flag_value if v is _flag_needs_value else v for v in value] | |
source = ParameterSource.COMMANDLINE | |
# The value wasn't set, or used the param's default, prompt if | |
# prompting is enabled. | |
elif ( | |
source in {None, ParameterSource.DEFAULT} | |
and self.prompt is not None | |
and (self.required or self.prompt_required) | |
and not ctx.resilient_parsing | |
): | |
value = self.prompt_for_value(ctx) | |
source = ParameterSource.PROMPT | |
return value, source | |
class Argument(Parameter): | |
"""Arguments are positional parameters to a command. They generally | |
provide fewer features than options but can have infinite ``nargs`` | |
and are required by default. | |
All parameters are passed onwards to the parameter constructor. | |
""" | |
param_type_name = "argument" | |
def __init__( | |
self, | |
param_decls: t.Sequence[str], | |
required: t.Optional[bool] = None, | |
**attrs: t.Any, | |
) -> None: | |
if required is None: | |
if attrs.get("default") is not None: | |
required = False | |
else: | |
required = attrs.get("nargs", 1) > 0 | |
if "multiple" in attrs: | |
raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") | |
super().__init__(param_decls, required=required, **attrs) | |
if __debug__: | |
if self.default is not None and self.nargs == -1: | |
raise TypeError("'default' is not supported for nargs=-1.") | |
def human_readable_name(self) -> str: | |
if self.metavar is not None: | |
return self.metavar | |
return self.name.upper() # type: ignore | |
def make_metavar(self) -> str: | |
if self.metavar is not None: | |
return self.metavar | |
var = self.type.get_metavar(self) | |
if not var: | |
var = self.name.upper() # type: ignore | |
if not self.required: | |
var = f"[{var}]" | |
if self.nargs != 1: | |
var += "..." | |
return var | |
def _parse_decls( | |
self, decls: t.Sequence[str], expose_value: bool | |
) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: | |
if not decls: | |
if not expose_value: | |
return None, [], [] | |
raise TypeError("Could not determine name for argument") | |
if len(decls) == 1: | |
name = arg = decls[0] | |
name = name.replace("-", "_").lower() | |
else: | |
raise TypeError( | |
"Arguments take exactly one parameter declaration, got" | |
f" {len(decls)}." | |
) | |
return name, [arg], [] | |
def get_usage_pieces(self, ctx: Context) -> t.List[str]: | |
return [self.make_metavar()] | |
def get_error_hint(self, ctx: Context) -> str: | |
return f"'{self.make_metavar()}'" | |
def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: | |
parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) | |