Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/click/decorators.py: 23%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import inspect
4import typing as t
5from functools import update_wrapper
6from gettext import gettext as _
8from .core import Argument
9from .core import Command
10from .core import Context
11from .core import Group
12from .core import Option
13from .core import Parameter
14from .globals import get_current_context
15from .utils import echo
17if t.TYPE_CHECKING:
18 import typing_extensions as te
20 P = te.ParamSpec("P")
22R = t.TypeVar("R")
23T = t.TypeVar("T")
24_AnyCallable = t.Callable[..., t.Any]
25FC = t.TypeVar("FC", bound="_AnyCallable | Command")
28def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]:
29 """Marks a callback as wanting to receive the current context
30 object as first argument.
31 """
33 def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
34 return f(get_current_context(), *args, **kwargs)
36 return update_wrapper(new_func, f)
39def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
40 """Similar to :func:`pass_context`, but only pass the object on the
41 context onwards (:attr:`Context.obj`). This is useful if that object
42 represents the state of a nested system.
43 """
45 def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
46 return f(get_current_context().obj, *args, **kwargs)
48 return update_wrapper(new_func, f)
51def make_pass_decorator(
52 object_type: type[T], ensure: bool = False
53) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]:
54 """Given an object type this creates a decorator that will work
55 similar to :func:`pass_obj` but instead of passing the object of the
56 current context, it will find the innermost context of type
57 :func:`object_type`.
59 This generates a decorator that works roughly like this::
61 from functools import update_wrapper
63 def decorator(f):
64 @pass_context
65 def new_func(ctx, *args, **kwargs):
66 obj = ctx.find_object(object_type)
67 return ctx.invoke(f, obj, *args, **kwargs)
68 return update_wrapper(new_func, f)
69 return decorator
71 :param object_type: the type of the object to pass.
72 :param ensure: if set to `True`, a new object will be created and
73 remembered on the context if it's not there yet.
74 """
76 def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
77 def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
78 ctx = get_current_context()
80 obj: T | None
81 if ensure:
82 obj = ctx.ensure_object(object_type)
83 else:
84 obj = ctx.find_object(object_type)
86 if obj is None:
87 raise RuntimeError(
88 "Managed to invoke callback without a context"
89 f" object of type {object_type.__name__!r}"
90 " existing."
91 )
93 return ctx.invoke(f, obj, *args, **kwargs)
95 return update_wrapper(new_func, f)
97 return decorator
100def pass_meta_key(
101 key: str, *, doc_description: str | None = None
102) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]:
103 """Create a decorator that passes a key from
104 :attr:`click.Context.meta` as the first argument to the decorated
105 function.
107 :param key: Key in ``Context.meta`` to pass.
108 :param doc_description: Description of the object being passed,
109 inserted into the decorator's docstring. Defaults to "the 'key'
110 key from Context.meta".
112 .. versionadded:: 8.0
113 """
115 def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]:
116 def new_func(*args: P.args, **kwargs: P.kwargs) -> R:
117 ctx = get_current_context()
118 obj = ctx.meta[key]
119 return ctx.invoke(f, obj, *args, **kwargs)
121 return update_wrapper(new_func, f)
123 if doc_description is None:
124 doc_description = f"the {key!r} key from :attr:`click.Context.meta`"
126 decorator.__doc__ = (
127 f"Decorator that passes {doc_description} as the first argument"
128 " to the decorated function."
129 )
130 return decorator
133CmdType = t.TypeVar("CmdType", bound=Command)
136# variant: no call, directly as decorator for a function.
137@t.overload
138def command(name: _AnyCallable) -> Command: ...
141# variant: with positional name and with positional or keyword cls argument:
142# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...)
143@t.overload
144def command(
145 name: str | None,
146 cls: type[CmdType],
147 **attrs: t.Any,
148) -> t.Callable[[_AnyCallable], CmdType]: ...
151# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...)
152@t.overload
153def command(
154 name: None = None,
155 *,
156 cls: type[CmdType],
157 **attrs: t.Any,
158) -> t.Callable[[_AnyCallable], CmdType]: ...
161# variant: with optional string name, no cls argument provided.
162@t.overload
163def command(
164 name: str | None = ..., cls: None = None, **attrs: t.Any
165) -> t.Callable[[_AnyCallable], Command]: ...
168def command(
169 name: str | _AnyCallable | None = None,
170 cls: type[CmdType] | None = None,
171 **attrs: t.Any,
172) -> Command | t.Callable[[_AnyCallable], Command | CmdType]:
173 r"""Creates a new :class:`Command` and uses the decorated function as
174 callback. This will also automatically attach all decorated
175 :func:`option`\s and :func:`argument`\s as parameters to the command.
177 The name of the command defaults to the name of the function, converted to
178 lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes
179 ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example,
180 ``init_data_command`` becomes ``init-data``.
182 All keyword arguments are forwarded to the underlying command class.
183 For the ``params`` argument, any decorated params are appended to
184 the end of the list.
186 Once decorated the function turns into a :class:`Command` instance
187 that can be invoked as a command line utility or be attached to a
188 command :class:`Group`.
190 :param name: The name of the command. Defaults to modifying the function's
191 name as described above.
192 :param cls: The command class to create. Defaults to :class:`Command`.
194 .. versionchanged:: 8.2
195 The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are
196 removed when generating the name.
198 .. versionchanged:: 8.1
199 This decorator can be applied without parentheses.
201 .. versionchanged:: 8.1
202 The ``params`` argument can be used. Decorated params are
203 appended to the end of the list.
204 """
206 func: t.Callable[[_AnyCallable], t.Any] | None = None
208 if callable(name):
209 func = name
210 name = None
211 assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
212 assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."
214 if cls is None:
215 cls = t.cast("type[CmdType]", Command)
217 def decorator(f: _AnyCallable) -> CmdType:
218 if isinstance(f, Command):
219 raise TypeError("Attempted to convert a callback into a command twice.")
221 attr_params = attrs.pop("params", None)
222 params = attr_params if attr_params is not None else []
224 try:
225 decorator_params = f.__click_params__ # type: ignore
226 except AttributeError:
227 pass
228 else:
229 del f.__click_params__ # type: ignore
230 params.extend(reversed(decorator_params))
232 if attrs.get("help") is None:
233 attrs["help"] = f.__doc__
235 if t.TYPE_CHECKING:
236 assert cls is not None
237 assert not callable(name)
239 if name is not None:
240 cmd_name = name
241 else:
242 cmd_name = f.__name__.lower().replace("_", "-")
243 cmd_left, sep, suffix = cmd_name.rpartition("-")
245 if sep and suffix in {"command", "cmd", "group", "grp"}:
246 cmd_name = cmd_left
248 cmd = cls(name=cmd_name, callback=f, params=params, **attrs)
249 cmd.__doc__ = f.__doc__
250 return cmd
252 if func is not None:
253 return decorator(func)
255 return decorator
258GrpType = t.TypeVar("GrpType", bound=Group)
261# variant: no call, directly as decorator for a function.
262@t.overload
263def group(name: _AnyCallable) -> Group: ...
266# variant: with positional name and with positional or keyword cls argument:
267# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...)
268@t.overload
269def group(
270 name: str | None,
271 cls: type[GrpType],
272 **attrs: t.Any,
273) -> t.Callable[[_AnyCallable], GrpType]: ...
276# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...)
277@t.overload
278def group(
279 name: None = None,
280 *,
281 cls: type[GrpType],
282 **attrs: t.Any,
283) -> t.Callable[[_AnyCallable], GrpType]: ...
286# variant: with optional string name, no cls argument provided.
287@t.overload
288def group(
289 name: str | None = ..., cls: None = None, **attrs: t.Any
290) -> t.Callable[[_AnyCallable], Group]: ...
293def group(
294 name: str | _AnyCallable | None = None,
295 cls: type[GrpType] | None = None,
296 **attrs: t.Any,
297) -> Group | t.Callable[[_AnyCallable], Group | GrpType]:
298 """Creates a new :class:`Group` with a function as callback. This
299 works otherwise the same as :func:`command` just that the `cls`
300 parameter is set to :class:`Group`.
302 .. versionchanged:: 8.1
303 This decorator can be applied without parentheses.
304 """
305 if cls is None:
306 cls = t.cast("type[GrpType]", Group)
308 if callable(name):
309 return command(cls=cls, **attrs)(name)
311 return command(name, cls, **attrs)
314def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None:
315 if isinstance(f, Command):
316 f.params.append(param)
317 else:
318 if not hasattr(f, "__click_params__"):
319 f.__click_params__ = [] # type: ignore
321 f.__click_params__.append(param) # type: ignore
324def argument(
325 *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any
326) -> t.Callable[[FC], FC]:
327 """Attaches an argument to the command. All positional arguments are
328 passed as parameter declarations to :class:`Argument`; all keyword
329 arguments are forwarded unchanged (except ``cls``).
330 This is equivalent to creating an :class:`Argument` instance manually
331 and attaching it to the :attr:`Command.params` list.
333 For the default argument class, refer to :class:`Argument` and
334 :class:`Parameter` for descriptions of parameters.
336 :param cls: the argument class to instantiate. This defaults to
337 :class:`Argument`.
338 :param param_decls: Passed as positional arguments to the constructor of
339 ``cls``.
340 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
341 """
342 if cls is None:
343 cls = Argument
345 def decorator(f: FC) -> FC:
346 _param_memo(f, cls(param_decls, **attrs))
347 return f
349 return decorator
352def option(
353 *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any
354) -> t.Callable[[FC], FC]:
355 """Attaches an option to the command. All positional arguments are
356 passed as parameter declarations to :class:`Option`; all keyword
357 arguments are forwarded unchanged (except ``cls``).
358 This is equivalent to creating an :class:`Option` instance manually
359 and attaching it to the :attr:`Command.params` list.
361 For the default option class, refer to :class:`Option` and
362 :class:`Parameter` for descriptions of parameters.
364 :param cls: the option class to instantiate. This defaults to
365 :class:`Option`.
366 :param param_decls: Passed as positional arguments to the constructor of
367 ``cls``.
368 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
369 """
370 if cls is None:
371 cls = Option
373 def decorator(f: FC) -> FC:
374 _param_memo(f, cls(param_decls, **attrs))
375 return f
377 return decorator
380def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
381 """Add a ``--yes`` option which shows a prompt before continuing if
382 not passed. If the prompt is declined, the program will exit.
384 :param param_decls: One or more option names. Defaults to the single
385 value ``"--yes"``.
386 :param kwargs: Extra arguments are passed to :func:`option`.
387 """
389 def callback(ctx: Context, param: Parameter, value: bool) -> None:
390 if not value:
391 ctx.abort()
393 if not param_decls:
394 param_decls = ("--yes",)
396 kwargs.setdefault("is_flag", True)
397 kwargs.setdefault("callback", callback)
398 kwargs.setdefault("expose_value", False)
399 kwargs.setdefault("prompt", _("Do you want to continue?"))
400 kwargs.setdefault("help", _("Confirm the action without prompting."))
401 return option(*param_decls, **kwargs)
404def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
405 """Add a ``--password`` option which prompts for a password, hiding
406 input and asking to enter the value again for confirmation.
408 :param param_decls: One or more option names. Defaults to the single
409 value ``"--password"``.
410 :param kwargs: Extra arguments are passed to :func:`option`.
411 """
412 if not param_decls:
413 param_decls = ("--password",)
415 kwargs.setdefault("prompt", True)
416 kwargs.setdefault("confirmation_prompt", True)
417 kwargs.setdefault("hide_input", True)
418 return option(*param_decls, **kwargs)
421def version_option(
422 version: str | None = None,
423 *param_decls: str,
424 package_name: str | None = None,
425 prog_name: str | None = None,
426 message: str | None = None,
427 **kwargs: t.Any,
428) -> t.Callable[[FC], FC]:
429 """Add a ``--version`` option which immediately prints the version
430 number and exits the program.
432 If ``version`` is not provided, Click will try to detect it using
433 :func:`importlib.metadata.version` to get the version for the
434 ``package_name``.
436 If ``package_name`` is not provided, Click will try to detect it by
437 inspecting the stack frames. If the detected (or given) name does
438 not match an installed distribution, Click resolves it as an import
439 (top-level module) name via
440 :func:`importlib.metadata.packages_distributions`, so e.g. ``PIL``
441 resolves to the ``Pillow`` distribution.
443 .. note::
444 The parameters and message variables accepted by this option are
445 frozen: no new slots will be added, to keep the common case simple
446 and predictable. If you need values it does not expose, such as a
447 file path, the Python version, or git metadata, use
448 :func:`custom_version_option` to render the output yourself.
450 Rationale: `discussion #3527
451 <https://github.com/pallets/click/discussions/3527>`_.
453 :param version: The version number to show. If not provided, Click
454 will try to detect it.
455 :param param_decls: One or more option names. Defaults to the single
456 value ``"--version"``.
457 :param package_name: The package name to detect the version from. If
458 not provided, Click will try to detect it.
459 :param prog_name: The name of the CLI to show in the message. If not
460 provided, it will be detected from the command.
461 :param message: The message to show. The values ``%(prog)s``,
462 ``%(package)s``, and ``%(version)s`` are available. Defaults to
463 ``"%(prog)s, version %(version)s"``.
464 :param kwargs: Extra arguments are passed to :func:`option`.
465 :raise RuntimeError: ``version`` could not be detected.
467 .. versionchanged:: 8.0
468 Add the ``package_name`` parameter, and the ``%(package)s``
469 value for messages.
471 .. versionchanged:: 8.0
472 Use :mod:`importlib.metadata` instead of ``pkg_resources``. The
473 version is detected based on the package name, not the entry
474 point name. The Python package name must match the installed
475 package name, or be passed with ``package_name=``.
477 .. versionchanged:: 8.4.2
478 When ``package_name`` does not match an installed distribution,
479 Click now resolves it as an import (top-level module).
480 """
481 if message is None:
482 message = _("%(prog)s, version %(version)s")
484 if version is None and package_name is None:
485 frame = inspect.currentframe()
486 f_back = frame.f_back if frame is not None else None
487 f_globals = f_back.f_globals if f_back is not None else None
488 # break reference cycle
489 # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
490 del frame
492 if f_globals is not None:
493 package_name = f_globals.get("__name__")
495 if package_name == "__main__":
496 package_name = f_globals.get("__package__")
498 if package_name:
499 package_name = package_name.partition(".")[0]
501 def callback(ctx: Context, param: Parameter, value: bool) -> None:
502 if not value or ctx.resilient_parsing:
503 return
505 nonlocal prog_name
506 nonlocal version
507 nonlocal package_name
509 if prog_name is None:
510 prog_name = ctx.find_root().info_name
512 if version is None and package_name is not None:
513 import importlib.metadata
515 try:
516 version = importlib.metadata.version(package_name)
517 except importlib.metadata.PackageNotFoundError:
518 # The given name didn't match an installed distribution.
519 # Try resolving it as an import (top-level module) name,
520 # e.g. ``PIL`` is provided by the ``Pillow`` distribution.
521 distributions = importlib.metadata.packages_distributions().get(
522 package_name, []
523 )
524 if len(distributions) == 1:
525 package_name = distributions[0]
526 version = importlib.metadata.version(package_name)
527 elif len(distributions) > 1:
528 raise RuntimeError(
529 f"{package_name!r} maps to multiple installed"
530 f" distributions ({', '.join(distributions)})."
531 " Pass 'package_name' to disambiguate."
532 ) from None
533 else:
534 raise RuntimeError(
535 f"{package_name!r} is not installed. Try passing"
536 " 'package_name' instead."
537 ) from None
539 if version is None:
540 raise RuntimeError(
541 f"Could not determine the version for {package_name!r} automatically."
542 )
544 echo(
545 message % {"prog": prog_name, "package": package_name, "version": version},
546 color=ctx.color,
547 )
548 ctx.exit()
550 if not param_decls:
551 param_decls = ("--version",)
553 kwargs.setdefault("is_flag", True)
554 kwargs.setdefault("expose_value", False)
555 kwargs.setdefault("is_eager", True)
556 kwargs.setdefault("help", _("Show the version and exit."))
557 kwargs["callback"] = callback
558 return option(*param_decls, **kwargs)
561def custom_version_option(
562 callback: t.Callable[[Context], str],
563 *param_decls: str,
564 **kwargs: t.Any,
565) -> t.Callable[[FC], FC]:
566 """Add a ``--version`` option whose output is produced by ``callback``.
568 This is the customizable companion to :func:`version_option`. Where
569 :func:`version_option` is intentionally limited to a fixed message and
570 a small set of values, this option calls ``callback`` to build the
571 whole string to print. Use it when you need values that
572 :func:`version_option` does not expose, such as a file path, the
573 Python version, or git metadata.
575 :param callback: Called with the current :class:`Context` when the
576 option is invoked. Its return value is printed, then the program
577 exits.
578 :param param_decls: One or more option names. Defaults to the single
579 value ``--version``.
580 :param kwargs: Extra arguments are passed to :func:`option`.
582 .. versionadded:: 8.5.0
583 """
585 def show_version(ctx: Context, param: Parameter, value: bool) -> None:
586 if not value or ctx.resilient_parsing:
587 return
589 echo(callback(ctx), color=ctx.color)
590 ctx.exit()
592 if not param_decls:
593 param_decls = ("--version",)
595 kwargs.setdefault("is_flag", True)
596 kwargs.setdefault("expose_value", False)
597 kwargs.setdefault("is_eager", True)
598 kwargs.setdefault("help", _("Show the version and exit."))
599 kwargs["callback"] = show_version
600 return option(*param_decls, **kwargs)
603def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
604 """Pre-configured ``--help`` option which immediately prints the help page
605 and exits the program.
607 :param param_decls: One or more option names. Defaults to the single
608 value ``"--help"``.
609 :param kwargs: Extra arguments are passed to :func:`option`.
610 """
612 def show_help(ctx: Context, param: Parameter, value: bool) -> None:
613 """Callback that print the help page on ``<stdout>`` and exits."""
614 if value and not ctx.resilient_parsing:
615 echo(ctx.get_help(), color=ctx.color)
616 ctx.exit()
618 if not param_decls:
619 param_decls = ("--help",)
621 kwargs.setdefault("is_flag", True)
622 kwargs.setdefault("expose_value", False)
623 kwargs.setdefault("is_eager", True)
624 kwargs.setdefault("help", _("Show this message and exit."))
625 kwargs.setdefault("callback", show_help)
627 return option(*param_decls, **kwargs)