Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Unreleased
- A `KeyboardInterrupt` arriving while `Command.main()` reports an abort or an error,
or while it exits, no longer escapes as an unhandled traceback. The command still
exits with the intended code; only the message may be lost. {issue}`3802`
- Add {meth}`Option.get_help_spec`, which returns the option's left help
column even when the option is hidden. {meth}`Option.get_help_record` still
returns `None` for hidden options, so help screens are unchanged. {pr}`3821`
- Document which types are inferred from `default`, and what an unrecognized
`type` callable does to a command-line value. {issue}`3036` {pr}`3808`

Expand Down
19 changes: 15 additions & 4 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3363,10 +3363,15 @@ def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
nargs=self.nargs,
)

def get_help_record(self, ctx: Context) -> tuple[str, str] | None:
if self.hidden:
return None
def get_help_spec(self, ctx: Context) -> str:
"""Returns the left column of the option's help record: its spellings
and metavar, like ``-v, --verbose`` or ``-c, --config TEXT``.

Unlike :meth:`get_help_record`, the spec is produced even when the
option is :attr:`hidden`.

.. versionadded:: 8.5.1
"""
any_prefix_is_slash = False

def _write_opts(opts: cabc.Sequence[str]) -> str:
Expand All @@ -3387,6 +3392,12 @@ def _write_opts(opts: cabc.Sequence[str]) -> str:
if self.secondary_opts:
rv.append(_write_opts(self.secondary_opts))

return ("; " if any_prefix_is_slash else " / ").join(rv)

def get_help_record(self, ctx: Context) -> tuple[str, str] | None:
if self.hidden:
return None

help = self.help or ""

extra = self.get_help_extra(ctx)
Expand All @@ -3406,7 +3417,7 @@ def _write_opts(opts: cabc.Sequence[str]) -> str:
extra_str = "; ".join(extra_items)
help = f"{help} [{extra_str}]" if help else f"[{extra_str}]"

return ("; " if any_prefix_is_slash else " / ").join(rv), help
return self.get_help_spec(ctx), help

def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra:
extra: types.OptionHelpExtra = {}
Expand Down
24 changes: 24 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ def test_deprecated_empty_help_no_leading_space(help_text, deprecated, expected)
assert opt.get_help_record(ctx)[1] == expected


@pytest.mark.parametrize(
("param_decls", "kwargs", "expected"),
[
(["-v", "--verbose"], {"is_flag": True}, "-v, --verbose"),
(["--config"], {}, "--config TEXT"),
(["--color/--no-color"], {}, "--color / --no-color"),
(["/debug;/no-debug"], {}, "/debug; /no-debug"),
],
)
@pytest.mark.parametrize("hidden", [False, True])
def test_help_spec(param_decls, kwargs, hidden, expected):
"""A hidden option still produces its spec via ``get_help_spec()``, unlike
``get_help_record()``.
"""
opt = click.Option(param_decls, hidden=hidden, **kwargs)
ctx = click.Context(click.Command("cli"))
assert opt.get_help_spec(ctx) == expected

if hidden:
assert opt.get_help_record(ctx) is None
else:
assert opt.get_help_record(ctx)[0] == expected


@pytest.mark.parametrize("deprecated", [True, "USE B INSTEAD"])
def test_deprecated_warning(runner, deprecated):
@click.command()
Expand Down