Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/click/decorators.py: 42%
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
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]
24FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command])
27def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]":
28 """Marks a callback as wanting to receive the current context
29 object as first argument.
30 """
32 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R":
33 return f(get_current_context(), *args, **kwargs)
35 return update_wrapper(new_func, f)
38def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]":
39 """Similar to :func:`pass_context`, but only pass the object on the
40 context onwards (:attr:`Context.obj`). This is useful if that object
41 represents the state of a nested system.
42 """
44 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R":
45 return f(get_current_context().obj, *args, **kwargs)
47 return update_wrapper(new_func, f)
50def make_pass_decorator(
51 object_type: t.Type[T], ensure: bool = False
52) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]:
53 """Given an object type this creates a decorator that will work
54 similar to :func:`pass_obj` but instead of passing the object of the
55 current context, it will find the innermost context of type
56 :func:`object_type`.
58 This generates a decorator that works roughly like this::
60 from functools import update_wrapper
62 def decorator(f):
63 @pass_context
64 def new_func(ctx, *args, **kwargs):
65 obj = ctx.find_object(object_type)
66 return ctx.invoke(f, obj, *args, **kwargs)
67 return update_wrapper(new_func, f)
68 return decorator
70 :param object_type: the type of the object to pass.
71 :param ensure: if set to `True`, a new object will be created and
72 remembered on the context if it's not there yet.
73 """
75 def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]":
76 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R":
77 ctx = get_current_context()
79 obj: t.Optional[T]
80 if ensure:
81 obj = ctx.ensure_object(object_type)
82 else:
83 obj = ctx.find_object(object_type)
85 if obj is None:
86 raise RuntimeError(
87 "Managed to invoke callback without a context"
88 f" object of type {object_type.__name__!r}"
89 " existing."
90 )
92 return ctx.invoke(f, obj, *args, **kwargs)
94 return update_wrapper(new_func, f)
96 return decorator # type: ignore[return-value]
99def pass_meta_key(
100 key: str, *, doc_description: t.Optional[str] = None
101) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]":
102 """Create a decorator that passes a key from
103 :attr:`click.Context.meta` as the first argument to the decorated
104 function.
106 :param key: Key in ``Context.meta`` to pass.
107 :param doc_description: Description of the object being passed,
108 inserted into the decorator's docstring. Defaults to "the 'key'
109 key from Context.meta".
111 .. versionadded:: 8.0
112 """
114 def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]":
115 def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R:
116 ctx = get_current_context()
117 obj = ctx.meta[key]
118 return ctx.invoke(f, obj, *args, **kwargs)
120 return update_wrapper(new_func, f)
122 if doc_description is None:
123 doc_description = f"the {key!r} key from :attr:`click.Context.meta`"
125 decorator.__doc__ = (
126 f"Decorator that passes {doc_description} as the first argument"
127 " to the decorated function."
128 )
129 return decorator # type: ignore[return-value]
132CmdType = t.TypeVar("CmdType", bound=Command)
135# variant: no call, directly as decorator for a function.
136@t.overload
137def command(name: _AnyCallable) -> Command:
138 ...
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: t.Optional[str],
146 cls: t.Type[CmdType],
147 **attrs: t.Any,
148) -> t.Callable[[_AnyCallable], CmdType]:
149 ...
152# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...)
153@t.overload
154def command(
155 name: None = None,
156 *,
157 cls: t.Type[CmdType],
158 **attrs: t.Any,
159) -> t.Callable[[_AnyCallable], CmdType]:
160 ...
163# variant: with optional string name, no cls argument provided.
164@t.overload
165def command(
166 name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any
167) -> t.Callable[[_AnyCallable], Command]:
168 ...
171def command(
172 name: t.Union[t.Optional[str], _AnyCallable] = None,
173 cls: t.Optional[t.Type[CmdType]] = None,
174 **attrs: t.Any,
175) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]:
176 r"""Creates a new :class:`Command` and uses the decorated function as
177 callback. This will also automatically attach all decorated
178 :func:`option`\s and :func:`argument`\s as parameters to the command.
180 The name of the command defaults to the name of the function with
181 underscores replaced by dashes. If you want to change that, you can
182 pass the intended name as the first argument.
184 All keyword arguments are forwarded to the underlying command class.
185 For the ``params`` argument, any decorated params are appended to
186 the end of the list.
188 Once decorated the function turns into a :class:`Command` instance
189 that can be invoked as a command line utility or be attached to a
190 command :class:`Group`.
192 :param name: the name of the command. This defaults to the function
193 name with underscores replaced by dashes.
194 :param cls: the command class to instantiate. This defaults to
195 :class:`Command`.
197 .. versionchanged:: 8.1
198 This decorator can be applied without parentheses.
200 .. versionchanged:: 8.1
201 The ``params`` argument can be used. Decorated params are
202 appended to the end of the list.
203 """
205 func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None
207 if callable(name):
208 func = name
209 name = None
210 assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
211 assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."
213 if cls is None:
214 cls = t.cast(t.Type[CmdType], Command)
216 def decorator(f: _AnyCallable) -> CmdType:
217 if isinstance(f, Command):
218 raise TypeError("Attempted to convert a callback into a command twice.")
220 attr_params = attrs.pop("params", None)
221 params = attr_params if attr_params is not None else []
223 try:
224 decorator_params = f.__click_params__ # type: ignore
225 except AttributeError:
226 pass
227 else:
228 del f.__click_params__ # type: ignore
229 params.extend(reversed(decorator_params))
231 if attrs.get("help") is None:
232 attrs["help"] = f.__doc__
234 if t.TYPE_CHECKING:
235 assert cls is not None
236 assert not callable(name)
238 cmd = cls(
239 name=name or f.__name__.lower().replace("_", "-"),
240 callback=f,
241 params=params,
242 **attrs,
243 )
244 cmd.__doc__ = f.__doc__
245 return cmd
247 if func is not None:
248 return decorator(func)
250 return decorator
253GrpType = t.TypeVar("GrpType", bound=Group)
256# variant: no call, directly as decorator for a function.
257@t.overload
258def group(name: _AnyCallable) -> Group:
259 ...
262# variant: with positional name and with positional or keyword cls argument:
263# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...)
264@t.overload
265def group(
266 name: t.Optional[str],
267 cls: t.Type[GrpType],
268 **attrs: t.Any,
269) -> t.Callable[[_AnyCallable], GrpType]:
270 ...
273# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...)
274@t.overload
275def group(
276 name: None = None,
277 *,
278 cls: t.Type[GrpType],
279 **attrs: t.Any,
280) -> t.Callable[[_AnyCallable], GrpType]:
281 ...
284# variant: with optional string name, no cls argument provided.
285@t.overload
286def group(
287 name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any
288) -> t.Callable[[_AnyCallable], Group]:
289 ...
292def group(
293 name: t.Union[str, _AnyCallable, None] = None,
294 cls: t.Optional[t.Type[GrpType]] = None,
295 **attrs: t.Any,
296) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]:
297 """Creates a new :class:`Group` with a function as callback. This
298 works otherwise the same as :func:`command` just that the `cls`
299 parameter is set to :class:`Group`.
301 .. versionchanged:: 8.1
302 This decorator can be applied without parentheses.
303 """
304 if cls is None:
305 cls = t.cast(t.Type[GrpType], Group)
307 if callable(name):
308 return command(cls=cls, **attrs)(name)
310 return command(name, cls, **attrs)
313def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None:
314 if isinstance(f, Command):
315 f.params.append(param)
316 else:
317 if not hasattr(f, "__click_params__"):
318 f.__click_params__ = [] # type: ignore
320 f.__click_params__.append(param) # type: ignore
323def argument(
324 *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any
325) -> t.Callable[[FC], FC]:
326 """Attaches an argument to the command. All positional arguments are
327 passed as parameter declarations to :class:`Argument`; all keyword
328 arguments are forwarded unchanged (except ``cls``).
329 This is equivalent to creating an :class:`Argument` instance manually
330 and attaching it to the :attr:`Command.params` list.
332 For the default argument class, refer to :class:`Argument` and
333 :class:`Parameter` for descriptions of parameters.
335 :param cls: the argument class to instantiate. This defaults to
336 :class:`Argument`.
337 :param param_decls: Passed as positional arguments to the constructor of
338 ``cls``.
339 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
340 """
341 if cls is None:
342 cls = Argument
344 def decorator(f: FC) -> FC:
345 _param_memo(f, cls(param_decls, **attrs))
346 return f
348 return decorator
351def option(
352 *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any
353) -> t.Callable[[FC], FC]:
354 """Attaches an option to the command. All positional arguments are
355 passed as parameter declarations to :class:`Option`; all keyword
356 arguments are forwarded unchanged (except ``cls``).
357 This is equivalent to creating an :class:`Option` instance manually
358 and attaching it to the :attr:`Command.params` list.
360 For the default option class, refer to :class:`Option` and
361 :class:`Parameter` for descriptions of parameters.
363 :param cls: the option class to instantiate. This defaults to
364 :class:`Option`.
365 :param param_decls: Passed as positional arguments to the constructor of
366 ``cls``.
367 :param attrs: Passed as keyword arguments to the constructor of ``cls``.
368 """
369 if cls is None:
370 cls = Option
372 def decorator(f: FC) -> FC:
373 _param_memo(f, cls(param_decls, **attrs))
374 return f
376 return decorator
379def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
380 """Add a ``--yes`` option which shows a prompt before continuing if
381 not passed. If the prompt is declined, the program will exit.
383 :param param_decls: One or more option names. Defaults to the single
384 value ``"--yes"``.
385 :param kwargs: Extra arguments are passed to :func:`option`.
386 """
388 def callback(ctx: Context, param: Parameter, value: bool) -> None:
389 if not value:
390 ctx.abort()
392 if not param_decls:
393 param_decls = ("--yes",)
395 kwargs.setdefault("is_flag", True)
396 kwargs.setdefault("callback", callback)
397 kwargs.setdefault("expose_value", False)
398 kwargs.setdefault("prompt", "Do you want to continue?")
399 kwargs.setdefault("help", "Confirm the action without prompting.")
400 return option(*param_decls, **kwargs)
403def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
404 """Add a ``--password`` option which prompts for a password, hiding
405 input and asking to enter the value again for confirmation.
407 :param param_decls: One or more option names. Defaults to the single
408 value ``"--password"``.
409 :param kwargs: Extra arguments are passed to :func:`option`.
410 """
411 if not param_decls:
412 param_decls = ("--password",)
414 kwargs.setdefault("prompt", True)
415 kwargs.setdefault("confirmation_prompt", True)
416 kwargs.setdefault("hide_input", True)
417 return option(*param_decls, **kwargs)
420def version_option(
421 version: t.Optional[str] = None,
422 *param_decls: str,
423 package_name: t.Optional[str] = None,
424 prog_name: t.Optional[str] = None,
425 message: t.Optional[str] = None,
426 **kwargs: t.Any,
427) -> t.Callable[[FC], FC]:
428 """Add a ``--version`` option which immediately prints the version
429 number and exits the program.
431 If ``version`` is not provided, Click will try to detect it using
432 :func:`importlib.metadata.version` to get the version for the
433 ``package_name``. On Python < 3.8, the ``importlib_metadata``
434 backport must be installed.
436 If ``package_name`` is not provided, Click will try to detect it by
437 inspecting the stack frames. This will be used to detect the
438 version, so it must match the name of the installed package.
440 :param version: The version number to show. If not provided, Click
441 will try to detect it.
442 :param param_decls: One or more option names. Defaults to the single
443 value ``"--version"``.
444 :param package_name: The package name to detect the version from. If
445 not provided, Click will try to detect it.
446 :param prog_name: The name of the CLI to show in the message. If not
447 provided, it will be detected from the command.
448 :param message: The message to show. The values ``%(prog)s``,
449 ``%(package)s``, and ``%(version)s`` are available. Defaults to
450 ``"%(prog)s, version %(version)s"``.
451 :param kwargs: Extra arguments are passed to :func:`option`.
452 :raise RuntimeError: ``version`` could not be detected.
454 .. versionchanged:: 8.0
455 Add the ``package_name`` parameter, and the ``%(package)s``
456 value for messages.
458 .. versionchanged:: 8.0
459 Use :mod:`importlib.metadata` instead of ``pkg_resources``. The
460 version is detected based on the package name, not the entry
461 point name. The Python package name must match the installed
462 package name, or be passed with ``package_name=``.
463 """
464 if message is None:
465 message = _("%(prog)s, version %(version)s")
467 if version is None and package_name is None:
468 frame = inspect.currentframe()
469 f_back = frame.f_back if frame is not None else None
470 f_globals = f_back.f_globals if f_back is not None else None
471 # break reference cycle
472 # https://docs.python.org/3/library/inspect.html#the-interpreter-stack
473 del frame
475 if f_globals is not None:
476 package_name = f_globals.get("__name__")
478 if package_name == "__main__":
479 package_name = f_globals.get("__package__")
481 if package_name:
482 package_name = package_name.partition(".")[0]
484 def callback(ctx: Context, param: Parameter, value: bool) -> None:
485 if not value or ctx.resilient_parsing:
486 return
488 nonlocal prog_name
489 nonlocal version
491 if prog_name is None:
492 prog_name = ctx.find_root().info_name
494 if version is None and package_name is not None:
495 metadata: t.Optional[types.ModuleType]
497 try:
498 from importlib import metadata # type: ignore
499 except ImportError:
500 # Python < 3.8
501 import importlib_metadata as metadata # type: ignore
503 try:
504 version = metadata.version(package_name) # type: ignore
505 except metadata.PackageNotFoundError: # type: ignore
506 raise RuntimeError(
507 f"{package_name!r} is not installed. Try passing"
508 " 'package_name' instead."
509 ) from None
511 if version is None:
512 raise RuntimeError(
513 f"Could not determine the version for {package_name!r} automatically."
514 )
516 echo(
517 message % {"prog": prog_name, "package": package_name, "version": version},
518 color=ctx.color,
519 )
520 ctx.exit()
522 if not param_decls:
523 param_decls = ("--version",)
525 kwargs.setdefault("is_flag", True)
526 kwargs.setdefault("expose_value", False)
527 kwargs.setdefault("is_eager", True)
528 kwargs.setdefault("help", _("Show the version and exit."))
529 kwargs["callback"] = callback
530 return option(*param_decls, **kwargs)
533def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]:
534 """Add a ``--help`` option which immediately prints the help page
535 and exits the program.
537 This is usually unnecessary, as the ``--help`` option is added to
538 each command automatically unless ``add_help_option=False`` is
539 passed.
541 :param param_decls: One or more option names. Defaults to the single
542 value ``"--help"``.
543 :param kwargs: Extra arguments are passed to :func:`option`.
544 """
546 def callback(ctx: Context, param: Parameter, value: bool) -> None:
547 if not value or ctx.resilient_parsing:
548 return
550 echo(ctx.get_help(), color=ctx.color)
551 ctx.exit()
553 if not param_decls:
554 param_decls = ("--help",)
556 kwargs.setdefault("is_flag", True)
557 kwargs.setdefault("expose_value", False)
558 kwargs.setdefault("is_eager", True)
559 kwargs.setdefault("help", _("Show this message and exit."))
560 kwargs["callback"] = callback
561 return option(*param_decls, **kwargs)