123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- import inspect
- import sys
- from functools import update_wrapper
- from ._compat import iteritems
- from ._unicodefun import _check_for_unicode_literals
- from .core import Argument
- from .core import Command
- from .core import Group
- from .core import Option
- from .globals import get_current_context
- from .utils import echo
- def pass_context(f):
- """Marks a callback as wanting to receive the current context
- object as first argument.
- """
- def new_func(*args, **kwargs):
- return f(get_current_context(), *args, **kwargs)
- return update_wrapper(new_func, f)
- def pass_obj(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):
- return f(get_current_context().obj, *args, **kwargs)
- return update_wrapper(new_func, f)
- def make_pass_decorator(object_type, ensure=False):
- """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):
- def new_func(*args, **kwargs):
- 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"
- " object of type '{}' existing".format(object_type.__name__)
- )
- return ctx.invoke(f, obj, *args, **kwargs)
- return update_wrapper(new_func, f)
- return decorator
- def _make_command(f, name, attrs, cls):
- if isinstance(f, Command):
- raise TypeError("Attempted to convert a callback into a command twice.")
- try:
- params = f.__click_params__
- params.reverse()
- del f.__click_params__
- except AttributeError:
- params = []
- help = attrs.get("help")
- if help is None:
- help = inspect.getdoc(f)
- if isinstance(help, bytes):
- help = help.decode("utf-8")
- else:
- help = inspect.cleandoc(help)
- attrs["help"] = help
- _check_for_unicode_literals()
- return cls(
- name=name or f.__name__.lower().replace("_", "-"),
- callback=f,
- params=params,
- **attrs
- )
- def command(name=None, cls=None, **attrs):
- 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.
- 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`.
- """
- if cls is None:
- cls = Command
- def decorator(f):
- cmd = _make_command(f, name, attrs, cls)
- cmd.__doc__ = f.__doc__
- return cmd
- return decorator
- def group(name=None, **attrs):
- """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`.
- """
- attrs.setdefault("cls", Group)
- return command(name, **attrs)
- def _param_memo(f, param):
- if isinstance(f, Command):
- f.params.append(param)
- else:
- if not hasattr(f, "__click_params__"):
- f.__click_params__ = []
- f.__click_params__.append(param)
- def argument(*param_decls, **attrs):
- """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):
- ArgumentClass = attrs.pop("cls", Argument)
- _param_memo(f, ArgumentClass(param_decls, **attrs))
- return f
- return decorator
- def option(*param_decls, **attrs):
- """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):
- # Issue 926, copy attrs, so pre-defined options can re-use the same cls=
- option_attrs = attrs.copy()
- if "help" in option_attrs:
- option_attrs["help"] = inspect.cleandoc(option_attrs["help"])
- OptionClass = option_attrs.pop("cls", Option)
- _param_memo(f, OptionClass(param_decls, **option_attrs))
- return f
- return decorator
- def confirmation_option(*param_decls, **attrs):
- """Shortcut for confirmation prompts that can be ignored by passing
- ``--yes`` as parameter.
- This is equivalent to decorating a function with :func:`option` with
- the following parameters::
- def callback(ctx, param, value):
- if not value:
- ctx.abort()
- @click.command()
- @click.option('--yes', is_flag=True, callback=callback,
- expose_value=False, prompt='Do you want to continue?')
- def dropdb():
- pass
- """
- def decorator(f):
- def callback(ctx, param, value):
- if not value:
- ctx.abort()
- attrs.setdefault("is_flag", True)
- attrs.setdefault("callback", callback)
- attrs.setdefault("expose_value", False)
- attrs.setdefault("prompt", "Do you want to continue?")
- attrs.setdefault("help", "Confirm the action without prompting.")
- return option(*(param_decls or ("--yes",)), **attrs)(f)
- return decorator
- def password_option(*param_decls, **attrs):
- """Shortcut for password prompts.
- This is equivalent to decorating a function with :func:`option` with
- the following parameters::
- @click.command()
- @click.option('--password', prompt=True, confirmation_prompt=True,
- hide_input=True)
- def changeadmin(password):
- pass
- """
- def decorator(f):
- attrs.setdefault("prompt", True)
- attrs.setdefault("confirmation_prompt", True)
- attrs.setdefault("hide_input", True)
- return option(*(param_decls or ("--password",)), **attrs)(f)
- return decorator
- def version_option(version=None, *param_decls, **attrs):
- """Adds a ``--version`` option which immediately ends the program
- printing out the version number. This is implemented as an eager
- option that prints the version and exits the program in the callback.
- :param version: the version number to show. If not provided Click
- attempts an auto discovery via setuptools.
- :param prog_name: the name of the program (defaults to autodetection)
- :param message: custom message to show instead of the default
- (``'%(prog)s, version %(version)s'``)
- :param others: everything else is forwarded to :func:`option`.
- """
- if version is None:
- if hasattr(sys, "_getframe"):
- module = sys._getframe(1).f_globals.get("__name__")
- else:
- module = ""
- def decorator(f):
- prog_name = attrs.pop("prog_name", None)
- message = attrs.pop("message", "%(prog)s, version %(version)s")
- def callback(ctx, param, value):
- if not value or ctx.resilient_parsing:
- return
- prog = prog_name
- if prog is None:
- prog = ctx.find_root().info_name
- ver = version
- if ver is None:
- try:
- import pkg_resources
- except ImportError:
- pass
- else:
- for dist in pkg_resources.working_set:
- scripts = dist.get_entry_map().get("console_scripts") or {}
- for _, entry_point in iteritems(scripts):
- if entry_point.module_name == module:
- ver = dist.version
- break
- if ver is None:
- raise RuntimeError("Could not determine version")
- echo(message % {"prog": prog, "version": ver}, color=ctx.color)
- ctx.exit()
- attrs.setdefault("is_flag", True)
- attrs.setdefault("expose_value", False)
- attrs.setdefault("is_eager", True)
- attrs.setdefault("help", "Show the version and exit.")
- attrs["callback"] = callback
- return option(*(param_decls or ("--version",)), **attrs)(f)
- return decorator
- def help_option(*param_decls, **attrs):
- """Adds a ``--help`` option which immediately ends the program
- printing out the help page. This is usually unnecessary to add as
- this is added by default to all commands unless suppressed.
- Like :func:`version_option`, this is implemented as eager option that
- prints in the callback and exits.
- All arguments are forwarded to :func:`option`.
- """
- def decorator(f):
- def callback(ctx, param, value):
- if value and not ctx.resilient_parsing:
- echo(ctx.get_help(), color=ctx.color)
- ctx.exit()
- attrs.setdefault("is_flag", True)
- attrs.setdefault("expose_value", False)
- attrs.setdefault("help", "Show this message and exit.")
- attrs.setdefault("is_eager", True)
- attrs["callback"] = callback
- return option(*(param_decls or ("--help",)), **attrs)(f)
- return decorator
|