diff --git a/src/click/__init__.py b/src/click/__init__.py index cfef88ea8..46f99e951 100644 --- a/src/click/__init__.py +++ b/src/click/__init__.py @@ -19,6 +19,7 @@ from .decorators import confirmation_option as confirmation_option from .decorators import group as group from .decorators import help_option as help_option +from .decorators import HelpOption as HelpOption from .decorators import make_pass_decorator as make_pass_decorator from .decorators import option as option from .decorators import pass_context as pass_context diff --git a/src/click/core.py b/src/click/core.py index 29e46bd70..c623764ee 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -1285,25 +1285,19 @@ def get_help_option_names(self, ctx: Context) -> t.List[str]: return list(all_names) def get_help_option(self, ctx: Context) -> t.Optional["Option"]: - """Returns the help option object.""" + """Returns the help option object. + + Unless ``add_help_option`` is ``False``. + """ help_options = self.get_help_option_names(ctx) if not help_options or not self.add_help_option: return None - def show_help(ctx: Context, param: "Parameter", value: str) -> None: - if value and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - return Option( - help_options, - is_flag=True, - is_eager=True, - expose_value=False, - callback=show_help, - help=_("Show this message and exit."), - ) + # Avoid circular import. + from .decorators import HelpOption + + return HelpOption(help_options) def make_parser(self, ctx: Context) -> OptionParser: """Creates the underlying option parser for this command.""" diff --git a/src/click/decorators.py b/src/click/decorators.py index c346c702a..ea1ae1a12 100644 --- a/src/click/decorators.py +++ b/src/click/decorators.py @@ -1,3 +1,4 @@ +from collections import abc import inspect import types import typing as t @@ -522,32 +523,41 @@ def callback(ctx: Context, param: Parameter, value: bool) -> None: return option(*param_decls, **kwargs) -def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--help`` option which immediately prints the help page +class HelpOption(Option): + """Pre-configured ``--help`` option which immediately prints the help page and exits the program. + """ - This is usually unnecessary, as the ``--help`` option is added to - each command automatically unless ``add_help_option=False`` is - passed. + def __init__( + self, + param_decls: abc.Sequence[str] | None = None, + **kwargs: t.Any, + ) -> None: + if not param_decls: + param_decls = ("--help",) - :param param_decls: One or more option names. Defaults to the single - value ``"--help"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", self.show_help) - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value or ctx.resilient_parsing: - return + super().__init__(param_decls, **kwargs) - echo(ctx.get_help(), color=ctx.color) - ctx.exit() + @staticmethod + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() - if not param_decls: - param_decls = ("--help",) - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show this message and exit.")) - kwargs["callback"] = callback +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Decorator for the pre-configured ``--help`` option defined above. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + kwargs.setdefault("cls", HelpOption) return option(*param_decls, **kwargs)