Skip to content

Commit

Permalink
Split generation of option's help extra and rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
kdeldycke committed Jul 1, 2023
1 parent 3fcf338 commit fe1cbbf
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -20,6 +20,9 @@ Unreleased
- Improve type hinting for decorators and give all generic types parameters.
:issue:`2398`
- Fix return value and type signature of `shell_completion.add_completion_class` function. :pr:`2421`
- Add ``get_help_extra`` method on ``Option`` to fetch the generated extra
items used in ``get_help_record`` to render help text. :issue:`2516`
:pr:`2517`


Version 8.1.3
Expand Down
45 changes: 30 additions & 15 deletions src/click/core.py
Expand Up @@ -2737,7 +2737,28 @@ def _write_opts(opts: t.Sequence[str]) -> str:
rv.append(_write_opts(self.secondary_opts))

help = self.help or ""
extra = []

extra = self.get_help_extra(ctx)
extra_items = []
if "envvars" in extra:
extra_items.append(
_("env var: {var}").format(var=", ".join(extra["envvars"]))
)
if "default" in extra:
extra_items.append(_("default: {default}").format(default=extra["default"]))
if "range" in extra:
extra_items.append(extra["range"])
if "required" in extra:
extra_items.append(_(extra["required"]))

if extra_items:
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

def get_help_extra(self, ctx: Context) -> t.Dict[str, t.Any]:
extra: t.Dict[str, t.Any] = {}

if self.show_envvar:
envvar = self.envvar
Expand All @@ -2751,12 +2772,10 @@ def _write_opts(opts: t.Sequence[str]) -> str:
envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}"

if envvar is not None:
var_str = (
envvar
if isinstance(envvar, str)
else ", ".join(str(d) for d in envvar)
)
extra.append(_("env var: {var}").format(var=var_str))
if isinstance(envvar, str):
extra["envvars"] = (envvar,)
else:
extra["envvars"] = tuple(str(d) for d in envvar)

# Temporarily enable resilient parsing to avoid type casting
# failing for the default. Might be possible to extend this to
Expand Down Expand Up @@ -2799,7 +2818,7 @@ def _write_opts(opts: t.Sequence[str]) -> str:
default_string = str(default_value)

if default_string:
extra.append(_("default: {default}").format(default=default_string))
extra["default"] = default_string

if (
isinstance(self.type, types._NumberRangeBase)
Expand All @@ -2809,16 +2828,12 @@ def _write_opts(opts: t.Sequence[str]) -> str:
range_str = self.type._describe_range()

if range_str:
extra.append(range_str)
extra["range"] = range_str

if self.required:
extra.append(_("required"))
extra["required"] = "required"

if extra:
extra_str = "; ".join(extra)
help = f"{help} [{extra_str}]" if help else f"[{extra_str}]"

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

@t.overload
def get_default(
Expand Down
35 changes: 23 additions & 12 deletions tests/test_options.py
Expand Up @@ -316,6 +316,7 @@ def __str__(self):

opt = click.Option(["-a"], default=Value(), show_default=True)
ctx = click.Context(click.Command("cli"))
assert opt.get_help_extra(ctx) == {"default": "special value"}
assert "special value" in opt.get_help_record(ctx)[1]


Expand All @@ -331,6 +332,7 @@ def __str__(self):
def test_intrange_default_help_text(type, expect):
option = click.Option(["--num"], type=type, show_default=True, default=2)
context = click.Context(click.Command("test"))
assert option.get_help_extra(context) == {"default": "2", "range": expect}
result = option.get_help_record(context)[1]
assert expect in result

Expand All @@ -339,6 +341,7 @@ def test_count_default_type_help():
"""A count option with the default type should not show >=0 in help."""
option = click.Option(["--count"], count=True, help="some words")
context = click.Context(click.Command("test"))
assert option.get_help_extra(context) == {}
result = option.get_help_record(context)[1]
assert result == "some words"

Expand All @@ -354,6 +357,7 @@ def test_file_type_help_default():
["--in"], type=click.File(), default=__file__, show_default=True
)
context = click.Context(click.Command("test"))
assert option.get_help_extra(context) == {"default": __file__}
result = option.get_help_record(context)[1]
assert __file__ in result

Expand Down Expand Up @@ -741,6 +745,7 @@ def test_show_default_boolean_flag_name(runner, default, expect):
help="Enable/Disable the cache.",
)
ctx = click.Context(click.Command("test"))
assert opt.get_help_extra(ctx) == {"default": expect}
message = opt.get_help_record(ctx)[1]
assert f"[default: {expect}]" in message

Expand All @@ -757,6 +762,7 @@ def test_show_true_default_boolean_flag_value(runner):
help="Enable the cache.",
)
ctx = click.Context(click.Command("test"))
assert opt.get_help_extra(ctx) == {"default": "True"}
message = opt.get_help_record(ctx)[1]
assert "[default: True]" in message

Expand All @@ -774,6 +780,7 @@ def test_hide_false_default_boolean_flag_value(runner, default):
help="Enable the cache.",
)
ctx = click.Context(click.Command("test"))
assert opt.get_help_extra(ctx) == {}
message = opt.get_help_record(ctx)[1]
assert "[default: " not in message

Expand All @@ -782,6 +789,7 @@ def test_show_default_string(runner):
"""When show_default is a string show that value as default."""
opt = click.Option(["--limit"], show_default="unlimited")
ctx = click.Context(click.Command("cli"))
assert opt.get_help_extra(ctx) == {"default": "(unlimited)"}
message = opt.get_help_record(ctx)[1]
assert "[default: (unlimited)]" in message

Expand All @@ -790,6 +798,7 @@ def test_do_not_show_no_default(runner):
"""When show_default is True and no default is set do not show None."""
opt = click.Option(["--limit"], show_default=True)
ctx = click.Context(click.Command("cli"))
assert opt.get_help_extra(ctx) == {}
message = opt.get_help_record(ctx)[1]
assert "[default: None]" not in message

Expand All @@ -800,28 +809,30 @@ def test_do_not_show_default_empty_multiple():
"""
opt = click.Option(["-a"], multiple=True, help="values", show_default=True)
ctx = click.Context(click.Command("cli"))
assert opt.get_help_extra(ctx) == {}
message = opt.get_help_record(ctx)[1]
assert message == "values"


@pytest.mark.parametrize(
("ctx_value", "opt_value", "expect"),
("ctx_value", "opt_value", "extra_value", "expect"),
[
(None, None, False),
(None, False, False),
(None, True, True),
(False, None, False),
(False, False, False),
(False, True, True),
(True, None, True),
(True, False, False),
(True, True, True),
(False, "one", True),
(None, None, {}, False),
(None, False, {}, False),
(None, True, {"default": "1"}, True),
(False, None, {}, False),
(False, False, {}, False),
(False, True, {"default": "1"}, True),
(True, None, {"default": "1"}, True),
(True, False, {}, False),
(True, True, {"default": "1"}, True),
(False, "one", {"default": "(one)"}, True),
],
)
def test_show_default_precedence(ctx_value, opt_value, expect):
def test_show_default_precedence(ctx_value, opt_value, extra_value, expect):
ctx = click.Context(click.Command("test"), show_default=ctx_value)
opt = click.Option("-a", default=1, help="value", show_default=opt_value)
assert opt.get_help_extra(ctx) == extra_value
help = opt.get_help_record(ctx)[1]
assert ("default:" in help) is expect

Expand Down

0 comments on commit fe1cbbf

Please sign in to comment.