Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/click/decorators.py: 23%
210 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 06:03 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-09 06:03 +0000
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 # type: ignore[return-value]
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 # 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: str | None,
147 cls: type[CmdType],
148 **attrs: t.Any,
149) -> t.Callable[[_AnyCallable], CmdType]:
150 ...
153# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...)
154@t.overload
155def command(
156 name: None = None,
157 *,
158 cls: type[CmdType],
159 **attrs: t.Any,
160) -> t.Callable[[_AnyCallable], CmdType]:
161 ...
164# variant: with optional string name, no cls argument provided.
165@t.overload
166def command(
167 name: str | None = ..., cls: None = None, **attrs: t.Any
168) -> t.Callable[[_AnyCallable], Command]:
169 ...
172def command(
173 name: str | _AnyCallable | None = None,
174 cls: type[CmdType] | None = None,
175 **attrs: t.Any,
176) -> Command | t.Callable[[_AnyCallable], Command | CmdType]:
177 r"""Creates a new :class:`Command` and uses the decorated function as
178 callback. This will also automatically attach all decorated
179 :func:`option`\s and :func:`argument`\s as parameters to the command.
181 The name of the command defaults to the name of the function, converted to
182 lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes
183 ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example,
184 ``init_data_command`` becomes ``init-data``.
186 All keyword arguments are forwarded to the underlying command class.
187 For the ``params`` argument, any decorated params are appended to
188 the end of the list.
190 Once decorated the function turns into a :class:`Command` instance
191 that can be invoked as a command line utility or be attached to a
192 command :class:`Group`.
194 :param name: The name of the command. Defaults to modifying the function's
195 name as described above.
196 :param cls: The command class to create. Defaults to :class:`Command`.
198 .. versionchanged:: 8.2
199 The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are
200 removed when generating the name.
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.Callable[[_AnyCallable], t.Any] | None = 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("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 if name is not None:
244 cmd_name = name
245 else:
246 cmd_name = f.__name__.lower().replace("_", "-")
247 cmd_left, sep, suffix = cmd_name.rpartition("-")
249 if sep and suffix in {"command", "cmd", "group", "grp"}:
250 cmd_name = cmd_left
252 cmd = cls(name=cmd_name, callback=f, params=params, **attrs)
253 cmd.__doc__ = f.__doc__
254 return cmd
256 if func is not None:
257 return decorator(func)
259 return decorator
262GrpType = t.TypeVar("GrpType", bound=Group)
265# variant: no call, directly as decorator for a function.
266@t.overload
267def group(name: _AnyCallable) -> Group:
268 ...
271# variant: with positional name and with positional or keyword cls argument:
272# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...)
273@t.overload
274def group(
275 name: str | None,
276 cls: type[GrpType],
277 **attrs: t.Any,
278) -> t.Callable[[_AnyCallable], GrpType]:
279 ...
282# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...)
283@t.overload
284def group(
285 name: None = None,
286 *,
287 cls: 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: str | None = ..., cls: None = None, **attrs: t.Any
297) -> t.Callable[[_AnyCallable], Group]:
298 ...
301def group(
302 name: str | _AnyCallable | None = None,
303 cls: type[GrpType] | None = None,
304 **attrs: t.Any,
305) -> Group | t.Callable[[_AnyCallable], 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("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(
333 *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any
334) -> t.Callable[[FC], FC]:
335 """Attaches an argument to the command. All positional arguments are
336 passed as parameter declarations to :class:`Argument`; all keyword
337 arguments are forwarded unchanged (except ``cls``).
338 This is equivalent to creating an :class:`Argument` instance manually
339 and attaching it to the :attr:`Command.params` list.
341 For the default argument class, refer to :class:`Argument` and
342 :class:`Parameter` for descriptions of parameters.
344 :param cls: the argument class to instantiate. This defaults to
345 :class:`Argument`.
346 :param param_decls: Passed as positional arguments to the constructor of
347 ``cls``.
348 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
349 """
350 if cls is None:
351 cls = Argument
353 def decorator(f: FC) -> FC:
354 _param_memo(f, cls(param_decls, **attrs))
355 return f
357 return decorator
360def option(
361 *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any
362) -> t.Callable[[FC], FC]:
363 """Attaches an option to the command. All positional arguments are
364 passed as parameter declarations to :class:`Option`; all keyword
365 arguments are forwarded unchanged (except ``cls``).
366 This is equivalent to creating an :class:`Option` instance manually
367 and attaching it to the :attr:`Command.params` list.
369 For the default option class, refer to :class:`Option` and
370 :class:`Parameter` for descriptions of parameters.
372 :param cls: the option class to instantiate. This defaults to
373 :class:`Option`.
374 :param param_decls: Passed as positional arguments to the constructor of
375 ``cls``.
376 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
377 """
378 if cls is None:
379 cls = Option
381 def decorator(f: FC) -> FC:
382 _param_memo(f, cls(param_decls, **attrs))
383 return f
385 return decorator
388def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
389 """Add a ``--yes`` option which shows a prompt before continuing if
390 not passed. If the prompt is declined, the program will exit.
392 :param param_decls: One or more option names. Defaults to the single
393 value ``"--yes"``.
394 :param kwargs: Extra arguments are passed to :func:`option`.
395 """
397 def callback(ctx: Context, param: Parameter, value: bool) -> None:
398 if not value:
399 ctx.abort()
401 if not param_decls:
402 param_decls = ("--yes",)
404 kwargs.setdefault("is_flag", True)
405 kwargs.setdefault("callback", callback)
406 kwargs.setdefault("expose_value", False)
407 kwargs.setdefault("prompt", "Do you want to continue?")
408 kwargs.setdefault("help", "Confirm the action without prompting.")
409 return option(*param_decls, **kwargs)
412def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
413 """Add a ``--password`` option which prompts for a password, hiding
414 input and asking to enter the value again for confirmation.
416 :param param_decls: One or more option names. Defaults to the single
417 value ``"--password"``.
418 :param kwargs: Extra arguments are passed to :func:`option`.
419 """
420 if not param_decls:
421 param_decls = ("--password",)
423 kwargs.setdefault("prompt", True)
424 kwargs.setdefault("confirmation_prompt", True)
425 kwargs.setdefault("hide_input", True)
426 return option(*param_decls, **kwargs)
429def version_option(
430 version: str | None = None,
431 *param_decls: str,
432 package_name: str | None = None,
433 prog_name: str | None = None,
434 message: str | None = None,
435 **kwargs: t.Any,
436) -> t.Callable[[FC], FC]:
437 """Add a ``--version`` option which immediately prints the version
438 number and exits the program.
440 If ``version`` is not provided, Click will try to detect it using
441 :func:`importlib.metadata.version` to get the version for the
442 ``package_name``.
444 If ``package_name`` is not provided, Click will try to detect it by
445 inspecting the stack frames. This will be used to detect the
446 version, so it must match the name of the installed package.
448 :param version: The version number to show. If not provided, Click
449 will try to detect it.
450 :param param_decls: One or more option names. Defaults to the single
451 value ``"--version"``.
452 :param package_name: The package name to detect the version from. If
453 not provided, Click will try to detect it.
454 :param prog_name: The name of the CLI to show in the message. If not
455 provided, it will be detected from the command.
456 :param message: The message to show. The values ``%(prog)s``,
457 ``%(package)s``, and ``%(version)s`` are available. Defaults to
458 ``"%(prog)s, version %(version)s"``.
459 :param kwargs: Extra arguments are passed to :func:`option`.
460 :raise RuntimeError: ``version`` could not be detected.
462 .. versionchanged:: 8.0
463 Add the ``package_name`` parameter, and the ``%(package)s``
464 value for messages.
466 .. versionchanged:: 8.0
467 Use :mod:`importlib.metadata` instead of ``pkg_resources``. The
468 version is detected based on the package name, not the entry
469 point name. The Python package name must match the installed
470 package name, or be passed with ``package_name=``.
471 """
472 if message is None:
473 message = _("%(prog)s, version %(version)s")
475 if version is None and package_name is None:
476 frame = inspect.currentframe()
477 f_back = frame.f_back if frame is not None else None
478 f_globals = f_back.f_globals if f_back is not None else None
479 # break reference cycle
480 # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
481 del frame
483 if f_globals is not None:
484 package_name = f_globals.get("__name__")
486 if package_name == "__main__":
487 package_name = f_globals.get("__package__")
489 if package_name:
490 package_name = package_name.partition(".")[0]
492 def callback(ctx: Context, param: Parameter, value: bool) -> None:
493 if not value or ctx.resilient_parsing:
494 return
496 nonlocal prog_name
497 nonlocal version
499 if prog_name is None:
500 prog_name = ctx.find_root().info_name
502 if version is None and package_name is not None:
503 import importlib.metadata
505 try:
506 version = importlib.metadata.version(package_name)
507 except importlib.metadata.PackageNotFoundError:
508 raise RuntimeError(
509 f"{package_name!r} is not installed. Try passing"
510 " 'package_name' instead."
511 ) from None
513 if version is None:
514 raise RuntimeError(
515 f"Could not determine the version for {package_name!r} automatically."
516 )
518 echo(
519 message % {"prog": prog_name, "package": package_name, "version": version},
520 color=ctx.color,
521 )
522 ctx.exit()
524 if not param_decls:
525 param_decls = ("--version",)
527 kwargs.setdefault("is_flag", True)
528 kwargs.setdefault("expose_value", False)
529 kwargs.setdefault("is_eager", True)
530 kwargs.setdefault("help", _("Show the version and exit."))
531 kwargs["callback"] = callback
532 return option(*param_decls, **kwargs)
535def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
536 """Add a ``--help`` option which immediately prints the help page
537 and exits the program.
539 This is usually unnecessary, as the ``--help`` option is added to
540 each command automatically unless ``add_help_option=False`` is
541 passed.
543 :param param_decls: One or more option names. Defaults to the single
544 value ``"--help"``.
545 :param kwargs: Extra arguments are passed to :func:`option`.
546 """
548 def callback(ctx: Context, param: Parameter, value: bool) -> None:
549 if not value or ctx.resilient_parsing:
550 return
552 echo(ctx.get_help(), color=ctx.color)
553 ctx.exit()
555 if not param_decls:
556 param_decls = ("--help",)
558 kwargs.setdefault("is_flag", True)
559 kwargs.setdefault("expose_value", False)
560 kwargs.setdefault("is_eager", True)
561 kwargs.setdefault("help", _("Show this message and exit."))
562 kwargs["callback"] = callback
563 return option(*param_decls, **kwargs)