| | from __future__ import annotations |
| |
|
| | import collections.abc as cabc |
| | import enum |
| | import errno |
| | import inspect |
| | import os |
| | import sys |
| | import typing as t |
| | from collections import abc |
| | from collections import Counter |
| | from contextlib import AbstractContextManager |
| | 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 ._utils import FLAG_NEEDS_VALUE |
| | from ._utils import UNSET |
| | from .exceptions import Abort |
| | from .exceptions import BadParameter |
| | from .exceptions import ClickException |
| | from .exceptions import Exit |
| | from .exceptions import MissingParameter |
| | from .exceptions import NoArgsIsHelpError |
| | 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 _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: |
| | 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 |
| | ) -> cabc.Iterator[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(Group, 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_nested_chain( |
| | base_command: Group, cmd_name: str, cmd: Command, register: bool = False |
| | ) -> None: |
| | if not base_command.chain or not isinstance(cmd, Group): |
| | return |
| |
|
| | if register: |
| | message = ( |
| | f"It is not possible to add the group {cmd_name!r} to another" |
| | f" group {base_command.name!r} that is in chain mode." |
| | ) |
| | else: |
| | message = ( |
| | f"Found the group {cmd_name!r} as subcommand to another group " |
| | f" {base_command.name!r} that is in chain mode. This is not supported." |
| | ) |
| |
|
| | raise RuntimeError(message) |
| |
|
| |
|
| | def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: |
| | return list(zip(*repeat(iter(iterable), batch_size), strict=False)) |
| |
|
| |
|
| | @contextmanager |
| | def augment_usage_errors( |
| | ctx: Context, param: Parameter | None = None |
| | ) -> cabc.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: cabc.Sequence[Parameter], |
| | declaration_order: cabc.Sequence[Parameter], |
| | ) -> list[Parameter]: |
| | """Returns all declared parameters in the order they should be processed. |
| | |
| | The declared parameters are re-shuffled depending on the order in which |
| | they were invoked, as well as the eagerness of each parameters. |
| | |
| | The invocation order takes precedence over the declaration order. I.e. the |
| | order in which the user provided them to the CLI is respected. |
| | |
| | This behavior and its effect on callback evaluation is detailed at: |
| | https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order |
| | """ |
| |
|
| | def sort_key(item: Parameter) -> 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.2 |
| | The ``protected_args`` attribute is deprecated and will be removed in |
| | Click 9.0. ``args`` will contain remaining unparsed tokens. |
| | |
| | .. 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. |
| | """ |
| |
|
| | |
| | |
| | |
| | formatter_class: type[HelpFormatter] = HelpFormatter |
| |
|
| | def __init__( |
| | self, |
| | command: Command, |
| | parent: Context | None = None, |
| | info_name: str | None = None, |
| | obj: t.Any | None = None, |
| | auto_envvar_prefix: str | None = None, |
| | default_map: cabc.MutableMapping[str, t.Any] | None = None, |
| | terminal_width: int | None = None, |
| | max_content_width: int | None = None, |
| | resilient_parsing: bool = False, |
| | allow_extra_args: bool | None = None, |
| | allow_interspersed_args: bool | None = None, |
| | ignore_unknown_options: bool | None = None, |
| | help_option_names: list[str] | None = None, |
| | token_normalize_func: t.Callable[[str], str] | None = None, |
| | color: bool | None = None, |
| | show_default: bool | None = None, |
| | ) -> None: |
| | |
| | self.parent = parent |
| | |
| | self.command = command |
| | |
| | self.info_name = info_name |
| | |
| | |
| | self.params: dict[str, t.Any] = {} |
| | |
| | self.args: list[str] = [] |
| | |
| | |
| | |
| | |
| | self._protected_args: list[str] = [] |
| | |
| | self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set() |
| |
|
| | if obj is None and parent is not None: |
| | obj = parent.obj |
| |
|
| | |
| | self.obj: t.Any = obj |
| | self._meta: dict[str, t.Any] = getattr(parent, "meta", {}) |
| |
|
| | |
| | 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: cabc.MutableMapping[str, t.Any] | None = default_map |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | self.invoked_subcommand: str | None = None |
| |
|
| | if terminal_width is None and parent is not None: |
| | terminal_width = parent.terminal_width |
| |
|
| | |
| | self.terminal_width: int | None = terminal_width |
| |
|
| | if max_content_width is None and parent is not None: |
| | max_content_width = parent.max_content_width |
| |
|
| | |
| | |
| | self.max_content_width: int | None = max_content_width |
| |
|
| | if allow_extra_args is None: |
| | allow_extra_args = command.allow_extra_args |
| |
|
| | |
| | |
| | |
| | |
| | self.allow_extra_args = allow_extra_args |
| |
|
| | if allow_interspersed_args is None: |
| | allow_interspersed_args = command.allow_interspersed_args |
| |
|
| | |
| | |
| | |
| | |
| | self.allow_interspersed_args: bool = allow_interspersed_args |
| |
|
| | if ignore_unknown_options is None: |
| | ignore_unknown_options = command.ignore_unknown_options |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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"] |
| |
|
| | |
| | self.help_option_names: list[str] = help_option_names |
| |
|
| | if token_normalize_func is None and parent is not None: |
| | token_normalize_func = parent.token_normalize_func |
| |
|
| | |
| | |
| | self.token_normalize_func: t.Callable[[str], str] | None = token_normalize_func |
| |
|
| | |
| | |
| | |
| | self.resilient_parsing: bool = resilient_parsing |
| |
|
| | |
| | |
| | |
| | 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: str | None = auto_envvar_prefix |
| |
|
| | if color is None and parent is not None: |
| | color = parent.color |
| |
|
| | |
| | self.color: bool | None = color |
| |
|
| | if show_default is None and parent is not None: |
| | show_default = parent.show_default |
| |
|
| | |
| | self.show_default: bool | None = show_default |
| |
|
| | self._close_callbacks: list[t.Callable[[], t.Any]] = [] |
| | self._depth = 0 |
| | self._parameter_source: dict[str, ParameterSource] = {} |
| | self._exit_stack = ExitStack() |
| |
|
| | @property |
| | def protected_args(self) -> list[str]: |
| | import warnings |
| |
|
| | warnings.warn( |
| | "'protected_args' is deprecated and will be removed in Click 9.0." |
| | " 'args' will contain remaining unparsed tokens.", |
| | DeprecationWarning, |
| | stacklevel=2, |
| | ) |
| | return self._protected_args |
| |
|
| | def to_info_dict(self) -> 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: type[BaseException] | None, |
| | exc_value: BaseException | None, |
| | tb: TracebackType | None, |
| | ) -> bool | None: |
| | self._depth -= 1 |
| | exit_result: bool | None = None |
| | if self._depth == 0: |
| | exit_result = self._close_with_exception_info(exc_type, exc_value, tb) |
| | pop_context() |
| |
|
| | return exit_result |
| |
|
| | @contextmanager |
| | def scope(self, cleanup: bool = True) -> cabc.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) -> 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: AbstractContextManager[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._close_with_exception_info(None, None, None) |
| |
|
| | def _close_with_exception_info( |
| | self, |
| | exc_type: type[BaseException] | None, |
| | exc_value: BaseException | None, |
| | tb: TracebackType | None, |
| | ) -> bool | None: |
| | """Unwind the exit stack by calling its :meth:`__exit__` providing the exception |
| | information to allow for exception handling by the various resources registered |
| | using :meth;`with_resource` |
| | |
| | :return: Whatever ``exit_stack.__exit__()`` returns. |
| | """ |
| | exit_result = self._exit_stack.__exit__(exc_type, exc_value, tb) |
| | |
| | self._exit_stack = ExitStack() |
| |
|
| | return exit_result |
| |
|
| | @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: type[V]) -> V | None: |
| | """Finds the closest object of a given type.""" |
| | node: Context | None = 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: 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: t.Literal[True] = True |
| | ) -> t.Any | None: ... |
| |
|
| | @t.overload |
| | def lookup_default( |
| | self, name: str, call: t.Literal[False] = ... |
| | ) -> t.Any | t.Callable[[], t.Any] | None: ... |
| |
|
| | def lookup_default(self, name: str, call: bool = True) -> t.Any | None: |
| | """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, UNSET) |
| |
|
| | if call and callable(value): |
| | return value() |
| |
|
| | return value |
| |
|
| | return UNSET |
| |
|
| | def fail(self, message: str) -> t.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) -> t.NoReturn: |
| | """Aborts the script.""" |
| | raise Abort() |
| |
|
| | def exit(self, code: int = 0) -> t.NoReturn: |
| | """Exits the application with a given exit code. |
| | |
| | .. versionchanged:: 8.2 |
| | Callbacks and context managers registered with :meth:`call_on_close` |
| | and :meth:`with_resource` are closed before exiting. |
| | """ |
| | self.close() |
| | 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, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any |
| | ) -> V: ... |
| |
|
| | @t.overload |
| | def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... |
| |
|
| | def invoke( |
| | self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any |
| | ) -> 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. |
| | |
| | .. versionchanged:: 8.0 |
| | All ``kwargs`` are tracked in :attr:`params` so they will be |
| | passed if :meth:`forward` is called at multiple levels. |
| | |
| | .. versionchanged:: 3.2 |
| | A new context is created, and missing arguments use default values. |
| | """ |
| | 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: |
| | default_value = param.get_default(ctx) |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if default_value is UNSET: |
| | default_value = None |
| | kwargs[param.name] = param.type_cast_value( |
| | ctx, default_value |
| | ) |
| |
|
| | |
| | |
| | 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) -> 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. |
| | """ |
| | |
| | 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) -> ParameterSource | None: |
| | """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 Command: |
| | """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: If ``True`` or non-empty string, issues a message |
| | indicating that the command is deprecated and highlights |
| | its deprecation in --help. The message can be customized |
| | by using a string as the value. |
| | |
| | .. versionchanged:: 8.2 |
| | This is the base class for all commands, not ``BaseCommand``. |
| | ``deprecated`` can be set to a string as well to customize the |
| | deprecation message. |
| | |
| | .. 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. |
| | """ |
| |
|
| | |
| | |
| | |
| | context_class: type[Context] = Context |
| |
|
| | |
| | allow_extra_args = False |
| |
|
| | |
| | allow_interspersed_args = True |
| |
|
| | |
| | ignore_unknown_options = False |
| |
|
| | def __init__( |
| | self, |
| | name: str | None, |
| | context_settings: cabc.MutableMapping[str, t.Any] | None = None, |
| | callback: t.Callable[..., t.Any] | None = None, |
| | params: list[Parameter] | None = None, |
| | help: str | None = None, |
| | epilog: str | None = None, |
| | short_help: str | None = None, |
| | options_metavar: str | None = "[OPTIONS]", |
| | add_help_option: bool = True, |
| | no_args_is_help: bool = False, |
| | hidden: bool = False, |
| | deprecated: bool | str = False, |
| | ) -> None: |
| | |
| | |
| | |
| | |
| | self.name = name |
| |
|
| | if context_settings is None: |
| | context_settings = {} |
| |
|
| | |
| | self.context_settings: cabc.MutableMapping[str, t.Any] = context_settings |
| |
|
| | |
| | |
| | self.callback = callback |
| | |
| | |
| | |
| | self.params: 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._help_option = None |
| | self.no_args_is_help = no_args_is_help |
| | self.hidden = hidden |
| | self.deprecated = deprecated |
| |
|
| | def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: |
| | return { |
| | "name": self.name, |
| | "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, |
| | } |
| |
|
| | def __repr__(self) -> str: |
| | return f"<{self.__class__.__name__} {self.name}>" |
| |
|
| | 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) -> list[Parameter]: |
| | params = self.params |
| | help_option = self.get_help_option(ctx) |
| |
|
| | if help_option is not None: |
| | params = [*params, help_option] |
| |
|
| | if __debug__: |
| | import warnings |
| |
|
| | opts = [opt for param in params for opt in param.opts] |
| | opts_counter = Counter(opts) |
| | duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) |
| |
|
| | for duplicate_opt in duplicate_opts: |
| | warnings.warn( |
| | ( |
| | f"The parameter {duplicate_opt} is used more than once. " |
| | "Remove its duplicate as parameters should be unique." |
| | ), |
| | stacklevel=3, |
| | ) |
| |
|
| | return params |
| |
|
| | 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) -> 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) -> 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) -> Option | None: |
| | """Returns the help option object. |
| | |
| | Skipped if :attr:`add_help_option` is ``False``. |
| | |
| | .. versionchanged:: 8.1.8 |
| | The help option is now cached to avoid creating it multiple times. |
| | """ |
| | help_option_names = self.get_help_option_names(ctx) |
| |
|
| | if not help_option_names or not self.add_help_option: |
| | return None |
| |
|
| | |
| | |
| | |
| | |
| | if self._help_option is None: |
| | |
| | from .decorators import help_option |
| |
|
| | |
| | help_option(*help_option_names)(self) |
| | self._help_option = self.params.pop() |
| |
|
| | return self._help_option |
| |
|
| | 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: |
| | deprecated_message = ( |
| | f"(DEPRECATED: {self.deprecated})" |
| | if isinstance(self.deprecated, str) |
| | else "(DEPRECATED)" |
| | ) |
| | text = _("{text} {deprecated_message}").format( |
| | text=text, deprecated_message=deprecated_message |
| | ) |
| |
|
| | 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: |
| | |
| | text = inspect.cleandoc(self.help).partition("\f")[0] |
| | else: |
| | text = "" |
| |
|
| | if self.deprecated: |
| | deprecated_message = ( |
| | f"(DEPRECATED: {self.deprecated})" |
| | if isinstance(self.deprecated, str) |
| | else "(DEPRECATED)" |
| | ) |
| | text = _("{text} {deprecated_message}").format( |
| | text=text, deprecated_message=deprecated_message |
| | ) |
| |
|
| | 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 make_context( |
| | self, |
| | info_name: str | None, |
| | args: list[str], |
| | parent: Context | None = 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) |
| |
|
| | with ctx.scope(cleanup=False): |
| | self.parse_args(ctx, args) |
| | return ctx |
| |
|
| | def parse_args(self, ctx: Context, args: list[str]) -> list[str]: |
| | if not args and self.no_args_is_help and not ctx.resilient_parsing: |
| | raise NoArgsIsHelpError(ctx) |
| |
|
| | 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)): |
| | _, args = param.handle_parse_result(ctx, opts, args) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | for name, value in ctx.params.items(): |
| | if value is UNSET: |
| | ctx.params[name] = None |
| |
|
| | 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: |
| | extra_message = ( |
| | f" {self.deprecated}" if isinstance(self.deprecated, str) else "" |
| | ) |
| | message = _( |
| | "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" |
| | ).format(name=self.name, extra_message=extra_message) |
| | 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) -> list[CompletionItem]: |
| | """Return a list of completions for the incomplete value. Looks |
| | at the names of options and chained multi-commands. |
| | |
| | Any command could be part of a chained multi-command, so sibling |
| | commands are valid at any point during command completion. |
| | |
| | :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: 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) |
| | is ParameterSource.COMMANDLINE |
| | ) |
| | ): |
| | continue |
| |
|
| | results.extend( |
| | CompletionItem(name, help=param.help) |
| | for name in [*param.opts, *param.secondary_opts] |
| | if name.startswith(incomplete) |
| | ) |
| |
|
| | while ctx.parent is not None: |
| | ctx = ctx.parent |
| |
|
| | if isinstance(ctx.command, Group) 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: cabc.Sequence[str] | None = None, |
| | prog_name: str | None = None, |
| | complete_var: str | None = None, |
| | standalone_mode: t.Literal[True] = True, |
| | **extra: t.Any, |
| | ) -> t.NoReturn: ... |
| |
|
| | @t.overload |
| | def main( |
| | self, |
| | args: cabc.Sequence[str] | None = None, |
| | prog_name: str | None = None, |
| | complete_var: str | None = None, |
| | standalone_mode: bool = ..., |
| | **extra: t.Any, |
| | ) -> t.Any: ... |
| |
|
| | def main( |
| | self, |
| | args: cabc.Sequence[str] | None = None, |
| | prog_name: str | None = None, |
| | complete_var: str | None = 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() |
| |
|
| | |
| | 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 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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: |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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: cabc.MutableMapping[str, t.Any], |
| | prog_name: str, |
| | complete_var: str | None = 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 _FakeSubclassCheck(type): |
| | def __subclasscheck__(cls, subclass: type) -> bool: |
| | return issubclass(subclass, cls.__bases__[0]) |
| |
|
| | def __instancecheck__(cls, instance: t.Any) -> bool: |
| | return isinstance(instance, cls.__bases__[0]) |
| |
|
| |
|
| | class _BaseCommand(Command, metaclass=_FakeSubclassCheck): |
| | """ |
| | .. deprecated:: 8.2 |
| | Will be removed in Click 9.0. Use ``Command`` instead. |
| | """ |
| |
|
| |
|
| | class Group(Command): |
| | """A group is a command that nests other commands (or more groups). |
| | |
| | :param name: The name of the group command. |
| | :param commands: Map names to :class:`Command` objects. Can be a list, which |
| | will use :attr:`Command.name` as the keys. |
| | :param invoke_without_command: Invoke the group's callback even if a |
| | subcommand is not given. |
| | :param no_args_is_help: If no arguments are given, show the group's help and |
| | exit. Defaults to the opposite of ``invoke_without_command``. |
| | :param subcommand_metavar: How to represent the subcommand argument in help. |
| | The default will represent whether ``chain`` is set or not. |
| | :param chain: Allow passing more than one subcommand argument. After parsing |
| | a command's arguments, if any arguments remain another command will be |
| | matched, and so on. |
| | :param result_callback: A function to call after the group's and |
| | subcommand's callbacks. The value returned by the subcommand is passed. |
| | If ``chain`` is enabled, the value will be a list of values returned by |
| | all the commands. If ``invoke_without_command`` is enabled, the value |
| | will be the value returned by the group's callback, or an empty list if |
| | ``chain`` is enabled. |
| | :param kwargs: Other arguments passed to :class:`Command`. |
| | |
| | .. versionchanged:: 8.0 |
| | The ``commands`` argument can be a list of command objects. |
| | |
| | .. versionchanged:: 8.2 |
| | Merged with and replaces the ``MultiCommand`` base class. |
| | """ |
| |
|
| | allow_extra_args = True |
| | allow_interspersed_args = False |
| |
|
| | |
| | |
| | |
| | |
| | |
| | command_class: type[Command] | None = None |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | group_class: type[Group] | type[type] | None = None |
| | |
| |
|
| | def __init__( |
| | self, |
| | name: str | None = None, |
| | commands: cabc.MutableMapping[str, Command] |
| | | cabc.Sequence[Command] |
| | | None = None, |
| | invoke_without_command: bool = False, |
| | no_args_is_help: bool | None = None, |
| | subcommand_metavar: str | None = None, |
| | chain: bool = False, |
| | result_callback: t.Callable[..., t.Any] | None = None, |
| | **kwargs: t.Any, |
| | ) -> None: |
| | super().__init__(name, **kwargs) |
| |
|
| | if commands is None: |
| | commands = {} |
| | elif isinstance(commands, abc.Sequence): |
| | commands = {c.name: c for c in commands if c.name is not None} |
| |
|
| | |
| | self.commands: cabc.MutableMapping[str, Command] = commands |
| |
|
| | 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 |
| | |
| | |
| | self._result_callback = result_callback |
| |
|
| | if self.chain: |
| | for param in self.params: |
| | if isinstance(param, Argument) and not param.required: |
| | raise RuntimeError( |
| | "A group in chain mode cannot have optional arguments." |
| | ) |
| |
|
| | def to_info_dict(self, ctx: Context) -> 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 add_command(self, cmd: Command, name: str | None = 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_nested_chain(self, name, cmd, register=True) |
| | self.commands[name] = cmd |
| |
|
| | @t.overload |
| | def command(self, __func: t.Callable[..., t.Any]) -> Command: ... |
| |
|
| | @t.overload |
| | 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.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 |
| |
|
| | func: t.Callable[..., t.Any] | None = None |
| |
|
| | if args and callable(args[0]): |
| | assert len(args) == 1 and not kwargs, ( |
| | "Use 'command(**kwargs)(callable)' to provide arguments." |
| | ) |
| | (func,) = args |
| | args = () |
| |
|
| | if self.command_class and kwargs.get("cls") is None: |
| | kwargs["cls"] = self.command_class |
| |
|
| | 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 |
| |
|
| | @t.overload |
| | def group(self, __func: t.Callable[..., t.Any]) -> Group: ... |
| |
|
| | @t.overload |
| | 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.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.Callable[..., t.Any] | None = 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 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: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: |
| | 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 get_command(self, ctx: Context, cmd_name: str) -> Command | None: |
| | """Given a context and a command name, this returns a :class:`Command` |
| | object if it exists or returns ``None``. |
| | """ |
| | return self.commands.get(cmd_name) |
| |
|
| | def list_commands(self, ctx: Context) -> list[str]: |
| | """Returns a list of subcommand names in the order they should appear.""" |
| | return sorted(self.commands) |
| |
|
| | def collect_usage_pieces(self, ctx: Context) -> 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 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) |
| | |
| | if cmd is None: |
| | continue |
| | if cmd.hidden: |
| | continue |
| |
|
| | commands.append((subcommand, cmd)) |
| |
|
| | |
| | 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: list[str]) -> list[str]: |
| | if not args and self.no_args_is_help and not ctx.resilient_parsing: |
| | raise NoArgsIsHelpError(ctx) |
| |
|
| | 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: |
| | |
| | |
| | |
| | with ctx: |
| | rv = super().invoke(ctx) |
| | return _process_result([] if self.chain else rv) |
| | ctx.fail(_("Missing command.")) |
| |
|
| | |
| | args = [*ctx._protected_args, *ctx.args] |
| | ctx.args = [] |
| | ctx._protected_args = [] |
| |
|
| | |
| | |
| | |
| | if not self.chain: |
| | |
| | |
| | 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)) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | with ctx: |
| | ctx.invoked_subcommand = "*" if args else None |
| | super().invoke(ctx) |
| |
|
| | |
| | |
| | |
| | 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: list[str] |
| | ) -> tuple[str | None, Command | None, list[str]]: |
| | cmd_name = make_str(args[0]) |
| | original_cmd_name = cmd_name |
| |
|
| | |
| | cmd = self.get_command(ctx, cmd_name) |
| |
|
| | |
| | |
| | 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 cmd is None and not ctx.resilient_parsing: |
| | if _split_opt(cmd_name)[0]: |
| | self.parse_args(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 shell_complete(self, ctx: Context, incomplete: str) -> 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 _MultiCommand(Group, metaclass=_FakeSubclassCheck): |
| | """ |
| | .. deprecated:: 8.2 |
| | Will be removed in Click 9.0. Use ``Group`` instead. |
| | """ |
| |
|
| |
|
| | class CommandCollection(Group): |
| | """A :class:`Group` that looks up subcommands on other groups. If a command |
| | is not found on this group, each registered source is checked in order. |
| | Parameters on a source are not added to this group, and a source's callback |
| | is not invoked when invoking its commands. In other words, this "flattens" |
| | commands in many groups into this one group. |
| | |
| | :param name: The name of the group command. |
| | :param sources: A list of :class:`Group` objects to look up commands from. |
| | :param kwargs: Other arguments passed to :class:`Group`. |
| | |
| | .. versionchanged:: 8.2 |
| | This is a subclass of ``Group``. Commands are looked up first on this |
| | group, then each of its sources. |
| | """ |
| |
|
| | def __init__( |
| | self, |
| | name: str | None = None, |
| | sources: list[Group] | None = None, |
| | **kwargs: t.Any, |
| | ) -> None: |
| | super().__init__(name, **kwargs) |
| | |
| | self.sources: list[Group] = sources or [] |
| |
|
| | def add_source(self, group: Group) -> None: |
| | """Add a group as a source of commands.""" |
| | self.sources.append(group) |
| |
|
| | def get_command(self, ctx: Context, cmd_name: str) -> Command | None: |
| | rv = super().get_command(ctx, cmd_name) |
| |
|
| | if rv is not None: |
| | return rv |
| |
|
| | for source in self.sources: |
| | rv = source.get_command(ctx, cmd_name) |
| |
|
| | if rv is not None: |
| | if self.chain: |
| | _check_nested_chain(self, cmd_name, rv) |
| |
|
| | return rv |
| |
|
| | return None |
| |
|
| | def list_commands(self, ctx: Context) -> list[str]: |
| | rv: set[str] = set(super().list_commands(ctx)) |
| |
|
| | for source in self.sources: |
| | rv.update(source.list_commands(ctx)) |
| |
|
| | return sorted(rv) |
| |
|
| |
|
| | def _check_iter(value: t.Any) -> cabc.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 latter 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: environment variable(s) that are used to provide a default value for |
| | this parameter. This can be a string or a sequence of strings. If a sequence is |
| | given, only the first non-empty environment variable is used for the parameter. |
| | :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. |
| | :param deprecated: If ``True`` or non-empty string, issues a message |
| | indicating that the argument is deprecated and highlights |
| | its deprecation in --help. The message can be customized |
| | by using a string as the value. A deprecated parameter |
| | cannot be required, a ValueError will be raised otherwise. |
| | |
| | .. versionchanged:: 8.2.0 |
| | Introduction of ``deprecated``. |
| | |
| | .. versionchanged:: 8.2 |
| | Adding duplicate parameter names to a :class:`~click.core.Command` will |
| | result in a ``UserWarning`` being shown. |
| | |
| | .. versionchanged:: 8.2 |
| | Adding duplicate parameter names to a :class:`~click.core.Command` will |
| | result in a ``UserWarning`` being shown. |
| | |
| | .. 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: cabc.Sequence[str] | None = None, |
| | type: types.ParamType | t.Any | None = None, |
| | required: bool = False, |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | default: t.Any | t.Callable[[], t.Any] | None = UNSET, |
| | callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, |
| | nargs: int | None = None, |
| | multiple: bool = False, |
| | metavar: str | None = None, |
| | expose_value: bool = True, |
| | is_eager: bool = False, |
| | envvar: str | cabc.Sequence[str] | None = None, |
| | shell_complete: t.Callable[ |
| | [Context, Parameter, str], list[CompletionItem] | list[str] |
| | ] |
| | | None = None, |
| | deprecated: bool | str = False, |
| | ) -> None: |
| | self.name: str | None |
| | self.opts: list[str] |
| | self.secondary_opts: list[str] |
| | self.name, self.opts, self.secondary_opts = self._parse_decls( |
| | param_decls or (), expose_value |
| | ) |
| | self.type: types.ParamType = types.convert_type(type, default) |
| |
|
| | |
| | |
| | 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: t.Any | t.Callable[[], t.Any] | None = default |
| | self.is_eager = is_eager |
| | self.metavar = metavar |
| | self.envvar = envvar |
| | self._custom_shell_complete = shell_complete |
| | self.deprecated = deprecated |
| |
|
| | 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}." |
| | ) |
| |
|
| | if required and deprecated: |
| | raise ValueError( |
| | f"The {self.param_type_name} '{self.human_readable_name}' " |
| | "is deprecated and still required. A deprecated " |
| | f"{self.param_type_name} cannot be required." |
| | ) |
| |
|
| | def to_info_dict(self) -> 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. |
| | |
| | .. versionchanged:: 8.3.0 |
| | Returns ``None`` for the :attr:`default` if it was not set. |
| | |
| | .. 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 if self.default is not UNSET else None, |
| | "envvar": self.envvar, |
| | } |
| |
|
| | def __repr__(self) -> str: |
| | return f"<{self.__class__.__name__} {self.name}>" |
| |
|
| | def _parse_decls( |
| | self, decls: cabc.Sequence[str], expose_value: bool |
| | ) -> tuple[str | None, list[str], list[str]]: |
| | raise NotImplementedError() |
| |
|
| | @property |
| | 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 |
| |
|
| | def make_metavar(self, ctx: Context) -> str: |
| | if self.metavar is not None: |
| | return self.metavar |
| |
|
| | metavar = self.type.get_metavar(param=self, ctx=ctx) |
| |
|
| | if metavar is None: |
| | metavar = self.type.name.upper() |
| |
|
| | if self.nargs != 1: |
| | metavar += "..." |
| |
|
| | return metavar |
| |
|
| | @t.overload |
| | def get_default( |
| | self, ctx: Context, call: t.Literal[True] = True |
| | ) -> t.Any | None: ... |
| |
|
| | @t.overload |
| | def get_default( |
| | self, ctx: Context, call: bool = ... |
| | ) -> t.Any | t.Callable[[], t.Any] | None: ... |
| |
|
| | def get_default( |
| | self, ctx: Context, call: bool = True |
| | ) -> t.Any | t.Callable[[], t.Any] | None: |
| | """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) |
| |
|
| | if value is UNSET: |
| | 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: cabc.Mapping[str, t.Any] |
| | ) -> tuple[t.Any, ParameterSource]: |
| | """Returns the parameter value produced by the parser. |
| | |
| | If the parser did not produce a value from user input, the value is either |
| | sourced from the environment variable, the default map, or the parameter's |
| | default value. In that order of precedence. |
| | |
| | If no value is found, an internal sentinel value is returned. |
| | |
| | :meta private: |
| | """ |
| | |
| | value = opts.get(self.name, UNSET) |
| | |
| | |
| | source = ( |
| | ParameterSource.COMMANDLINE |
| | if value is not UNSET |
| | else ParameterSource.DEFAULT |
| | ) |
| |
|
| | if value is UNSET: |
| | envvar_value = self.value_from_envvar(ctx) |
| | if envvar_value is not None: |
| | value = envvar_value |
| | source = ParameterSource.ENVIRONMENT |
| |
|
| | if value is UNSET: |
| | default_map_value = ctx.lookup_default(self.name) |
| | if default_map_value is not UNSET: |
| | value = default_map_value |
| | source = ParameterSource.DEFAULT_MAP |
| |
|
| | if value is UNSET: |
| | default_value = self.get_default(ctx) |
| | if default_value is not UNSET: |
| | value = default_value |
| | 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 parameter's |
| | :attr:`type`, :attr:`multiple`, and :attr:`nargs`. |
| | """ |
| | if value is None: |
| | if self.multiple or self.nargs == -1: |
| | return () |
| | else: |
| | return value |
| |
|
| | def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: |
| | try: |
| | return _check_iter(value) |
| | except TypeError: |
| | |
| | |
| | |
| | raise BadParameter( |
| | _("Value must be an iterable."), ctx=ctx, param=self |
| | ) from None |
| |
|
| | |
| |
|
| | if self.nargs == 1 or self.type.is_composite: |
| |
|
| | def convert(value: t.Any) -> t.Any: |
| | return self.type(value, param=self, ctx=ctx) |
| |
|
| | elif self.nargs == -1: |
| |
|
| | def convert(value: t.Any) -> t.Any: |
| | return tuple(self.type(x, self, ctx) for x in check_iter(value)) |
| |
|
| | else: |
| |
|
| | def convert(value: t.Any) -> t.Any: |
| | 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: |
| | """A value is considered missing if: |
| | |
| | - it is :attr:`UNSET`, |
| | - or if it is an empty sequence while the parameter is suppose to have |
| | non-single value (i.e. :attr:`nargs` is not ``1`` or :attr:`multiple` is |
| | set). |
| | |
| | :meta private: |
| | """ |
| | if value is UNSET: |
| | 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: |
| | """Process the value of this parameter: |
| | |
| | 1. Type cast the value using :meth:`type_cast_value`. |
| | 2. Check if the value is missing (see: :meth:`value_is_missing`), and raise |
| | :exc:`MissingParameter` if it is required. |
| | 3. If a :attr:`callback` is set, call it to have the value replaced by the |
| | result of the callback. If the value was not set, the callback receive |
| | ``None``. This keep the legacy behavior as it was before the introduction of |
| | the :attr:`UNSET` sentinel. |
| | |
| | :meta private: |
| | """ |
| | |
| | |
| | |
| | |
| | |
| | if value is UNSET: |
| | if self.multiple or self.nargs == -1: |
| | value = () |
| | else: |
| | 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: |
| | |
| | |
| | if value is UNSET: |
| | value = None |
| |
|
| | |
| | unset_keys = {k: None for k, v in ctx.params.items() if v is UNSET} |
| | |
| | if not unset_keys: |
| | value = self.callback(ctx, self, value) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | else: |
| | |
| | |
| | with ctx: |
| | |
| | ctx.params.update(unset_keys) |
| | |
| | value = self.callback(ctx, self, value) |
| | |
| | ctx.params.update( |
| | { |
| | k: UNSET |
| | for k in unset_keys |
| | |
| | |
| | if k in ctx.params and ctx.params[k] is None |
| | } |
| | ) |
| |
|
| | return value |
| |
|
| | def resolve_envvar_value(self, ctx: Context) -> str | None: |
| | """Returns the value found in the environment variable(s) attached to this |
| | parameter. |
| | |
| | Environment variables values are `always returned as strings |
| | <https://docs.python.org/3/library/os.html#os.environ>`_. |
| | |
| | This method returns ``None`` if: |
| | |
| | - the :attr:`envvar` property is not set on the :class:`Parameter`, |
| | - the environment variable is not found in the environment, |
| | - the variable is found in the environment but its value is empty (i.e. the |
| | environment variable is present but has an empty string). |
| | |
| | If :attr:`envvar` is setup with multiple environment variables, |
| | then only the first non-empty value is returned. |
| | |
| | .. caution:: |
| | |
| | The raw value extracted from the environment is not normalized and is |
| | returned as-is. Any normalization or reconciliation is performed later by |
| | the :class:`Parameter`'s :attr:`type`. |
| | |
| | :meta private: |
| | """ |
| | if not self.envvar: |
| | 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) -> str | cabc.Sequence[str] | None: |
| | """Process the raw environment variable string for this parameter. |
| | |
| | Returns the string as-is or splits it into a sequence of strings if the |
| | parameter is expecting multiple values (i.e. its :attr:`nargs` property is set |
| | to a value other than ``1``). |
| | |
| | :meta private: |
| | """ |
| | rv = self.resolve_envvar_value(ctx) |
| |
|
| | if rv is not None and self.nargs != 1: |
| | return self.type.split_envvar_value(rv) |
| |
|
| | return rv |
| |
|
| | def handle_parse_result( |
| | self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] |
| | ) -> tuple[t.Any, list[str]]: |
| | """Process the value produced by the parser from user input. |
| | |
| | Always process the value through the Parameter's :attr:`type`, wherever it |
| | comes from. |
| | |
| | If the parameter is deprecated, this method warn the user about it. But only if |
| | the value has been explicitly set by the user (and as such, is not coming from |
| | a default). |
| | |
| | :meta private: |
| | """ |
| | with augment_usage_errors(ctx, param=self): |
| | value, source = self.consume_value(ctx, opts) |
| |
|
| | ctx.set_parameter_source(self.name, source) |
| |
|
| | |
| | if ( |
| | self.deprecated |
| | and value is not UNSET |
| | and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) |
| | ): |
| | extra_message = ( |
| | f" {self.deprecated}" if isinstance(self.deprecated, str) else "" |
| | ) |
| | message = _( |
| | "DeprecationWarning: The {param_type} {name!r} is deprecated." |
| | "{extra_message}" |
| | ).format( |
| | param_type=self.param_type_name, |
| | name=self.human_readable_name, |
| | extra_message=extra_message, |
| | ) |
| | echo(style(message, fg="red"), err=True) |
| |
|
| | |
| | try: |
| | value = self.process_value(ctx, value) |
| | except Exception: |
| | if not ctx.resilient_parsing: |
| | raise |
| | |
| | |
| | |
| | value = UNSET |
| |
|
| | |
| | if ( |
| | self.expose_value |
| | |
| | |
| | |
| | and (self.name not in ctx.params or ctx.params[self.name] is UNSET) |
| | ): |
| | |
| | |
| | assert self.name is not None, ( |
| | f"{self!r} parameter's name should not be None when exposing value." |
| | ) |
| | ctx.params[self.name] = value |
| |
|
| | return value, args |
| |
|
| | def get_help_record(self, ctx: Context) -> tuple[str, str] | None: |
| | pass |
| |
|
| | def get_usage_pieces(self, ctx: Context) -> 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) -> 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("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 and error messages. |
| | 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. A deprecated option cannot be |
| | prompted. |
| | :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. |
| | :param attrs: Other command arguments described in :class:`Parameter`. |
| | |
| | .. versionchanged:: 8.2 |
| | ``envvar`` used with ``flag_value`` will always use the ``flag_value``, |
| | previously it would use the value of the environment variable. |
| | |
| | .. versionchanged:: 8.1 |
| | Help text indentation is cleaned here instead of only in the |
| | ``@option`` decorator. |
| | |
| | .. versionchanged:: 8.1 |
| | The ``show_default`` parameter overrides |
| | ``Context.show_default``. |
| | |
| | .. versionchanged:: 8.1 |
| | 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: cabc.Sequence[str] | None = None, |
| | show_default: bool | str | None = None, |
| | prompt: bool | str = False, |
| | confirmation_prompt: bool | str = False, |
| | prompt_required: bool = True, |
| | hide_input: bool = False, |
| | is_flag: bool | None = None, |
| | flag_value: t.Any = UNSET, |
| | multiple: bool = False, |
| | count: bool = False, |
| | allow_from_autoenv: bool = True, |
| | type: types.ParamType | t.Any | None = None, |
| | help: str | None = None, |
| | hidden: bool = False, |
| | show_choices: bool = True, |
| | show_envvar: bool = False, |
| | deprecated: bool | str = False, |
| | **attrs: t.Any, |
| | ) -> None: |
| | if help: |
| | help = inspect.cleandoc(help) |
| |
|
| | super().__init__( |
| | param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs |
| | ) |
| |
|
| | if prompt is True: |
| | if self.name is None: |
| | raise TypeError("'name' is required with 'prompt=True'.") |
| |
|
| | prompt_text: str | None = self.name.replace("_", " ").capitalize() |
| | elif prompt is False: |
| | prompt_text = None |
| | else: |
| | prompt_text = prompt |
| |
|
| | if deprecated: |
| | deprecated_message = ( |
| | f"(DEPRECATED: {deprecated})" |
| | if isinstance(deprecated, str) |
| | else "(DEPRECATED)" |
| | ) |
| | help = help + deprecated_message if help is not None else deprecated_message |
| |
|
| | self.prompt = prompt_text |
| | self.confirmation_prompt = confirmation_prompt |
| | self.prompt_required = prompt_required |
| | self.hide_input = hide_input |
| | self.hidden = hidden |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | self._flag_needs_value = self.prompt is not None and not self.prompt_required |
| |
|
| | |
| | if is_flag is None: |
| | |
| | if flag_value is not UNSET: |
| | is_flag = True |
| | |
| | elif self._flag_needs_value: |
| | is_flag = False |
| | |
| | elif self.secondary_opts: |
| | is_flag = True |
| | |
| | |
| | elif is_flag is False and not self._flag_needs_value: |
| | self._flag_needs_value = self.default is UNSET |
| |
|
| | if is_flag: |
| | |
| | if self.default is UNSET and not self.required and not self.prompt: |
| | if multiple: |
| | self.default = () |
| |
|
| | |
| | if type is None: |
| | |
| | if flag_value is UNSET: |
| | self.type: types.ParamType = types.BoolParamType() |
| | |
| | elif isinstance(flag_value, bool): |
| | self.type = types.BoolParamType() |
| | |
| | else: |
| | self.type = types.convert_type(None, flag_value) |
| |
|
| | self.is_flag: bool = bool(is_flag) |
| | self.is_bool_flag: bool = bool( |
| | is_flag and isinstance(self.type, types.BoolParamType) |
| | ) |
| | self.flag_value: t.Any = flag_value |
| |
|
| | |
| | if self.is_bool_flag: |
| | if self.default is UNSET and not self.required: |
| | self.default = False |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | if self.default is True and self.flag_value is not UNSET: |
| | self.default = self.flag_value |
| |
|
| | |
| | if self.flag_value is UNSET: |
| | if self.is_flag: |
| | self.flag_value = True |
| | else: |
| | self.flag_value = None |
| |
|
| | |
| | self.count = count |
| | if count: |
| | if type is None: |
| | self.type = types.IntRange(min=0) |
| | if self.default is UNSET: |
| | 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 deprecated and prompt: |
| | raise ValueError("`deprecated` options cannot use `prompt`.") |
| |
|
| | if self.nargs == -1: |
| | raise TypeError("nargs=-1 is not supported for options.") |
| |
|
| | 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'.") |
| |
|
| | def to_info_dict(self) -> dict[str, t.Any]: |
| | """ |
| | .. versionchanged:: 8.3.0 |
| | Returns ``None`` for the :attr:`flag_value` if it was not set. |
| | """ |
| | 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 if self.flag_value is not UNSET else None, |
| | count=self.count, |
| | hidden=self.hidden, |
| | ) |
| | return info_dict |
| |
|
| | def get_error_hint(self, ctx: Context) -> str: |
| | result = super().get_error_hint(ctx) |
| | if self.show_envvar and self.envvar is not None: |
| | result += f" (env var: '{self.envvar}')" |
| | return result |
| |
|
| | def _parse_decls( |
| | self, decls: cabc.Sequence[str], expose_value: bool |
| | ) -> tuple[str | None, list[str], 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])) |
| | 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( |
| | f"Could not determine name for option with declarations {decls!r}" |
| | ) |
| |
|
| | 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) -> tuple[str, str] | None: |
| | if self.hidden: |
| | return None |
| |
|
| | any_prefix_is_slash = False |
| |
|
| | def _write_opts(opts: cabc.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(ctx=ctx)}" |
| |
|
| | return rv |
| |
|
| | rv = [_write_opts(self.opts)] |
| |
|
| | if self.secondary_opts: |
| | rv.append(_write_opts(self.secondary_opts)) |
| |
|
| | help = self.help or "" |
| |
|
| | extra = self.get_help_extra(ctx) |
| | extra_items = [] |
| | if "envvars" in extra: |
| | extra_items.append( |
| | _("env var: {var}").format(var=", ".join(extra["envvars"])) |
| | ) |
| | if "default" in extra: |
| | extra_items.append(_("default: {default}").format(default=extra["default"])) |
| | if "range" in extra: |
| | extra_items.append(extra["range"]) |
| | if "required" in extra: |
| | extra_items.append(_(extra["required"])) |
| |
|
| | if extra_items: |
| | extra_str = "; ".join(extra_items) |
| | help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" |
| |
|
| | return ("; " if any_prefix_is_slash else " / ").join(rv), help |
| |
|
| | def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: |
| | extra: types.OptionHelpExtra = {} |
| |
|
| | 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: |
| | if isinstance(envvar, str): |
| | extra["envvars"] = (envvar,) |
| | else: |
| | extra["envvars"] = tuple(str(d) for d in envvar) |
| |
|
| | |
| | |
| | |
| | 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 not in (None, UNSET)) |
| | ): |
| | 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 isinstance(default_value, enum.Enum): |
| | default_string = default_value.name |
| | elif inspect.isfunction(default_value): |
| | default_string = _("(dynamic)") |
| | elif self.is_bool_flag and self.secondary_opts: |
| | |
| | |
| | default_string = _split_opt( |
| | (self.opts if default_value else self.secondary_opts)[0] |
| | )[1] |
| | elif self.is_bool_flag and not self.secondary_opts and not default_value: |
| | default_string = "" |
| | elif default_value == "": |
| | default_string = '""' |
| | else: |
| | default_string = str(default_value) |
| |
|
| | if default_string: |
| | extra["default"] = default_string |
| |
|
| | if ( |
| | isinstance(self.type, types._NumberRangeBase) |
| | |
| | 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["range"] = range_str |
| |
|
| | if self.required: |
| | extra["required"] = "required" |
| |
|
| | return extra |
| |
|
| | 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 |
| |
|
| | |
| | |
| | default = self.get_default(ctx) |
| |
|
| | |
| | if self.is_bool_flag: |
| | |
| | |
| | if default in (UNSET, None): |
| | default = None |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | else: |
| | default = bool(default) |
| | return confirm(self.prompt, default) |
| |
|
| | |
| | |
| | prompt_kwargs: t.Any = {} |
| | if isinstance(self.show_default, bool): |
| | prompt_kwargs["show_default"] = self.show_default |
| |
|
| | return prompt( |
| | self.prompt, |
| | |
| | |
| | default=None if default is UNSET else 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), |
| | **prompt_kwargs, |
| | ) |
| |
|
| | def resolve_envvar_value(self, ctx: Context) -> str | None: |
| | """:class:`Option` resolves its environment variable the same way as |
| | :func:`Parameter.resolve_envvar_value`, but it also supports |
| | :attr:`Context.auto_envvar_prefix`. If we could not find an environment from |
| | the :attr:`envvar` property, we fallback on :attr:`Context.auto_envvar_prefix` |
| | to build dynamiccaly the environment variable name using the |
| | :python:`{ctx.auto_envvar_prefix}_{self.name.upper()}` template. |
| | |
| | :meta private: |
| | """ |
| | 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.Any: |
| | """For :class:`Option`, this method processes the raw environment variable |
| | string the same way as :func:`Parameter.value_from_envvar` does. |
| | |
| | But in the case of non-boolean flags, the value is analyzed to determine if the |
| | flag is activated or not, and returns a boolean of its activation, or the |
| | :attr:`flag_value` if the latter is set. |
| | |
| | This method also takes care of repeated options (i.e. options with |
| | :attr:`multiple` set to ``True``). |
| | |
| | :meta private: |
| | """ |
| | rv = self.resolve_envvar_value(ctx) |
| |
|
| | |
| | if rv is None: |
| | return None |
| |
|
| | |
| | |
| | |
| | if self.is_flag and not self.is_bool_flag: |
| | |
| | |
| | if self.flag_value is not UNSET and rv == self.flag_value: |
| | return self.flag_value |
| | |
| | |
| | return types.BoolParamType.str_to_bool(rv) |
| |
|
| | |
| | value_depth = (self.nargs != 1) + bool(self.multiple) |
| | if value_depth > 0: |
| | multi_rv = self.type.split_envvar_value(rv) |
| | if self.multiple and self.nargs != 1: |
| | multi_rv = batch(multi_rv, self.nargs) |
| |
|
| | return multi_rv |
| |
|
| | return rv |
| |
|
| | def consume_value( |
| | self, ctx: Context, opts: cabc.Mapping[str, Parameter] |
| | ) -> tuple[t.Any, ParameterSource]: |
| | """For :class:`Option`, the value can be collected from an interactive prompt |
| | if the option is a flag that needs a value (and the :attr:`prompt` property is |
| | set). |
| | |
| | Additionally, this method handles flag option that are activated without a |
| | value, in which case the :attr:`flag_value` is returned. |
| | |
| | :meta private: |
| | """ |
| | value, source = super().consume_value(ctx, opts) |
| |
|
| | |
| | |
| | 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.is_flag |
| | and value is True |
| | and not self.is_bool_flag |
| | and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) |
| | ): |
| | value = self.flag_value |
| |
|
| | |
| | |
| | |
| | elif ( |
| | self.multiple |
| | and value is not UNSET |
| | and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) |
| | 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 |
| |
|
| | |
| | |
| | elif ( |
| | ( |
| | value is UNSET |
| | or source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) |
| | ) |
| | 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 |
| |
|
| | def process_value(self, ctx: Context, value: t.Any) -> t.Any: |
| | |
| | |
| | |
| | |
| | |
| | if self.is_flag and not self.required and self.is_bool_flag and value is UNSET: |
| | value = False |
| |
|
| | if self.callback is not None: |
| | value = self.callback(ctx, self, value) |
| |
|
| | return value |
| |
|
| | |
| | return super().process_value(ctx, value) |
| |
|
| |
|
| | 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 constructor of :class:`Parameter`. |
| | """ |
| |
|
| | param_type_name = "argument" |
| |
|
| | def __init__( |
| | self, |
| | param_decls: cabc.Sequence[str], |
| | required: bool | None = None, |
| | **attrs: t.Any, |
| | ) -> None: |
| | |
| | if required is None: |
| | |
| | |
| | if attrs.get("default", UNSET) is UNSET: |
| | required = attrs.get("nargs", 1) > 0 |
| | |
| | else: |
| | required = False |
| |
|
| | if "multiple" in attrs: |
| | raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") |
| |
|
| | super().__init__(param_decls, required=required, **attrs) |
| |
|
| | @property |
| | def human_readable_name(self) -> str: |
| | if self.metavar is not None: |
| | return self.metavar |
| | return self.name.upper() |
| |
|
| | def make_metavar(self, ctx: Context) -> str: |
| | if self.metavar is not None: |
| | return self.metavar |
| | var = self.type.get_metavar(param=self, ctx=ctx) |
| | if not var: |
| | var = self.name.upper() |
| | if self.deprecated: |
| | var += "!" |
| | if not self.required: |
| | var = f"[{var}]" |
| | if self.nargs != 1: |
| | var += "..." |
| | return var |
| |
|
| | def _parse_decls( |
| | self, decls: cabc.Sequence[str], expose_value: bool |
| | ) -> tuple[str | None, list[str], list[str]]: |
| | if not decls: |
| | if not expose_value: |
| | return None, [], [] |
| | raise TypeError("Argument is marked as exposed, but does not have a name.") |
| | 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)}: {decls}." |
| | ) |
| | return name, [arg], [] |
| |
|
| | def get_usage_pieces(self, ctx: Context) -> list[str]: |
| | return [self.make_metavar(ctx)] |
| |
|
| | def get_error_hint(self, ctx: Context) -> str: |
| | return f"'{self.make_metavar(ctx)}'" |
| |
|
| | def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: |
| | parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) |
| |
|
| |
|
| | def __getattr__(name: str) -> object: |
| | import warnings |
| |
|
| | if name == "BaseCommand": |
| | warnings.warn( |
| | "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" |
| | " 'Command' instead.", |
| | DeprecationWarning, |
| | stacklevel=2, |
| | ) |
| | return _BaseCommand |
| |
|
| | if name == "MultiCommand": |
| | warnings.warn( |
| | "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" |
| | " 'Group' instead.", |
| | DeprecationWarning, |
| | stacklevel=2, |
| | ) |
| | return _MultiCommand |
| |
|
| | raise AttributeError(name) |
| |
|