1import inspect
2import sys
3from functools import update_wrapper
4
5from ._compat import iteritems
6from ._unicodefun import _check_for_unicode_literals
7from .core import Argument
8from .core import Command
9from .core import Group
10from .core import Option
11from .globals import get_current_context
12from .utils import echo
13
14
15def pass_context(f):
16    """Marks a callback as wanting to receive the current context
17    object as first argument.
18    """
19
20    def new_func(*args, **kwargs):
21        return f(get_current_context(), *args, **kwargs)
22
23    return update_wrapper(new_func, f)
24
25
26def pass_obj(f):
27    """Similar to :func:`pass_context`, but only pass the object on the
28    context onwards (:attr:`Context.obj`).  This is useful if that object
29    represents the state of a nested system.
30    """
31
32    def new_func(*args, **kwargs):
33        return f(get_current_context().obj, *args, **kwargs)
34
35    return update_wrapper(new_func, f)
36
37
38def make_pass_decorator(object_type, ensure=False):
39    """Given an object type this creates a decorator that will work
40    similar to :func:`pass_obj` but instead of passing the object of the
41    current context, it will find the innermost context of type
42    :func:`object_type`.
43
44    This generates a decorator that works roughly like this::
45
46        from functools import update_wrapper
47
48        def decorator(f):
49            @pass_context
50            def new_func(ctx, *args, **kwargs):
51                obj = ctx.find_object(object_type)
52                return ctx.invoke(f, obj, *args, **kwargs)
53            return update_wrapper(new_func, f)
54        return decorator
55
56    :param object_type: the type of the object to pass.
57    :param ensure: if set to `True`, a new object will be created and
58                   remembered on the context if it's not there yet.
59    """
60
61    def decorator(f):
62        def new_func(*args, **kwargs):
63            ctx = get_current_context()
64            if ensure:
65                obj = ctx.ensure_object(object_type)
66            else:
67                obj = ctx.find_object(object_type)
68            if obj is None:
69                raise RuntimeError(
70                    "Managed to invoke callback without a context"
71                    " object of type '{}' existing".format(object_type.__name__)
72                )
73            return ctx.invoke(f, obj, *args, **kwargs)
74
75        return update_wrapper(new_func, f)
76
77    return decorator
78
79
80def _make_command(f, name, attrs, cls):
81    if isinstance(f, Command):
82        raise TypeError("Attempted to convert a callback into a command twice.")
83    try:
84        params = f.__click_params__
85        params.reverse()
86        del f.__click_params__
87    except AttributeError:
88        params = []
89    help = attrs.get("help")
90    if help is None:
91        help = inspect.getdoc(f)
92        if isinstance(help, bytes):
93            help = help.decode("utf-8")
94    else:
95        help = inspect.cleandoc(help)
96    attrs["help"] = help
97    _check_for_unicode_literals()
98    return cls(
99        name=name or f.__name__.lower().replace("_", "-"),
100        callback=f,
101        params=params,
102        **attrs
103    )
104
105
106def command(name=None, cls=None, **attrs):
107    r"""Creates a new :class:`Command` and uses the decorated function as
108    callback.  This will also automatically attach all decorated
109    :func:`option`\s and :func:`argument`\s as parameters to the command.
110
111    The name of the command defaults to the name of the function with
112    underscores replaced by dashes.  If you want to change that, you can
113    pass the intended name as the first argument.
114
115    All keyword arguments are forwarded to the underlying command class.
116
117    Once decorated the function turns into a :class:`Command` instance
118    that can be invoked as a command line utility or be attached to a
119    command :class:`Group`.
120
121    :param name: the name of the command.  This defaults to the function
122                 name with underscores replaced by dashes.
123    :param cls: the command class to instantiate.  This defaults to
124                :class:`Command`.
125    """
126    if cls is None:
127        cls = Command
128
129    def decorator(f):
130        cmd = _make_command(f, name, attrs, cls)
131        cmd.__doc__ = f.__doc__
132        return cmd
133
134    return decorator
135
136
137def group(name=None, **attrs):
138    """Creates a new :class:`Group` with a function as callback.  This
139    works otherwise the same as :func:`command` just that the `cls`
140    parameter is set to :class:`Group`.
141    """
142    attrs.setdefault("cls", Group)
143    return command(name, **attrs)
144
145
146def _param_memo(f, param):
147    if isinstance(f, Command):
148        f.params.append(param)
149    else:
150        if not hasattr(f, "__click_params__"):
151            f.__click_params__ = []
152        f.__click_params__.append(param)
153
154
155def argument(*param_decls, **attrs):
156    """Attaches an argument to the command.  All positional arguments are
157    passed as parameter declarations to :class:`Argument`; all keyword
158    arguments are forwarded unchanged (except ``cls``).
159    This is equivalent to creating an :class:`Argument` instance manually
160    and attaching it to the :attr:`Command.params` list.
161
162    :param cls: the argument class to instantiate.  This defaults to
163                :class:`Argument`.
164    """
165
166    def decorator(f):
167        ArgumentClass = attrs.pop("cls", Argument)
168        _param_memo(f, ArgumentClass(param_decls, **attrs))
169        return f
170
171    return decorator
172
173
174def option(*param_decls, **attrs):
175    """Attaches an option to the command.  All positional arguments are
176    passed as parameter declarations to :class:`Option`; all keyword
177    arguments are forwarded unchanged (except ``cls``).
178    This is equivalent to creating an :class:`Option` instance manually
179    and attaching it to the :attr:`Command.params` list.
180
181    :param cls: the option class to instantiate.  This defaults to
182                :class:`Option`.
183    """
184
185    def decorator(f):
186        # Issue 926, copy attrs, so pre-defined options can re-use the same cls=
187        option_attrs = attrs.copy()
188
189        if "help" in option_attrs:
190            option_attrs["help"] = inspect.cleandoc(option_attrs["help"])
191        OptionClass = option_attrs.pop("cls", Option)
192        _param_memo(f, OptionClass(param_decls, **option_attrs))
193        return f
194
195    return decorator
196
197
198def confirmation_option(*param_decls, **attrs):
199    """Shortcut for confirmation prompts that can be ignored by passing
200    ``--yes`` as parameter.
201
202    This is equivalent to decorating a function with :func:`option` with
203    the following parameters::
204
205        def callback(ctx, param, value):
206            if not value:
207                ctx.abort()
208
209        @click.command()
210        @click.option('--yes', is_flag=True, callback=callback,
211                      expose_value=False, prompt='Do you want to continue?')
212        def dropdb():
213            pass
214    """
215
216    def decorator(f):
217        def callback(ctx, param, value):
218            if not value:
219                ctx.abort()
220
221        attrs.setdefault("is_flag", True)
222        attrs.setdefault("callback", callback)
223        attrs.setdefault("expose_value", False)
224        attrs.setdefault("prompt", "Do you want to continue?")
225        attrs.setdefault("help", "Confirm the action without prompting.")
226        return option(*(param_decls or ("--yes",)), **attrs)(f)
227
228    return decorator
229
230
231def password_option(*param_decls, **attrs):
232    """Shortcut for password prompts.
233
234    This is equivalent to decorating a function with :func:`option` with
235    the following parameters::
236
237        @click.command()
238        @click.option('--password', prompt=True, confirmation_prompt=True,
239                      hide_input=True)
240        def changeadmin(password):
241            pass
242    """
243
244    def decorator(f):
245        attrs.setdefault("prompt", True)
246        attrs.setdefault("confirmation_prompt", True)
247        attrs.setdefault("hide_input", True)
248        return option(*(param_decls or ("--password",)), **attrs)(f)
249
250    return decorator
251
252
253def version_option(version=None, *param_decls, **attrs):
254    """Adds a ``--version`` option which immediately ends the program
255    printing out the version number.  This is implemented as an eager
256    option that prints the version and exits the program in the callback.
257
258    :param version: the version number to show.  If not provided Click
259                    attempts an auto discovery via setuptools.
260    :param prog_name: the name of the program (defaults to autodetection)
261    :param message: custom message to show instead of the default
262                    (``'%(prog)s, version %(version)s'``)
263    :param others: everything else is forwarded to :func:`option`.
264    """
265    if version is None:
266        if hasattr(sys, "_getframe"):
267            module = sys._getframe(1).f_globals.get("__name__")
268        else:
269            module = ""
270
271    def decorator(f):
272        prog_name = attrs.pop("prog_name", None)
273        message = attrs.pop("message", "%(prog)s, version %(version)s")
274
275        def callback(ctx, param, value):
276            if not value or ctx.resilient_parsing:
277                return
278            prog = prog_name
279            if prog is None:
280                prog = ctx.find_root().info_name
281            ver = version
282            if ver is None:
283                try:
284                    import pkg_resources
285                except ImportError:
286                    pass
287                else:
288                    for dist in pkg_resources.working_set:
289                        scripts = dist.get_entry_map().get("console_scripts") or {}
290                        for _, entry_point in iteritems(scripts):
291                            if entry_point.module_name == module:
292                                ver = dist.version
293                                break
294                if ver is None:
295                    raise RuntimeError("Could not determine version")
296            echo(message % {"prog": prog, "version": ver}, color=ctx.color)
297            ctx.exit()
298
299        attrs.setdefault("is_flag", True)
300        attrs.setdefault("expose_value", False)
301        attrs.setdefault("is_eager", True)
302        attrs.setdefault("help", "Show the version and exit.")
303        attrs["callback"] = callback
304        return option(*(param_decls or ("--version",)), **attrs)(f)
305
306    return decorator
307
308
309def help_option(*param_decls, **attrs):
310    """Adds a ``--help`` option which immediately ends the program
311    printing out the help page.  This is usually unnecessary to add as
312    this is added by default to all commands unless suppressed.
313
314    Like :func:`version_option`, this is implemented as eager option that
315    prints in the callback and exits.
316
317    All arguments are forwarded to :func:`option`.
318    """
319
320    def decorator(f):
321        def callback(ctx, param, value):
322            if value and not ctx.resilient_parsing:
323                echo(ctx.get_help(), color=ctx.color)
324                ctx.exit()
325
326        attrs.setdefault("is_flag", True)
327        attrs.setdefault("expose_value", False)
328        attrs.setdefault("help", "Show this message and exit.")
329        attrs.setdefault("is_eager", True)
330        attrs["callback"] = callback
331        return option(*(param_decls or ("--help",)), **attrs)(f)
332
333    return decorator
334