|
14 | 14 | """Decorators for Click commands"""
|
15 | 15 | import asyncio
|
16 | 16 | from functools import wraps
|
| 17 | +from typing import Callable, Optional |
17 | 18 |
|
18 | 19 | import click
|
19 | 20 |
|
20 | 21 | from planet import exceptions
|
| 22 | +from planet.cli.options import pretty |
| 23 | + |
| 24 | + |
| 25 | +def command(group: click.Group, |
| 26 | + name: Optional[str] = None, |
| 27 | + extra_args: list[Callable] = []): |
| 28 | + """a decorator that adds common utilities/options to a click command |
| 29 | +
|
| 30 | + usage: |
| 31 | +
|
| 32 | + @command(features) # pass a group created with @click.group |
| 33 | + def my_command(): |
| 34 | + ... |
| 35 | +
|
| 36 | + this single decorator replaces a list of decorators we would otherwise |
| 37 | + add to every function e.g.: |
| 38 | +
|
| 39 | + @features.command |
| 40 | + @coro |
| 41 | + @translate_exceptions |
| 42 | + @click.pass_context |
| 43 | + def my_command(): |
| 44 | + ... |
| 45 | + """ |
| 46 | + |
| 47 | + # the decorators to add to the function **when the function is run** |
| 48 | + # (as opposed to when the function is registered as a click command) |
| 49 | + decorators = [ |
| 50 | + coro, |
| 51 | + translate_exceptions, |
| 52 | + click.pass_context, |
| 53 | + pretty, |
| 54 | + ] + extra_args |
| 55 | + |
| 56 | + # since we want to use `command` as a function with an arg: `@command(group)`, |
| 57 | + # we need to create and return an "real" decorator that takes the function as its |
| 58 | + # arg. |
| 59 | + def decorator(f): |
| 60 | + |
| 61 | + # run any click-specific registration decorators |
| 62 | + for fn in decorators: |
| 63 | + f = fn(f) |
| 64 | + |
| 65 | + @wraps(f) |
| 66 | + def wrapper(*args, **kwargs): |
| 67 | + cmd = f |
| 68 | + |
| 69 | + # wrap cmd with all the default decorators |
| 70 | + for d in decorators: |
| 71 | + cmd = d(f) |
| 72 | + |
| 73 | + return cmd(*args, **kwargs) |
| 74 | + |
| 75 | + # register the whole thing as a Click command. |
| 76 | + # Doing this last (outside the wrapper) allows click to |
| 77 | + # pick up the options/arguments added to the command by e.g. |
| 78 | + # `@click.option()` |
| 79 | + return group.command(name=name)(wrapper) |
| 80 | + |
| 81 | + return decorator |
21 | 82 |
|
22 | 83 |
|
23 | 84 | # https://github.com/pallets/click/issues/85#issuecomment-503464628
|
@@ -60,6 +121,6 @@ def wrapper(*args, **kwargs):
|
60 | 121 | 'Auth information does not exist or is corrupted. Initialize '
|
61 | 122 | 'with `planet auth init`.')
|
62 | 123 | except exceptions.PlanetError as ex:
|
63 |
| - raise click.ClickException(ex) |
| 124 | + raise click.ClickException(str(ex)) |
64 | 125 |
|
65 | 126 | return wrapper
|
0 commit comments