Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/click/decorators.py: 24%
207 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:07 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 07:07 +0000
1import inspect
2import types
3import typing as t
4from functools import update_wrapper
5from gettext import gettext as _
7from .core import Argument
8from .core import Command
9from .core import Context
10from .core import Group
11from .core import Option
12from .core import Parameter
13from .globals import get_current_context
14from .utils import echo
16if t.TYPE_CHECKING:
17 import typing_extensions as te
19 P = te.ParamSpec("P")
21R = t.TypeVar("R")
22T = t.TypeVar("T")
23_AnyCallable = t.Callable[..., t.Any]
24_Decorator: "te.TypeAlias" = t.Callable[[T], T]
25FC = t.TypeVar("FC", bound=t.Union[_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.Any, 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: t.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.Optional[T]
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 # type: ignore[return-value]
100def pass_meta_key(
101 key: str, *, doc_description: t.Optional[str] = None
102) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, 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.Any, 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 # type: ignore[return-value]
133CmdType = t.TypeVar("CmdType", bound=Command)
136# variant: no call, directly as decorator for a function.
137@t.overload
138def command(name: _AnyCallable) -> Command:
139 ...
142# variant: with positional name and with positional or keyword cls argument:
143# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...)
144@t.overload
145def command(
146 name: t.Optional[str],
147 cls: t.Type[CmdType],
148 **attrs: t.Any,
149) -> t.Callable[[_AnyCallable], CmdType]:
150 ...
153# variant: name omitted, cls _must_ be a keyword argument, @command(cmd=CommandCls, ...)
154# The correct way to spell this overload is to use keyword-only argument syntax:
155# def command(*, cls: t.Type[CmdType], **attrs: t.Any) -> ...
156# However, mypy thinks this doesn't fit the overloaded function. Pyright does
157# accept that spelling, and the following work-around makes pyright issue a
158# warning that CmdType could be left unsolved, but mypy sees it as fine. *shrug*
159@t.overload
160def command(
161 name: None = None,
162 cls: t.Type[CmdType] = ...,
163 **attrs: t.Any,
164) -> t.Callable[[_AnyCallable], CmdType]:
165 ...
168# variant: with optional string name, no cls argument provided.
169@t.overload
170def command(
171 name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any
172) -> t.Callable[[_AnyCallable], Command]:
173 ...
176def command(
177 name: t.Union[t.Optional[str], _AnyCallable] = None,
178 cls: t.Optional[t.Type[CmdType]] = None,
179 **attrs: t.Any,
180) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]:
181 r"""Creates a new :class:`Command` and uses the decorated function as
182 callback. This will also automatically attach all decorated
183 :func:`option`\s and :func:`argument`\s as parameters to the command.
185 The name of the command defaults to the name of the function with
186 underscores replaced by dashes. If you want to change that, you can
187 pass the intended name as the first argument.
189 All keyword arguments are forwarded to the underlying command class.
190 For the ``params`` argument, any decorated params are appended to
191 the end of the list.
193 Once decorated the function turns into a :class:`Command` instance
194 that can be invoked as a command line utility or be attached to a
195 command :class:`Group`.
197 :param name: the name of the command. This defaults to the function
198 name with underscores replaced by dashes.
199 :param cls: the command class to instantiate. This defaults to
200 :class:`Command`.
202 .. versionchanged:: 8.1
203 This decorator can be applied without parentheses.
205 .. versionchanged:: 8.1
206 The ``params`` argument can be used. Decorated params are
207 appended to the end of the list.
208 """
210 func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None
212 if callable(name):
213 func = name
214 name = None
215 assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
216 assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."
218 if cls is None:
219 cls = t.cast(t.Type[CmdType], Command)
221 def decorator(f: _AnyCallable) -> CmdType:
222 if isinstance(f, Command):
223 raise TypeError("Attempted to convert a callback into a command twice.")
225 attr_params = attrs.pop("params", None)
226 params = attr_params if attr_params is not None else []
228 try:
229 decorator_params = f.__click_params__ # type: ignore
230 except AttributeError:
231 pass
232 else:
233 del f.__click_params__ # type: ignore
234 params.extend(reversed(decorator_params))
236 if attrs.get("help") is None:
237 attrs["help"] = f.__doc__
239 if t.TYPE_CHECKING:
240 assert cls is not None
241 assert not callable(name)
243 cmd = cls(
244 name=name or f.__name__.lower().replace("_", "-"),
245 callback=f,
246 params=params,
247 **attrs,
248 )
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:
264 ...
267# variant: with positional name and with positional or keyword cls argument:
268# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...)
269@t.overload
270def group(
271 name: t.Optional[str],
272 cls: t.Type[GrpType],
273 **attrs: t.Any,
274) -> t.Callable[[_AnyCallable], GrpType]:
275 ...
278# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...)
279# The _correct_ way to spell this overload is to use keyword-only argument syntax:
280# def group(*, cls: t.Type[GrpType], **attrs: t.Any) -> ...
281# However, mypy thinks this doesn't fit the overloaded function. Pyright does
282# accept that spelling, and the following work-around makes pyright issue a
283# warning that GrpType could be left unsolved, but mypy sees it as fine. *shrug*
284@t.overload
285def group(
286 name: None = None,
287 cls: t.Type[GrpType] = ...,
288 **attrs: t.Any,
289) -> t.Callable[[_AnyCallable], GrpType]:
290 ...
293# variant: with optional string name, no cls argument provided.
294@t.overload
295def group(
296 name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any
297) -> t.Callable[[_AnyCallable], Group]:
298 ...
301def group(
302 name: t.Union[str, _AnyCallable, None] = None,
303 cls: t.Optional[t.Type[GrpType]] = None,
304 **attrs: t.Any,
305) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]:
306 """Creates a new :class:`Group` with a function as callback. This
307 works otherwise the same as :func:`command` just that the `cls`
308 parameter is set to :class:`Group`.
310 .. versionchanged:: 8.1
311 This decorator can be applied without parentheses.
312 """
313 if cls is None:
314 cls = t.cast(t.Type[GrpType], Group)
316 if callable(name):
317 return command(cls=cls, **attrs)(name)
319 return command(name, cls, **attrs)
322def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None:
323 if isinstance(f, Command):
324 f.params.append(param)
325 else:
326 if not hasattr(f, "__click_params__"):
327 f.__click_params__ = [] # type: ignore
329 f.__click_params__.append(param) # type: ignore
332def argument(*param_decls: str, **attrs: t.Any) -> _Decorator[FC]:
333 """Attaches an argument to the command. All positional arguments are
334 passed as parameter declarations to :class:`Argument`; all keyword
335 arguments are forwarded unchanged (except ``cls``).
336 This is equivalent to creating an :class:`Argument` instance manually
337 and attaching it to the :attr:`Command.params` list.
339 For the default argument class, refer to :class:`Argument` and
340 :class:`Parameter` for descriptions of parameters.
342 :param cls: the argument class to instantiate. This defaults to
343 :class:`Argument`.
344 :param param_decls: Passed as positional arguments to the constructor of
345 ``cls``.
346 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
347 """
349 def decorator(f: FC) -> FC:
350 ArgumentClass = attrs.pop("cls", None) or Argument
351 _param_memo(f, ArgumentClass(param_decls, **attrs))
352 return f
354 return decorator
357def option(*param_decls: str, **attrs: t.Any) -> _Decorator[FC]:
358 """Attaches an option to the command. All positional arguments are
359 passed as parameter declarations to :class:`Option`; all keyword
360 arguments are forwarded unchanged (except ``cls``).
361 This is equivalent to creating an :class:`Option` instance manually
362 and attaching it to the :attr:`Command.params` list.
364 For the default option class, refer to :class:`Option` and
365 :class:`Parameter` for descriptions of parameters.
367 :param cls: the option class to instantiate. This defaults to
368 :class:`Option`.
369 :param param_decls: Passed as positional arguments to the constructor of
370 ``cls``.
371 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
372 """
374 def decorator(f: FC) -> FC:
375 # Issue 926, copy attrs, so pre-defined options can re-use the same cls=
376 option_attrs = attrs.copy()
377 OptionClass = option_attrs.pop("cls", None) or Option
378 _param_memo(f, OptionClass(param_decls, **option_attrs))
379 return f
381 return decorator
384def confirmation_option(*param_decls: str, **kwargs: t.Any) -> _Decorator[FC]:
385 """Add a ``--yes`` option which shows a prompt before continuing if
386 not passed. If the prompt is declined, the program will exit.
388 :param param_decls: One or more option names. Defaults to the single
389 value ``"--yes"``.
390 :param kwargs: Extra arguments are passed to :func:`option`.
391 """
393 def callback(ctx: Context, param: Parameter, value: bool) -> None:
394 if not value:
395 ctx.abort()
397 if not param_decls:
398 param_decls = ("--yes",)
400 kwargs.setdefault("is_flag", True)
401 kwargs.setdefault("callback", callback)
402 kwargs.setdefault("expose_value", False)
403 kwargs.setdefault("prompt", "Do you want to continue?")
404 kwargs.setdefault("help", "Confirm the action without prompting.")
405 return option(*param_decls, **kwargs)
408def password_option(*param_decls: str, **kwargs: t.Any) -> _Decorator[FC]:
409 """Add a ``--password`` option which prompts for a password, hiding
410 input and asking to enter the value again for confirmation.
412 :param param_decls: One or more option names. Defaults to the single
413 value ``"--password"``.
414 :param kwargs: Extra arguments are passed to :func:`option`.
415 """
416 if not param_decls:
417 param_decls = ("--password",)
419 kwargs.setdefault("prompt", True)
420 kwargs.setdefault("confirmation_prompt", True)
421 kwargs.setdefault("hide_input", True)
422 return option(*param_decls, **kwargs)
425def version_option(
426 version: t.Optional[str] = None,
427 *param_decls: str,
428 package_name: t.Optional[str] = None,
429 prog_name: t.Optional[str] = None,
430 message: t.Optional[str] = None,
431 **kwargs: t.Any,
432) -> _Decorator[FC]:
433 """Add a ``--version`` option which immediately prints the version
434 number and exits the program.
436 If ``version`` is not provided, Click will try to detect it using
437 :func:`importlib.metadata.version` to get the version for the
438 ``package_name``. On Python < 3.8, the ``importlib_metadata``
439 backport must be installed.
441 If ``package_name`` is not provided, Click will try to detect it by
442 inspecting the stack frames. This will be used to detect the
443 version, so it must match the name of the installed package.
445 :param version: The version number to show. If not provided, Click
446 will try to detect it.
447 :param param_decls: One or more option names. Defaults to the single
448 value ``"--version"``.
449 :param package_name: The package name to detect the version from. If
450 not provided, Click will try to detect it.
451 :param prog_name: The name of the CLI to show in the message. If not
452 provided, it will be detected from the command.
453 :param message: The message to show. The values ``%(prog)s``,
454 ``%(package)s``, and ``%(version)s`` are available. Defaults to
455 ``"%(prog)s, version %(version)s"``.
456 :param kwargs: Extra arguments are passed to :func:`option`.
457 :raise RuntimeError: ``version`` could not be detected.
459 .. versionchanged:: 8.0
460 Add the ``package_name`` parameter, and the ``%(package)s``
461 value for messages.
463 .. versionchanged:: 8.0
464 Use :mod:`importlib.metadata` instead of ``pkg_resources``. The
465 version is detected based on the package name, not the entry
466 point name. The Python package name must match the installed
467 package name, or be passed with ``package_name=``.
468 """
469 if message is None:
470 message = _("%(prog)s, version %(version)s")
472 if version is None and package_name is None:
473 frame = inspect.currentframe()
474 f_back = frame.f_back if frame is not None else None
475 f_globals = f_back.f_globals if f_back is not None else None
476 # break reference cycle
477 # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
478 del frame
480 if f_globals is not None:
481 package_name = f_globals.get("__name__")
483 if package_name == "__main__":
484 package_name = f_globals.get("__package__")
486 if package_name:
487 package_name = package_name.partition(".")[0]
489 def callback(ctx: Context, param: Parameter, value: bool) -> None:
490 if not value or ctx.resilient_parsing:
491 return
493 nonlocal prog_name
494 nonlocal version
496 if prog_name is None:
497 prog_name = ctx.find_root().info_name
499 if version is None and package_name is not None:
500 metadata: t.Optional[types.ModuleType]
502 try:
503 from importlib import metadata # type: ignore
504 except ImportError:
505 # Python < 3.8
506 import importlib_metadata as metadata # type: ignore
508 try:
509 version = metadata.version(package_name) # type: ignore
510 except metadata.PackageNotFoundError: # type: ignore
511 raise RuntimeError(
512 f"{package_name!r} is not installed. Try passing"
513 " 'package_name' instead."
514 ) from None
516 if version is None:
517 raise RuntimeError(
518 f"Could not determine the version for {package_name!r} automatically."
519 )
521 echo(
522 t.cast(str, message)
523 % {"prog": prog_name, "package": package_name, "version": version},
524 color=ctx.color,
525 )
526 ctx.exit()
528 if not param_decls:
529 param_decls = ("--version",)
531 kwargs.setdefault("is_flag", True)
532 kwargs.setdefault("expose_value", False)
533 kwargs.setdefault("is_eager", True)
534 kwargs.setdefault("help", _("Show the version and exit."))
535 kwargs["callback"] = callback
536 return option(*param_decls, **kwargs)
539def help_option(*param_decls: str, **kwargs: t.Any) -> _Decorator[FC]:
540 """Add a ``--help`` option which immediately prints the help page
541 and exits the program.
543 This is usually unnecessary, as the ``--help`` option is added to
544 each command automatically unless ``add_help_option=False`` is
545 passed.
547 :param param_decls: One or more option names. Defaults to the single
548 value ``"--help"``.
549 :param kwargs: Extra arguments are passed to :func:`option`.
550 """
552 def callback(ctx: Context, param: Parameter, value: bool) -> None:
553 if not value or ctx.resilient_parsing:
554 return
556 echo(ctx.get_help(), color=ctx.color)
557 ctx.exit()
559 if not param_decls:
560 param_decls = ("--help",)
562 kwargs.setdefault("is_flag", True)
563 kwargs.setdefault("expose_value", False)
564 kwargs.setdefault("is_eager", True)
565 kwargs.setdefault("help", _("Show this message and exit."))
566 kwargs["callback"] = callback
567 return option(*param_decls, **kwargs)