Skip to content

Commit 21a68bd

Browse files
committed
add pass_meta_key decorator
1 parent 1cb8609 commit 21a68bd

4 files changed

Lines changed: 65 additions & 0 deletions

File tree

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ Unreleased
184184
return a path object instead of a string. :issue:`405`
185185
- ``TypeError`` is raised when parameter with ``multiple=True`` or
186186
``nargs > 1`` has non-iterable default. :issue:`1749`
187+
- Add a ``pass_meta_key`` decorator for passing a key from
188+
``Context.meta``. This is useful for extensions using ``meta`` to
189+
store information. :issue:`1739`
187190

188191

189192
Version 7.1.2

docs/api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Decorators
3131

3232
.. autofunction:: make_pass_decorator
3333

34+
.. autofunction:: click.decorators.pass_meta_key
35+
36+
3437
Utilities
3538
---------
3639

src/click/decorators.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import inspect
2+
import typing as t
23
from functools import update_wrapper
34

45
from .core import Argument
@@ -8,6 +9,8 @@
89
from .globals import get_current_context
910
from .utils import echo
1011

12+
if t.TYPE_CHECKING:
13+
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
1114

1215
def pass_context(f):
1316
"""Marks a callback as wanting to receive the current context
@@ -75,6 +78,39 @@ def new_func(*args, **kwargs):
7578
return decorator
7679

7780

81+
def pass_meta_key(
82+
key: str, *, doc_description: t.Optional[str] = None
83+
) -> "t.Callable[[F], F]":
84+
"""Create a decorator that passes a key from
85+
:attr:`click.Context.meta` as the first argument to the decorated
86+
function.
87+
88+
:param key: Key in ``Context.meta`` to pass.
89+
:param doc_description: Description of the object being passed,
90+
inserted into the decorator's docstring. Defaults to "the 'key'
91+
key from Context.meta".
92+
93+
.. versionadded:: 8.0
94+
"""
95+
96+
def decorator(f: "F") -> "F":
97+
def new_func(*args, **kwargs):
98+
ctx = get_current_context()
99+
obj = ctx.meta[key]
100+
return ctx.invoke(f, obj, *args, **kwargs)
101+
102+
return update_wrapper(t.cast("F", new_func), f)
103+
104+
if doc_description is None:
105+
doc_description = f"the {key!r} key from :attr:`click.Context.meta`"
106+
107+
decorator.__doc__ = (
108+
f"Decorator that passes {doc_description} as the first argument"
109+
" to the decorated function."
110+
)
111+
return decorator
112+
113+
78114
def _make_command(f, name, attrs, cls):
79115
if isinstance(f, Command):
80116
raise TypeError("Attempted to convert a callback into a command twice.")

tests/test_context.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import click
66
from click.core import ParameterSource
7+
from click.decorators import pass_meta_key
78

89

910
def test_ensure_context_objects(runner):
@@ -151,6 +152,28 @@ def cli(ctx):
151152
runner.invoke(cli, [], catch_exceptions=False)
152153

153154

155+
def test_make_pass_meta_decorator(runner):
156+
@click.group()
157+
@click.pass_context
158+
def cli(ctx):
159+
ctx.meta["value"] = "good"
160+
161+
@cli.command()
162+
@pass_meta_key("value")
163+
def show(value):
164+
return value
165+
166+
result = runner.invoke(cli, ["show"], standalone_mode=False)
167+
assert result.return_value == "good"
168+
169+
170+
def test_make_pass_meta_decorator_doc():
171+
pass_value = pass_meta_key("value")
172+
assert "the 'value' key from :attr:`click.Context.meta`" in pass_value.__doc__
173+
pass_value = pass_meta_key("value", doc_description="the test value")
174+
assert "passes the test value" in pass_value.__doc__
175+
176+
154177
def test_context_pushing():
155178
rv = []
156179

0 commit comments

Comments
 (0)