From edea710bedcec63b0a7e736baf341c2bc7a63e6c Mon Sep 17 00:00:00 2001 From: Gianluca Gippetto Date: Mon, 13 Nov 2023 02:29:00 +0100 Subject: [PATCH] Add cloup.get_current_context --- cloup/__init__.py | 3 ++- cloup/_context.py | 58 +++++++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/cloup/__init__.py b/cloup/__init__.py index 0c18ac7..637f27c 100644 --- a/cloup/__init__.py +++ b/cloup/__init__.py @@ -41,7 +41,7 @@ HelpFormatter, HelpSection, ) -from ._context import Context, pass_context +from ._context import Context, get_current_context, pass_context from ._params import Argument, Option, argument, option from ._option_groups import ( OptionGroup, @@ -103,6 +103,7 @@ "constraint", "dir_path", "file_path", + "get_current_context", "group", "help_option", "option", diff --git a/cloup/_context.py b/cloup/_context.py index 8445192..9c0ec8e 100644 --- a/cloup/_context.py +++ b/cloup/_context.py @@ -1,8 +1,10 @@ from __future__ import annotations import warnings -from typing import Any, Callable, cast, Dict, List, Optional, Type, TypeVar, TYPE_CHECKING from functools import update_wrapper +from typing import ( + Any, Callable, cast, Dict, List, Optional, Type, TypeVar, TYPE_CHECKING, overload, +) import click @@ -11,6 +13,41 @@ from cloup.formatting import HelpFormatter from cloup.typing import MISSING, Possibly +if TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = TypeVar("R") + + +@overload +def get_current_context() -> "Context": + ... + + +@overload +def get_current_context(silent: bool = False) -> "Optional[Context]": + ... + + +def get_current_context(silent: bool = False) -> "Optional[Context]": + """Equivalent to :func:`click.get_current_context` but casts the returned + :class:`click.Context` object to :class:`cloup.Context` (which is safe when using + cloup commands classes and decorators).""" + return cast(Optional[Context], click.get_current_context(silent=silent)) + + +def pass_context(f: "Callable[te.Concatenate[Context, P], R]") -> "Callable[P, R]": + """Marks a callback as wanting to receive the current context object as first + argument. Equivalent to :func:`click.pass_context` but assumes the current context + is of type :class:`cloup.Context`.""" + + def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + def _warn_if_formatter_settings_conflict( ctx_key: str, @@ -225,22 +262,3 @@ def settings( dictionary, so that you can be guided by your IDE. """ return pick_non_missing(locals()) - - -if TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = TypeVar("R") - - -def pass_context(f: Callable[te.Concatenate[Context, P], R]) -> Callable[P, R]: - """Marks a callback as wanting to receive the current context - object as first argument. - """ - - def new_func(*args: P.args, **kwargs: P.kwargs) -> R: - return f(cast(Context, click.get_current_context()), *args, **kwargs) - - return update_wrapper(new_func, f)