codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import collections.abc as cabc4import enum5import errno6import inspect7import os8import sys9import typing as t10from collections import abc11from collections import Counter12from contextlib import AbstractContextManager13from contextlib import contextmanager14from contextlib import ExitStack15from functools import update_wrapper16from gettext import gettext as _17from gettext import ngettext18from itertools import repeat19from types import TracebackType20 21from . import types22from ._utils import FLAG_NEEDS_VALUE23from ._utils import UNSET24from .exceptions import Abort25from .exceptions import BadParameter26from .exceptions import ClickException27from .exceptions import Exit28from .exceptions import MissingParameter29from .exceptions import NoArgsIsHelpError30from .exceptions import UsageError31from .formatting import HelpFormatter32from .formatting import join_options33from .globals import pop_context34from .globals import push_context35from .parser import _OptionParser36from .parser import _split_opt37from .termui import confirm38from .termui import prompt39from .termui import style40from .utils import _detect_program_name41from .utils import _expand_args42from .utils import echo43from .utils import make_default_short_help44from .utils import make_str45from .utils import PacifyFlushWrapper46 47if t.TYPE_CHECKING:48 from .shell_completion import CompletionItem49 50F = t.TypeVar("F", bound="t.Callable[..., t.Any]")51V = t.TypeVar("V")52 53 54def _complete_visible_commands(55 ctx: Context, incomplete: str56) -> cabc.Iterator[tuple[str, Command]]:57 """List all the subcommands of a group that start with the58 incomplete value and aren't hidden.59 60 :param ctx: Invocation context for the group.61 :param incomplete: Value being completed. May be empty.62 """63 multi = t.cast(Group, ctx.command)64 65 for name in multi.list_commands(ctx):66 if name.startswith(incomplete):67 command = multi.get_command(ctx, name)68 69 if command is not None and not command.hidden:70 yield name, command71 72 73def _check_nested_chain(74 base_command: Group, cmd_name: str, cmd: Command, register: bool = False75) -> None:76 if not base_command.chain or not isinstance(cmd, Group):77 return78 79 if register:80 message = (81 f"It is not possible to add the group {cmd_name!r} to another"82 f" group {base_command.name!r} that is in chain mode."83 )84 else:85 message = (86 f"Found the group {cmd_name!r} as subcommand to another group "87 f" {base_command.name!r} that is in chain mode. This is not supported."88 )89 90 raise RuntimeError(message)91 92 93def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]:94 return list(zip(*repeat(iter(iterable), batch_size), strict=False))95 96 97@contextmanager98def augment_usage_errors(99 ctx: Context, param: Parameter | None = None100) -> cabc.Iterator[None]:101 """Context manager that attaches extra information to exceptions."""102 try:103 yield104 except BadParameter as e:105 if e.ctx is None:106 e.ctx = ctx107 if param is not None and e.param is None:108 e.param = param109 raise110 except UsageError as e:111 if e.ctx is None:112 e.ctx = ctx113 raise114 115 116def iter_params_for_processing(117 invocation_order: cabc.Sequence[Parameter],118 declaration_order: cabc.Sequence[Parameter],119) -> list[Parameter]:120 """Returns all declared parameters in the order they should be processed.121 122 The declared parameters are re-shuffled depending on the order in which123 they were invoked, as well as the eagerness of each parameters.124 125 The invocation order takes precedence over the declaration order. I.e. the126 order in which the user provided them to the CLI is respected.127 128 This behavior and its effect on callback evaluation is detailed at:129 https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order130 """131 132 def sort_key(item: Parameter) -> tuple[bool, float]:133 try:134 idx: float = invocation_order.index(item)135 except ValueError:136 idx = float("inf")137 138 return not item.is_eager, idx139 140 return sorted(declaration_order, key=sort_key)141 142 143class ParameterSource(enum.IntEnum):144 """This is an :class:`~enum.IntEnum` that indicates the source of a145 parameter's value.146 147 Use :meth:`click.Context.get_parameter_source` to get the148 source for a parameter by name.149 150 Members are ordered from most explicit to least explicit source.151 This allows comparison to check if a value was explicitly provided:152 153 .. code-block:: python154 155 source = ctx.get_parameter_source("port")156 if source < click.ParameterSource.DEFAULT_MAP:157 ... # value was explicitly set158 159 .. versionchanged:: 8.3.3160 Use :class:`~enum.IntEnum` and reorder members from most to161 least explicit. Supports comparison operators.162 163 .. versionchanged:: 8.0164 Use :class:`~enum.Enum` and drop the ``validate`` method.165 166 .. versionchanged:: 8.0167 Added the ``PROMPT`` value.168 """169 170 PROMPT = enum.auto()171 """Used a prompt to confirm a default or provide a value."""172 COMMANDLINE = enum.auto()173 """The value was provided by the command line args."""174 ENVIRONMENT = enum.auto()175 """The value was provided with an environment variable."""176 DEFAULT_MAP = enum.auto()177 """Used a default provided by :attr:`Context.default_map`."""178 DEFAULT = enum.auto()179 """Used the default specified by the parameter."""180 181 182class Context:183 """The context is a special internal object that holds state relevant184 for the script execution at every single level. It's normally invisible185 to commands unless they opt-in to getting access to it.186 187 The context is useful as it can pass internal objects around and can188 control special execution features such as reading data from189 environment variables.190 191 A context can be used as context manager in which case it will call192 :meth:`close` on teardown.193 194 :param command: the command class for this context.195 :param parent: the parent context.196 :param info_name: the info name for this invocation. Generally this197 is the most descriptive name for the script or198 command. For the toplevel script it is usually199 the name of the script, for commands below it it's200 the name of the script.201 :param obj: an arbitrary object of user data.202 :param auto_envvar_prefix: the prefix to use for automatic environment203 variables. If this is `None` then reading204 from environment variables is disabled. This205 does not affect manually set environment206 variables which are always read.207 :param default_map: a dictionary (like object) with default values208 for parameters.209 :param terminal_width: the width of the terminal. The default is210 inherit from parent context. If no context211 defines the terminal width then auto212 detection will be applied.213 :param max_content_width: the maximum width for content rendered by214 Click (this currently only affects help215 pages). This defaults to 80 characters if216 not overridden. In other words: even if the217 terminal is larger than that, Click will not218 format things wider than 80 characters by219 default. In addition to that, formatters might220 add some safety mapping on the right.221 :param resilient_parsing: if this flag is enabled then Click will222 parse without any interactivity or callback223 invocation. Default values will also be224 ignored. This is useful for implementing225 things such as completion support.226 :param allow_extra_args: if this is set to `True` then extra arguments227 at the end will not raise an error and will be228 kept on the context. The default is to inherit229 from the command.230 :param allow_interspersed_args: if this is set to `False` then options231 and arguments cannot be mixed. The232 default is to inherit from the command.233 :param ignore_unknown_options: instructs click to ignore options it does234 not know and keeps them for later235 processing.236 :param help_option_names: optionally a list of strings that define how237 the default help parameter is named. The238 default is ``['--help']``.239 :param token_normalize_func: an optional function that is used to240 normalize tokens (options, choices,241 etc.). This for instance can be used to242 implement case insensitive behavior.243 :param color: controls if the terminal supports ANSI colors or not. The244 default is autodetection. This is only needed if ANSI245 codes are used in texts that Click prints which is by246 default not the case. This for instance would affect247 help output.248 :param show_default: Show the default value for commands. If this249 value is not set, it defaults to the value from the parent250 context. ``Command.show_default`` overrides this default for the251 specific command.252 253 .. versionchanged:: 8.2254 The ``protected_args`` attribute is deprecated and will be removed in255 Click 9.0. ``args`` will contain remaining unparsed tokens.256 257 .. versionchanged:: 8.1258 The ``show_default`` parameter is overridden by259 ``Command.show_default``, instead of the other way around.260 261 .. versionchanged:: 8.0262 The ``show_default`` parameter defaults to the value from the263 parent context.264 265 .. versionchanged:: 7.1266 Added the ``show_default`` parameter.267 268 .. versionchanged:: 4.0269 Added the ``color``, ``ignore_unknown_options``, and270 ``max_content_width`` parameters.271 272 .. versionchanged:: 3.0273 Added the ``allow_extra_args`` and ``allow_interspersed_args``274 parameters.275 276 .. versionchanged:: 2.0277 Added the ``resilient_parsing``, ``help_option_names``, and278 ``token_normalize_func`` parameters.279 """280 281 #: The formatter class to create with :meth:`make_formatter`.282 #:283 #: .. versionadded:: 8.0284 formatter_class: type[HelpFormatter] = HelpFormatter285 286 def __init__(287 self,288 command: Command,289 parent: Context | None = None,290 info_name: str | None = None,291 obj: t.Any | None = None,292 auto_envvar_prefix: str | None = None,293 default_map: cabc.MutableMapping[str, t.Any] | None = None,294 terminal_width: int | None = None,295 max_content_width: int | None = None,296 resilient_parsing: bool = False,297 allow_extra_args: bool | None = None,298 allow_interspersed_args: bool | None = None,299 ignore_unknown_options: bool | None = None,300 help_option_names: list[str] | None = None,301 token_normalize_func: t.Callable[[str], str] | None = None,302 color: bool | None = None,303 show_default: bool | None = None,304 ) -> None:305 #: the parent context or `None` if none exists.306 self.parent = parent307 #: the :class:`Command` for this context.308 self.command = command309 #: the descriptive information name310 self.info_name = info_name311 #: Map of parameter names to their parsed values. Parameters312 #: with ``expose_value=False`` are not stored.313 self.params: dict[str, t.Any] = {}314 #: the leftover arguments.315 self.args: list[str] = []316 #: protected arguments. These are arguments that are prepended317 #: to `args` when certain parsing scenarios are encountered but318 #: must be never propagated to another arguments. This is used319 #: to implement nested parsing.320 self._protected_args: list[str] = []321 #: the collected prefixes of the command's options.322 self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set()323 324 if obj is None and parent is not None:325 obj = parent.obj326 327 #: the user object stored.328 self.obj: t.Any = obj329 self._meta: dict[str, t.Any] = getattr(parent, "meta", {})330 331 #: A dictionary (-like object) with defaults for parameters.332 if (333 default_map is None334 and info_name is not None335 and parent is not None336 and parent.default_map is not None337 ):338 default_map = parent.default_map.get(info_name)339 340 self.default_map: cabc.MutableMapping[str, t.Any] | None = default_map341 342 #: This flag indicates if a subcommand is going to be executed. A343 #: group callback can use this information to figure out if it's344 #: being executed directly or because the execution flow passes345 #: onwards to a subcommand. By default it's None, but it can be346 #: the name of the subcommand to execute.347 #:348 #: If chaining is enabled this will be set to ``'*'`` in case349 #: any commands are executed. It is however not possible to350 #: figure out which ones. If you require this knowledge you351 #: should use a :func:`result_callback`.352 self.invoked_subcommand: str | None = None353 354 if terminal_width is None and parent is not None:355 terminal_width = parent.terminal_width356 357 #: The width of the terminal (None is autodetection).358 self.terminal_width: int | None = terminal_width359 360 if max_content_width is None and parent is not None:361 max_content_width = parent.max_content_width362 363 #: The maximum width of formatted content (None implies a sensible364 #: default which is 80 for most things).365 self.max_content_width: int | None = max_content_width366 367 if allow_extra_args is None:368 allow_extra_args = command.allow_extra_args369 370 #: Indicates if the context allows extra args or if it should371 #: fail on parsing.372 #:373 #: .. versionadded:: 3.0374 self.allow_extra_args = allow_extra_args375 376 if allow_interspersed_args is None:377 allow_interspersed_args = command.allow_interspersed_args378 379 #: Indicates if the context allows mixing of arguments and380 #: options or not.381 #:382 #: .. versionadded:: 3.0383 self.allow_interspersed_args: bool = allow_interspersed_args384 385 if ignore_unknown_options is None:386 ignore_unknown_options = command.ignore_unknown_options387 388 #: Instructs click to ignore options that a command does not389 #: understand and will store it on the context for later390 #: processing. This is primarily useful for situations where you391 #: want to call into external programs. Generally this pattern is392 #: strongly discouraged because it's not possibly to losslessly393 #: forward all arguments.394 #:395 #: .. versionadded:: 4.0396 self.ignore_unknown_options: bool = ignore_unknown_options397 398 if help_option_names is None:399 if parent is not None:400 help_option_names = parent.help_option_names401 else:402 help_option_names = ["--help"]403 404 #: The names for the help options.405 self.help_option_names: list[str] = help_option_names406 407 if token_normalize_func is None and parent is not None:408 token_normalize_func = parent.token_normalize_func409 410 #: An optional normalization function for tokens. This is411 #: options, choices, commands etc.412 self.token_normalize_func: t.Callable[[str], str] | None = token_normalize_func413 414 #: Indicates if resilient parsing is enabled. In that case Click415 #: will do its best to not cause any failures and default values416 #: will be ignored. Useful for completion.417 self.resilient_parsing: bool = resilient_parsing418 419 # If there is no envvar prefix yet, but the parent has one and420 # the command on this level has a name, we can expand the envvar421 # prefix automatically.422 if auto_envvar_prefix is None:423 if (424 parent is not None425 and parent.auto_envvar_prefix is not None426 and self.info_name is not None427 ):428 auto_envvar_prefix = (429 f"{parent.auto_envvar_prefix}_{self.info_name.upper()}"430 )431 else:432 auto_envvar_prefix = auto_envvar_prefix.upper()433 434 if auto_envvar_prefix is not None:435 auto_envvar_prefix = auto_envvar_prefix.replace("-", "_")436 437 self.auto_envvar_prefix: str | None = auto_envvar_prefix438 439 if color is None and parent is not None:440 color = parent.color441 442 #: Controls if styling output is wanted or not.443 self.color: bool | None = color444 445 if show_default is None and parent is not None:446 show_default = parent.show_default447 448 #: Show option default values when formatting help text.449 self.show_default: bool | None = show_default450 451 self._close_callbacks: list[t.Callable[[], t.Any]] = []452 self._depth = 0453 self._parameter_source: dict[str, ParameterSource] = {}454 self._exit_stack = ExitStack()455 456 @property457 def protected_args(self) -> list[str]:458 import warnings459 460 warnings.warn(461 "'protected_args' is deprecated and will be removed in Click 9.0."462 " 'args' will contain remaining unparsed tokens.",463 DeprecationWarning,464 stacklevel=2,465 )466 return self._protected_args467 468 def to_info_dict(self) -> dict[str, t.Any]:469 """Gather information that could be useful for a tool generating470 user-facing documentation. This traverses the entire CLI471 structure.472 473 .. code-block:: python474 475 with Context(cli) as ctx:476 info = ctx.to_info_dict()477 478 .. versionadded:: 8.0479 """480 return {481 "command": self.command.to_info_dict(self),482 "info_name": self.info_name,483 "allow_extra_args": self.allow_extra_args,484 "allow_interspersed_args": self.allow_interspersed_args,485 "ignore_unknown_options": self.ignore_unknown_options,486 "auto_envvar_prefix": self.auto_envvar_prefix,487 }488 489 def __enter__(self) -> Context:490 self._depth += 1491 push_context(self)492 return self493 494 def __exit__(495 self,496 exc_type: type[BaseException] | None,497 exc_value: BaseException | None,498 tb: TracebackType | None,499 ) -> bool | None:500 self._depth -= 1501 exit_result: bool | None = None502 if self._depth == 0:503 exit_result = self._close_with_exception_info(exc_type, exc_value, tb)504 pop_context()505 506 return exit_result507 508 @contextmanager509 def scope(self, cleanup: bool = True) -> cabc.Iterator[Context]:510 """This helper method can be used with the context object to promote511 it to the current thread local (see :func:`get_current_context`).512 The default behavior of this is to invoke the cleanup functions which513 can be disabled by setting `cleanup` to `False`. The cleanup514 functions are typically used for things such as closing file handles.515 516 If the cleanup is intended the context object can also be directly517 used as a context manager.518 519 Example usage::520 521 with ctx.scope():522 assert get_current_context() is ctx523 524 This is equivalent::525 526 with ctx:527 assert get_current_context() is ctx528 529 .. versionadded:: 5.0530 531 :param cleanup: controls if the cleanup functions should be run or532 not. The default is to run these functions. In533 some situations the context only wants to be534 temporarily pushed in which case this can be disabled.535 Nested pushes automatically defer the cleanup.536 """537 if not cleanup:538 self._depth += 1539 try:540 with self as rv:541 yield rv542 finally:543 if not cleanup:544 self._depth -= 1545 546 @property547 def meta(self) -> dict[str, t.Any]:548 """This is a dictionary which is shared with all the contexts549 that are nested. It exists so that click utilities can store some550 state here if they need to. It is however the responsibility of551 that code to manage this dictionary well.552 553 The keys are supposed to be unique dotted strings. For instance554 module paths are a good choice for it. What is stored in there is555 irrelevant for the operation of click. However what is important is556 that code that places data here adheres to the general semantics of557 the system.558 559 Example usage::560 561 LANG_KEY = f'{__name__}.lang'562 563 def set_language(value):564 ctx = get_current_context()565 ctx.meta[LANG_KEY] = value566 567 def get_language():568 return get_current_context().meta.get(LANG_KEY, 'en_US')569 570 .. versionadded:: 5.0571 """572 return self._meta573 574 def make_formatter(self) -> HelpFormatter:575 """Creates the :class:`~click.HelpFormatter` for the help and576 usage output.577 578 To quickly customize the formatter class used without overriding579 this method, set the :attr:`formatter_class` attribute.580 581 .. versionchanged:: 8.0582 Added the :attr:`formatter_class` attribute.583 """584 return self.formatter_class(585 width=self.terminal_width, max_width=self.max_content_width586 )587 588 def with_resource(self, context_manager: AbstractContextManager[V]) -> V:589 """Register a resource as if it were used in a ``with``590 statement. The resource will be cleaned up when the context is591 popped.592 593 Uses :meth:`contextlib.ExitStack.enter_context`. It calls the594 resource's ``__enter__()`` method and returns the result. When595 the context is popped, it closes the stack, which calls the596 resource's ``__exit__()`` method.597 598 To register a cleanup function for something that isn't a599 context manager, use :meth:`call_on_close`. Or use something600 from :mod:`contextlib` to turn it into a context manager first.601 602 .. code-block:: python603 604 @click.group()605 @click.option("--name")606 @click.pass_context607 def cli(ctx):608 ctx.obj = ctx.with_resource(connect_db(name))609 610 :param context_manager: The context manager to enter.611 :return: Whatever ``context_manager.__enter__()`` returns.612 613 .. versionadded:: 8.0614 """615 return self._exit_stack.enter_context(context_manager)616 617 def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:618 """Register a function to be called when the context tears down.619 620 This can be used to close resources opened during the script621 execution. Resources that support Python's context manager622 protocol which would be used in a ``with`` statement should be623 registered with :meth:`with_resource` instead.624 625 :param f: The function to execute on teardown.626 """627 return self._exit_stack.callback(f)628 629 def close(self) -> None:630 """Invoke all close callbacks registered with631 :meth:`call_on_close`, and exit all context managers entered632 with :meth:`with_resource`.633 """634 self._close_with_exception_info(None, None, None)635 636 def _close_with_exception_info(637 self,638 exc_type: type[BaseException] | None,639 exc_value: BaseException | None,640 tb: TracebackType | None,641 ) -> bool | None:642 """Unwind the exit stack by calling its :meth:`__exit__` providing the exception643 information to allow for exception handling by the various resources registered644 using :meth;`with_resource`645 646 :return: Whatever ``exit_stack.__exit__()`` returns.647 """648 exit_result = self._exit_stack.__exit__(exc_type, exc_value, tb)649 # In case the context is reused, create a new exit stack.650 self._exit_stack = ExitStack()651 652 return exit_result653 654 @property655 def command_path(self) -> str:656 """The computed command path. This is used for the ``usage``657 information on the help page. It's automatically created by658 combining the info names of the chain of contexts to the root.659 """660 rv = ""661 if self.info_name is not None:662 rv = self.info_name663 if self.parent is not None:664 parent_command_path = [self.parent.command_path]665 666 if isinstance(self.parent.command, Command):667 for param in self.parent.command.get_params(self):668 parent_command_path.extend(param.get_usage_pieces(self))669 670 rv = f"{' '.join(parent_command_path)} {rv}"671 return rv.lstrip()672 673 def find_root(self) -> Context:674 """Finds the outermost context."""675 node = self676 while node.parent is not None:677 node = node.parent678 return node679 680 def find_object(self, object_type: type[V]) -> V | None:681 """Finds the closest object of a given type."""682 node: Context | None = self683 684 while node is not None:685 if isinstance(node.obj, object_type):686 return node.obj687 688 node = node.parent689 690 return None691 692 def ensure_object(self, object_type: type[V]) -> V:693 """Like :meth:`find_object` but sets the innermost object to a694 new instance of `object_type` if it does not exist.695 """696 rv = self.find_object(object_type)697 if rv is None:698 self.obj = rv = object_type()699 return rv700 701 def _default_map_has(self, name: str | None) -> bool:702 """Check if :attr:`default_map` contains a real value for ``name``.703 704 Returns ``False`` when the key is absent, the map is ``None``,705 ``name`` is ``None``, or the stored value is the internal706 :data:`UNSET` sentinel.707 """708 return (709 name is not None710 and self.default_map is not None711 and name in self.default_map712 and self.default_map[name] is not UNSET713 )714 715 @t.overload716 def lookup_default(717 self, name: str, call: t.Literal[True] = True718 ) -> t.Any | None: ...719 720 @t.overload721 def lookup_default(722 self, name: str, call: t.Literal[False] = ...723 ) -> t.Any | t.Callable[[], t.Any] | None: ...724 725 def lookup_default(self, name: str, call: bool = True) -> t.Any | None:726 """Get the default for a parameter from :attr:`default_map`.727 728 :param name: Name of the parameter.729 :param call: If the default is a callable, call it. Disable to730 return the callable instead.731 732 .. versionchanged:: 8.0733 Added the ``call`` parameter.734 """735 if not self._default_map_has(name):736 return None737 738 # Assert to make the type checker happy.739 assert self.default_map is not None740 value = self.default_map[name]741 742 if call and callable(value):743 return value()744 745 return value746 747 def fail(self, message: str) -> t.NoReturn:748 """Aborts the execution of the program with a specific error749 message.750 751 :param message: the error message to fail with.752 """753 raise UsageError(message, self)754 755 def abort(self) -> t.NoReturn:756 """Aborts the script."""757 raise Abort()758 759 def exit(self, code: int = 0) -> t.NoReturn:760 """Exits the application with a given exit code.761 762 .. versionchanged:: 8.2763 Callbacks and context managers registered with :meth:`call_on_close`764 and :meth:`with_resource` are closed before exiting.765 """766 self.close()767 raise Exit(code)768 769 def get_usage(self) -> str:770 """Helper method to get formatted usage string for the current771 context and command.772 """773 return self.command.get_usage(self)774 775 def get_help(self) -> str:776 """Helper method to get formatted help page for the current777 context and command.778 """779 return self.command.get_help(self)780 781 def _make_sub_context(self, command: Command) -> Context:782 """Create a new context of the same type as this context, but783 for a new command.784 785 :meta private:786 """787 return type(self)(command, info_name=command.name, parent=self)788 789 @t.overload790 def invoke(791 self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any792 ) -> V: ...793 794 @t.overload795 def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ...796 797 def invoke(798 self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any799 ) -> t.Any | V:800 """Invokes a command callback in exactly the way it expects. There801 are two ways to invoke this method:802 803 1. the first argument can be a callback and all other arguments and804 keyword arguments are forwarded directly to the function.805 2. the first argument is a click command object. In that case all806 arguments are forwarded as well but proper click parameters807 (options and click arguments) must be keyword arguments and Click808 will fill in defaults.809 810 .. versionchanged:: 8.0811 All ``kwargs`` are tracked in :attr:`params` so they will be812 passed if :meth:`forward` is called at multiple levels.813 814 .. versionchanged:: 3.2815 A new context is created, and missing arguments use default values.816 """817 if isinstance(callback, Command):818 other_cmd = callback819 820 if other_cmd.callback is None:821 raise TypeError(822 "The given command does not have a callback that can be invoked."823 )824 else:825 callback = t.cast("t.Callable[..., V]", other_cmd.callback)826 827 ctx = self._make_sub_context(other_cmd)828 829 for param in other_cmd.params:830 if param.name not in kwargs and param.expose_value:831 default_value = param.get_default(ctx)832 # We explicitly hide the :attr:`UNSET` value to the user, as we833 # choose to make it an implementation detail. And because ``invoke``834 # has been designed as part of Click public API, we return ``None``835 # instead. Refs:836 # https://github.com/pallets/click/issues/3066837 # https://github.com/pallets/click/issues/3065838 # https://github.com/pallets/click/pull/3068839 if default_value is UNSET:840 default_value = None841 kwargs[param.name] = param.type_cast_value( # type: ignore842 ctx, default_value843 )844 845 # Track all kwargs as params, so that forward() will pass846 # them on in subsequent calls.847 ctx.params.update(kwargs)848 else:849 ctx = self850 851 with augment_usage_errors(self):852 with ctx:853 return callback(*args, **kwargs)854 855 def forward(self, cmd: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any:856 """Similar to :meth:`invoke` but fills in default keyword857 arguments from the current context if the other command expects858 it. This cannot invoke callbacks directly, only other commands.859 860 .. versionchanged:: 8.0861 All ``kwargs`` are tracked in :attr:`params` so they will be862 passed if ``forward`` is called at multiple levels.863 """864 # Can only forward to other commands, not direct callbacks.865 if not isinstance(cmd, Command):866 raise TypeError("Callback is not a command.")867 868 for param in self.params:869 if param not in kwargs:870 kwargs[param] = self.params[param]871 872 return self.invoke(cmd, *args, **kwargs)873 874 def set_parameter_source(self, name: str, source: ParameterSource) -> None:875 """Set the source of a parameter. This indicates the location876 from which the value of the parameter was obtained.877 878 :param name: The name of the parameter.879 :param source: A member of :class:`~click.core.ParameterSource`.880 """881 self._parameter_source[name] = source882 883 def get_parameter_source(self, name: str) -> ParameterSource | None:884 """Get the source of a parameter. This indicates the location885 from which the value of the parameter was obtained.886 887 This can be useful for determining when a user specified a value888 on the command line that is the same as the default value. It889 will be :attr:`~click.core.ParameterSource.DEFAULT` only if the890 value was actually taken from the default.891 892 :param name: The name of the parameter.893 :rtype: ParameterSource894 895 .. versionchanged:: 8.0896 Returns ``None`` if the parameter was not provided from any897 source.898 """899 return self._parameter_source.get(name)900 901 902class Command:903 """Commands are the basic building block of command line interfaces in904 Click. A basic command handles command line parsing and might dispatch905 more parsing to commands nested below it.906 907 :param name: the name of the command to use unless a group overrides it.908 :param context_settings: an optional dictionary with defaults that are909 passed to the context object.910 :param callback: the callback to invoke. This is optional.911 :param params: the parameters to register with this command. This can912 be either :class:`Option` or :class:`Argument` objects.913 :param help: the help string to use for this command.914 :param epilog: like the help string but it's printed at the end of the915 help page after everything else.916 :param short_help: the short help to use for this command. This is917 shown on the command listing of the parent command.918 :param add_help_option: by default each command registers a ``--help``919 option. This can be disabled by this parameter.920 :param no_args_is_help: this controls what happens if no arguments are921 provided. This option is disabled by default.922 If enabled this will add ``--help`` as argument923 if no arguments are passed924 :param hidden: hide this command from help outputs.925 :param deprecated: If ``True`` or non-empty string, issues a message926 indicating that the command is deprecated and highlights927 its deprecation in --help. The message can be customized928 by using a string as the value.929 930 .. versionchanged:: 8.2931 This is the base class for all commands, not ``BaseCommand``.932 ``deprecated`` can be set to a string as well to customize the933 deprecation message.934 935 .. versionchanged:: 8.1936 ``help``, ``epilog``, and ``short_help`` are stored unprocessed,937 all formatting is done when outputting help text, not at init,938 and is done even if not using the ``@command`` decorator.939 940 .. versionchanged:: 8.0941 Added a ``repr`` showing the command name.942 943 .. versionchanged:: 7.1944 Added the ``no_args_is_help`` parameter.945 946 .. versionchanged:: 2.0947 Added the ``context_settings`` parameter.948 """949 950 #: The context class to create with :meth:`make_context`.951 #:952 #: .. versionadded:: 8.0953 context_class: type[Context] = Context954 955 #: the default for the :attr:`Context.allow_extra_args` flag.956 allow_extra_args = False957 958 #: the default for the :attr:`Context.allow_interspersed_args` flag.959 allow_interspersed_args = True960 961 #: the default for the :attr:`Context.ignore_unknown_options` flag.962 ignore_unknown_options = False963 964 def __init__(965 self,966 name: str | None,967 context_settings: cabc.MutableMapping[str, t.Any] | None = None,968 callback: t.Callable[..., t.Any] | None = None,969 params: list[Parameter] | None = None,970 help: str | None = None,971 epilog: str | None = None,972 short_help: str | None = None,973 options_metavar: str | None = "[OPTIONS]",974 add_help_option: bool = True,975 no_args_is_help: bool = False,976 hidden: bool = False,977 deprecated: bool | str = False,978 ) -> None:979 #: the name the command thinks it has. Upon registering a command980 #: on a :class:`Group` the group will default the command name981 #: with this information. You should instead use the982 #: :class:`Context`\'s :attr:`~Context.info_name` attribute.983 self.name = name984 985 if context_settings is None:986 context_settings = {}987 988 #: an optional dictionary with defaults passed to the context.989 self.context_settings: cabc.MutableMapping[str, t.Any] = context_settings990 991 #: the callback to execute when the command fires. This might be992 #: `None` in which case nothing happens.993 self.callback = callback994 #: the list of parameters for this command in the order they995 #: should show up in the help page and execute. Eager parameters996 #: will automatically be handled before non eager ones.997 self.params: list[Parameter] = params or []998 self.help = help999 self.epilog = epilog1000 self.options_metavar = options_metavar1001 self.short_help = short_help1002 self.add_help_option = add_help_option1003 self._help_option = None1004 self.no_args_is_help = no_args_is_help1005 self.hidden = hidden1006 self.deprecated = deprecated1007 1008 def to_info_dict(self, ctx: Context) -> dict[str, t.Any]:1009 return {1010 "name": self.name,1011 "params": [param.to_info_dict() for param in self.get_params(ctx)],1012 "help": self.help,1013 "epilog": self.epilog,1014 "short_help": self.short_help,1015 "hidden": self.hidden,1016 "deprecated": self.deprecated,1017 }1018 1019 def __repr__(self) -> str:1020 return f"<{self.__class__.__name__} {self.name}>"1021 1022 def get_usage(self, ctx: Context) -> str:1023 """Formats the usage line into a string and returns it.1024 1025 Calls :meth:`format_usage` internally.1026 """1027 formatter = ctx.make_formatter()1028 self.format_usage(ctx, formatter)1029 return formatter.getvalue().rstrip("\n")1030 1031 def get_params(self, ctx: Context) -> list[Parameter]:1032 params = self.params1033 help_option = self.get_help_option(ctx)1034 1035 if help_option is not None:1036 params = [*params, help_option]1037 1038 if __debug__:1039 import warnings1040 1041 opts = [opt for param in params for opt in param.opts]1042 opts_counter = Counter(opts)1043 duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1)1044 1045 for duplicate_opt in duplicate_opts:1046 warnings.warn(1047 (1048 f"The parameter {duplicate_opt} is used more than once. "1049 "Remove its duplicate as parameters should be unique."1050 ),1051 stacklevel=3,1052 )1053 1054 return params1055 1056 def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None:1057 """Writes the usage line into the formatter.1058 1059 This is a low-level method called by :meth:`get_usage`.1060 """1061 pieces = self.collect_usage_pieces(ctx)1062 formatter.write_usage(ctx.command_path, " ".join(pieces))1063 1064 def collect_usage_pieces(self, ctx: Context) -> list[str]:1065 """Returns all the pieces that go into the usage line and returns1066 it as a list of strings.1067 """1068 rv = [self.options_metavar] if self.options_metavar else []1069 1070 for param in self.get_params(ctx):1071 rv.extend(param.get_usage_pieces(ctx))1072 1073 return rv1074 1075 def get_help_option_names(self, ctx: Context) -> list[str]:1076 """Returns the names for the help option."""1077 all_names = set(ctx.help_option_names)1078 for param in self.params:1079 all_names.difference_update(param.opts)1080 all_names.difference_update(param.secondary_opts)1081 return list(all_names)1082 1083 def get_help_option(self, ctx: Context) -> Option | None:1084 """Returns the help option object.1085 1086 Skipped if :attr:`add_help_option` is ``False``.1087 1088 .. versionchanged:: 8.1.81089 The help option is now cached to avoid creating it multiple times.1090 """1091 help_option_names = self.get_help_option_names(ctx)1092 1093 if not help_option_names or not self.add_help_option:1094 return None1095 1096 # Cache the help option object in private _help_option attribute to1097 # avoid creating it multiple times. Not doing this will break the1098 # callback odering by iter_params_for_processing(), which relies on1099 # object comparison.1100 if self._help_option is None:1101 # Avoid circular import.1102 from .decorators import help_option1103 1104 # Apply help_option decorator and pop resulting option1105 help_option(*help_option_names)(self)1106 self._help_option = self.params.pop() # type: ignore[assignment]1107 1108 return self._help_option1109 1110 def make_parser(self, ctx: Context) -> _OptionParser:1111 """Creates the underlying option parser for this command."""1112 parser = _OptionParser(ctx)1113 for param in self.get_params(ctx):1114 param.add_to_parser(parser, ctx)1115 return parser1116 1117 def get_help(self, ctx: Context) -> str:1118 """Formats the help into a string and returns it.1119 1120 Calls :meth:`format_help` internally.1121 """1122 formatter = ctx.make_formatter()1123 self.format_help(ctx, formatter)1124 return formatter.getvalue().rstrip("\n")1125 1126 def get_short_help_str(self, limit: int = 45) -> str:1127 """Gets short help for the command or makes it by shortening the1128 long help string.1129 """1130 if self.short_help:1131 text = inspect.cleandoc(self.short_help)1132 elif self.help:1133 text = make_default_short_help(self.help, limit)1134 else:1135 text = ""1136 1137 if self.deprecated:1138 deprecated_message = (1139 f"(DEPRECATED: {self.deprecated})"1140 if isinstance(self.deprecated, str)1141 else "(DEPRECATED)"1142 )1143 text = _("{text} {deprecated_message}").format(1144 text=text, deprecated_message=deprecated_message1145 )1146 1147 return text.strip()1148 1149 def format_help(self, ctx: Context, formatter: HelpFormatter) -> None:1150 """Writes the help into the formatter if it exists.1151 1152 This is a low-level method called by :meth:`get_help`.1153 1154 This calls the following methods:1155 1156 - :meth:`format_usage`1157 - :meth:`format_help_text`1158 - :meth:`format_options`1159 - :meth:`format_epilog`1160 """1161 self.format_usage(ctx, formatter)1162 self.format_help_text(ctx, formatter)1163 self.format_options(ctx, formatter)1164 self.format_epilog(ctx, formatter)1165 1166 def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None:1167 """Writes the help text to the formatter if it exists."""1168 if self.help is not None:1169 # truncate the help text to the first form feed1170 text = inspect.cleandoc(self.help).partition("\f")[0]1171 else:1172 text = ""1173 1174 if self.deprecated:1175 deprecated_message = (1176 f"(DEPRECATED: {self.deprecated})"1177 if isinstance(self.deprecated, str)1178 else "(DEPRECATED)"1179 )1180 text = _("{text} {deprecated_message}").format(1181 text=text, deprecated_message=deprecated_message1182 )1183 1184 if text:1185 formatter.write_paragraph()1186 1187 with formatter.indentation():1188 formatter.write_text(text)1189 1190 def format_options(self, ctx: Context, formatter: HelpFormatter) -> None:1191 """Writes all the options into the formatter if they exist."""1192 opts = []1193 for param in self.get_params(ctx):1194 rv = param.get_help_record(ctx)1195 if rv is not None:1196 opts.append(rv)1197 1198 if opts:1199 with formatter.section(_("Options")):1200 formatter.write_dl(opts)