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 update_wrapper
from gettext
import gettext
as _
from gettext
import ngettext
from itertools
import repeat
from types
import TracebackType
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)))
@contextmanager
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.MutableMapping[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.MutableMapping[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: t.Optional[t.Type[BaseException]],
exc_value: t.Optional[BaseException],
tb: t.Optional[TracebackType],
) ->
None:
self._depth -= 1
if self._depth == 0:
self.close()
pop_context()
@contextmanager
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
@property
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()
@property
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
@t.overload
def lookup_default(
self, name: str, call:
"te.Literal[True]" =
True
) -> t.Optional[t.Any]:
...
@t.overload
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)
@t.overload
def invoke(
__self,
# noqa: B902
__callback:
"t.Callable[..., V]",
*args: t.Any,
**kwargs: t.Any,
) -> V:
...
@t.overload
def invoke(
__self,
# noqa: B902
__callback:
"Command",
*args: t.Any,
**kwargs: t.Any,
) -> t.Any:
...
def invoke(
__self,
# noqa: B902
__callback: t.Union[
"Command",
"t.Callable[..., V]"],
*args: t.Any,
**kwargs: t.Any,
) -> t.Union[t.Any, V]:
"""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 = t.cast(
"t.Callable[..., V]", 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.MutableMapping[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.MutableMapping[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
'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 invocable 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
@t.overload
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":
...
@t.overload
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
``
"__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)
as e:
echo(file=sys.stderr)
raise Abort()
from e
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.MutableMapping[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``.
.. versionchanged:: 8.2.0
Dots (``.``)
in ``prog_name`` are replaced
with underscores (``_``).
"""
if complete_var
is None:
complete_name = prog_name.replace(
"-",
"_").replace(
".",
"_")
complete_var = f
"_{complete_name}_COMPLETE".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.MutableMapping[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."""
if self.help
is not None:
# truncate the help text to the first form feed
text = inspect.cleandoc(self.help).partition(
"\f")[0]
else:
text =
""
if self.deprecated:
text = _(
"(Deprecated) {text}").format(text=text)
if text:
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.
:param attrs: Other command arguments described
in :
class:`Command`.
"""
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)
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
--> --------------------
--> maximum size reached
--> --------------------