Spaces:
Runtime error
Runtime error
import inspect | |
import types | |
import typing as t | |
from functools import update_wrapper | |
from gettext import gettext as _ | |
from .core import Argument | |
from .core import Command | |
from .core import Context | |
from .core import Group | |
from .core import Option | |
from .core import Parameter | |
from .globals import get_current_context | |
from .utils import echo | |
F = t.TypeVar("F", bound=t.Callable[..., t.Any]) | |
FC = t.TypeVar("FC", bound=t.Union[t.Callable[..., t.Any], Command]) | |
def pass_context(f: F) -> F: | |
"""Marks a callback as wanting to receive the current context | |
object as first argument. | |
""" | |
def new_func(*args, **kwargs): # type: ignore | |
return f(get_current_context(), *args, **kwargs) | |
return update_wrapper(t.cast(F, new_func), f) | |
def pass_obj(f: F) -> F: | |
"""Similar to :func:`pass_context`, but only pass the object on the | |
context onwards (:attr:`Context.obj`). This is useful if that object | |
represents the state of a nested system. | |
""" | |
def new_func(*args, **kwargs): # type: ignore | |
return f(get_current_context().obj, *args, **kwargs) | |
return update_wrapper(t.cast(F, new_func), f) | |
def make_pass_decorator( | |
object_type: t.Type, ensure: bool = False | |
) -> "t.Callable[[F], F]": | |
"""Given an object type this creates a decorator that will work | |
similar to :func:`pass_obj` but instead of passing the object of the | |
current context, it will find the innermost context of type | |
:func:`object_type`. | |
This generates a decorator that works roughly like this:: | |
from functools import update_wrapper | |
def decorator(f): | |
@pass_context | |
def new_func(ctx, *args, **kwargs): | |
obj = ctx.find_object(object_type) | |
return ctx.invoke(f, obj, *args, **kwargs) | |
return update_wrapper(new_func, f) | |
return decorator | |
:param object_type: the type of the object to pass. | |
:param ensure: if set to `True`, a new object will be created and | |
remembered on the context if it's not there yet. | |
""" | |
def decorator(f: F) -> F: | |
def new_func(*args, **kwargs): # type: ignore | |
ctx = get_current_context() | |
if ensure: | |
obj = ctx.ensure_object(object_type) | |
else: | |
obj = ctx.find_object(object_type) | |
if obj is None: | |
raise RuntimeError( | |
"Managed to invoke callback without a context" | |
f" object of type {object_type.__name__!r}" | |
" existing." | |
) | |
return ctx.invoke(f, obj, *args, **kwargs) | |
return update_wrapper(t.cast(F, new_func), f) | |
return decorator | |
def pass_meta_key( | |
key: str, *, doc_description: t.Optional[str] = None | |
) -> "t.Callable[[F], F]": | |
"""Create a decorator that passes a key from | |
:attr:`click.Context.meta` as the first argument to the decorated | |
function. | |
:param key: Key in ``Context.meta`` to pass. | |
:param doc_description: Description of the object being passed, | |
inserted into the decorator's docstring. Defaults to "the 'key' | |
key from Context.meta". | |
.. versionadded:: 8.0 | |
""" | |
def decorator(f: F) -> F: | |
def new_func(*args, **kwargs): # type: ignore | |
ctx = get_current_context() | |
obj = ctx.meta[key] | |
return ctx.invoke(f, obj, *args, **kwargs) | |
return update_wrapper(t.cast(F, new_func), f) | |
if doc_description is None: | |
doc_description = f"the {key!r} key from :attr:`click.Context.meta`" | |
decorator.__doc__ = ( | |
f"Decorator that passes {doc_description} as the first argument" | |
" to the decorated function." | |
) | |
return decorator | |
CmdType = t.TypeVar("CmdType", bound=Command) | |
def command( | |
__func: t.Callable[..., t.Any], | |
) -> Command: | |
... | |
def command( | |
name: t.Optional[str] = None, | |
**attrs: t.Any, | |
) -> t.Callable[..., Command]: | |
... | |
def command( | |
name: t.Optional[str] = None, | |
cls: t.Type[CmdType] = ..., | |
**attrs: t.Any, | |
) -> t.Callable[..., CmdType]: | |
... | |
def command( | |
name: t.Union[str, t.Callable[..., t.Any], None] = None, | |
cls: t.Optional[t.Type[Command]] = None, | |
**attrs: t.Any, | |
) -> t.Union[Command, t.Callable[..., Command]]: | |
r"""Creates a new :class:`Command` and uses the decorated function as | |
callback. This will also automatically attach all decorated | |
:func:`option`\s and :func:`argument`\s as parameters to the command. | |
The name of the command defaults to the name of the function with | |
underscores replaced by dashes. If you want to change that, you can | |
pass the intended name as the first argument. | |
All keyword arguments are forwarded to the underlying command class. | |
For the ``params`` argument, any decorated params are appended to | |
the end of the list. | |
Once decorated the function turns into a :class:`Command` instance | |
that can be invoked as a command line utility or be attached to a | |
command :class:`Group`. | |
:param name: the name of the command. This defaults to the function | |
name with underscores replaced by dashes. | |
:param cls: the command class to instantiate. This defaults to | |
:class:`Command`. | |
.. versionchanged:: 8.1 | |
This decorator can be applied without parentheses. | |
.. versionchanged:: 8.1 | |
The ``params`` argument can be used. Decorated params are | |
appended to the end of the list. | |
""" | |
func: t.Optional[t.Callable[..., t.Any]] = None | |
if callable(name): | |
func = name | |
name = None | |
assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." | |
assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." | |
if cls is None: | |
cls = Command | |
def decorator(f: t.Callable[..., t.Any]) -> Command: | |
if isinstance(f, Command): | |
raise TypeError("Attempted to convert a callback into a command twice.") | |
attr_params = attrs.pop("params", None) | |
params = attr_params if attr_params is not None else [] | |
try: | |
decorator_params = f.__click_params__ # type: ignore | |
except AttributeError: | |
pass | |
else: | |
del f.__click_params__ # type: ignore | |
params.extend(reversed(decorator_params)) | |
if attrs.get("help") is None: | |
attrs["help"] = f.__doc__ | |
cmd = cls( # type: ignore[misc] | |
name=name or f.__name__.lower().replace("_", "-"), # type: ignore[arg-type] | |
callback=f, | |
params=params, | |
**attrs, | |
) | |
cmd.__doc__ = f.__doc__ | |
return cmd | |
if func is not None: | |
return decorator(func) | |
return decorator | |
def group( | |
__func: t.Callable[..., t.Any], | |
) -> Group: | |
... | |
def group( | |
name: t.Optional[str] = None, | |
**attrs: t.Any, | |
) -> t.Callable[[F], Group]: | |
... | |
def group( | |
name: t.Union[str, t.Callable[..., t.Any], None] = None, **attrs: t.Any | |
) -> t.Union[Group, t.Callable[[F], Group]]: | |
"""Creates a new :class:`Group` with a function as callback. This | |
works otherwise the same as :func:`command` just that the `cls` | |
parameter is set to :class:`Group`. | |
.. versionchanged:: 8.1 | |
This decorator can be applied without parentheses. | |
""" | |
if attrs.get("cls") is None: | |
attrs["cls"] = Group | |
if callable(name): | |
grp: t.Callable[[F], Group] = t.cast(Group, command(**attrs)) | |
return grp(name) | |
return t.cast(Group, command(name, **attrs)) | |
def _param_memo(f: FC, param: Parameter) -> None: | |
if isinstance(f, Command): | |
f.params.append(param) | |
else: | |
if not hasattr(f, "__click_params__"): | |
f.__click_params__ = [] # type: ignore | |
f.__click_params__.append(param) # type: ignore | |
def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: | |
"""Attaches an argument to the command. All positional arguments are | |
passed as parameter declarations to :class:`Argument`; all keyword | |
arguments are forwarded unchanged (except ``cls``). | |
This is equivalent to creating an :class:`Argument` instance manually | |
and attaching it to the :attr:`Command.params` list. | |
:param cls: the argument class to instantiate. This defaults to | |
:class:`Argument`. | |
""" | |
def decorator(f: FC) -> FC: | |
ArgumentClass = attrs.pop("cls", None) or Argument | |
_param_memo(f, ArgumentClass(param_decls, **attrs)) | |
return f | |
return decorator | |
def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: | |
"""Attaches an option to the command. All positional arguments are | |
passed as parameter declarations to :class:`Option`; all keyword | |
arguments are forwarded unchanged (except ``cls``). | |
This is equivalent to creating an :class:`Option` instance manually | |
and attaching it to the :attr:`Command.params` list. | |
:param cls: the option class to instantiate. This defaults to | |
:class:`Option`. | |
""" | |
def decorator(f: FC) -> FC: | |
# Issue 926, copy attrs, so pre-defined options can re-use the same cls= | |
option_attrs = attrs.copy() | |
OptionClass = option_attrs.pop("cls", None) or Option | |
_param_memo(f, OptionClass(param_decls, **option_attrs)) | |
return f | |
return decorator | |
def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: | |
"""Add a ``--yes`` option which shows a prompt before continuing if | |
not passed. If the prompt is declined, the program will exit. | |
:param param_decls: One or more option names. Defaults to the single | |
value ``"--yes"``. | |
:param kwargs: Extra arguments are passed to :func:`option`. | |
""" | |
def callback(ctx: Context, param: Parameter, value: bool) -> None: | |
if not value: | |
ctx.abort() | |
if not param_decls: | |
param_decls = ("--yes",) | |
kwargs.setdefault("is_flag", True) | |
kwargs.setdefault("callback", callback) | |
kwargs.setdefault("expose_value", False) | |
kwargs.setdefault("prompt", "Do you want to continue?") | |
kwargs.setdefault("help", "Confirm the action without prompting.") | |
return option(*param_decls, **kwargs) | |
def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: | |
"""Add a ``--password`` option which prompts for a password, hiding | |
input and asking to enter the value again for confirmation. | |
:param param_decls: One or more option names. Defaults to the single | |
value ``"--password"``. | |
:param kwargs: Extra arguments are passed to :func:`option`. | |
""" | |
if not param_decls: | |
param_decls = ("--password",) | |
kwargs.setdefault("prompt", True) | |
kwargs.setdefault("confirmation_prompt", True) | |
kwargs.setdefault("hide_input", True) | |
return option(*param_decls, **kwargs) | |
def version_option( | |
version: t.Optional[str] = None, | |
*param_decls: str, | |
package_name: t.Optional[str] = None, | |
prog_name: t.Optional[str] = None, | |
message: t.Optional[str] = None, | |
**kwargs: t.Any, | |
) -> t.Callable[[FC], FC]: | |
"""Add a ``--version`` option which immediately prints the version | |
number and exits the program. | |
If ``version`` is not provided, Click will try to detect it using | |
:func:`importlib.metadata.version` to get the version for the | |
``package_name``. On Python < 3.8, the ``importlib_metadata`` | |
backport must be installed. | |
If ``package_name`` is not provided, Click will try to detect it by | |
inspecting the stack frames. This will be used to detect the | |
version, so it must match the name of the installed package. | |
:param version: The version number to show. If not provided, Click | |
will try to detect it. | |
:param param_decls: One or more option names. Defaults to the single | |
value ``"--version"``. | |
:param package_name: The package name to detect the version from. If | |
not provided, Click will try to detect it. | |
:param prog_name: The name of the CLI to show in the message. If not | |
provided, it will be detected from the command. | |
:param message: The message to show. The values ``%(prog)s``, | |
``%(package)s``, and ``%(version)s`` are available. Defaults to | |
``"%(prog)s, version %(version)s"``. | |
:param kwargs: Extra arguments are passed to :func:`option`. | |
:raise RuntimeError: ``version`` could not be detected. | |
.. versionchanged:: 8.0 | |
Add the ``package_name`` parameter, and the ``%(package)s`` | |
value for messages. | |
.. versionchanged:: 8.0 | |
Use :mod:`importlib.metadata` instead of ``pkg_resources``. The | |
version is detected based on the package name, not the entry | |
point name. The Python package name must match the installed | |
package name, or be passed with ``package_name=``. | |
""" | |
if message is None: | |
message = _("%(prog)s, version %(version)s") | |
if version is None and package_name is None: | |
frame = inspect.currentframe() | |
f_back = frame.f_back if frame is not None else None | |
f_globals = f_back.f_globals if f_back is not None else None | |
# break reference cycle | |
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack | |
del frame | |
if f_globals is not None: | |
package_name = f_globals.get("__name__") | |
if package_name == "__main__": | |
package_name = f_globals.get("__package__") | |
if package_name: | |
package_name = package_name.partition(".")[0] | |
def callback(ctx: Context, param: Parameter, value: bool) -> None: | |
if not value or ctx.resilient_parsing: | |
return | |
nonlocal prog_name | |
nonlocal version | |
if prog_name is None: | |
prog_name = ctx.find_root().info_name | |
if version is None and package_name is not None: | |
metadata: t.Optional[types.ModuleType] | |
try: | |
from importlib import metadata # type: ignore | |
except ImportError: | |
# Python < 3.8 | |
import importlib_metadata as metadata # type: ignore | |
try: | |
version = metadata.version(package_name) # type: ignore | |
except metadata.PackageNotFoundError: # type: ignore | |
raise RuntimeError( | |
f"{package_name!r} is not installed. Try passing" | |
" 'package_name' instead." | |
) from None | |
if version is None: | |
raise RuntimeError( | |
f"Could not determine the version for {package_name!r} automatically." | |
) | |
echo( | |
t.cast(str, message) | |
% {"prog": prog_name, "package": package_name, "version": version}, | |
color=ctx.color, | |
) | |
ctx.exit() | |
if not param_decls: | |
param_decls = ("--version",) | |
kwargs.setdefault("is_flag", True) | |
kwargs.setdefault("expose_value", False) | |
kwargs.setdefault("is_eager", True) | |
kwargs.setdefault("help", _("Show the version and exit.")) | |
kwargs["callback"] = callback | |
return option(*param_decls, **kwargs) | |
def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: | |
"""Add a ``--help`` option which immediately prints the help page | |
and exits the program. | |
This is usually unnecessary, as the ``--help`` option is added to | |
each command automatically unless ``add_help_option=False`` is | |
passed. | |
:param param_decls: One or more option names. Defaults to the single | |
value ``"--help"``. | |
:param kwargs: Extra arguments are passed to :func:`option`. | |
""" | |
def callback(ctx: Context, param: Parameter, value: bool) -> None: | |
if not value or ctx.resilient_parsing: | |
return | |
echo(ctx.get_help(), color=ctx.color) | |
ctx.exit() | |
if not param_decls: | |
param_decls = ("--help",) | |
kwargs.setdefault("is_flag", True) | |
kwargs.setdefault("expose_value", False) | |
kwargs.setdefault("is_eager", True) | |
kwargs.setdefault("help", _("Show this message and exit.")) | |
kwargs["callback"] = callback | |
return option(*param_decls, **kwargs) | |