diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 7d384b8..d29e9bb 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -5,6 +5,8 @@ on: tags: [v*] pull_request: workflow_dispatch: + schedule: + - cron: "0 6 * * 7" jobs: tests: diff --git a/cloup/__init__.py b/cloup/__init__.py index eec86e7..637f27c 100644 --- a/cloup/__init__.py +++ b/cloup/__init__.py @@ -11,7 +11,6 @@ # decorators confirmation_option, help_option, - pass_context, pass_obj, password_option, version_option, @@ -42,7 +41,7 @@ HelpFormatter, HelpSection, ) -from ._context import Context +from ._context import Context, get_current_context, pass_context from ._params import Argument, Option, argument, option from ._option_groups import ( OptionGroup, @@ -104,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 20fa278..9c0ec8e 100644 --- a/cloup/_context.py +++ b/cloup/_context.py @@ -1,5 +1,10 @@ +from __future__ import annotations + import warnings -from typing import Any, Callable, Dict, List, Optional, Type +from functools import update_wrapper +from typing import ( + Any, Callable, cast, Dict, List, Optional, Type, TypeVar, TYPE_CHECKING, overload, +) import click @@ -8,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, diff --git a/tox.ini b/tox.ini index d7ffcae..ac359cd 100644 --- a/tox.ini +++ b/tox.ini @@ -39,7 +39,9 @@ deps = flake8 commands = flake8 cloup tests examples [testenv:mypy] -deps = mypy +deps = + mypy + typing-extensions commands = mypy --strict cloup mypy tests examples