123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- import sys
- import inspect
- from functools import update_wrapper
- from ._compat import iteritems
- from ._unicodefun import _check_for_unicode_literals
- from .utils import echo
- from .globals import get_current_context
- 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 %r existing'
- % 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. 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 script_name, 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
- # Circular dependencies between core and decorators
- from .core import Command, Group, Argument, Option
|