Skip to content

Commit 76552ff

Browse files
authored
Show default string in prompt (#3328)
2 parents 8c95c73 + 8c452e0 commit 76552ff

5 files changed

Lines changed: 82 additions & 5 deletions

File tree

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Unreleased
2121
``pytest-xdist`` to detect test pollution and race conditions. :pr:`3151`
2222
- Add contributor documentation for running stress tests, randomized
2323
parallel tests, and Flask smoke tests. :pr:`3151` :pr:`3177`
24+
- Show custom ``show_default`` string in prompts, matching the existing
25+
help text behavior. :issue:`2836` :pr:`2837` :pr:`3165` :pr:`3262` :pr:`3280`
26+
:pr:`3328``
2427
- Fix ``default=True`` with boolean ``flag_value`` always returning the
2528
``flag_value`` instead of ``True``. The ``default=True`` to ``flag_value``
2629
substitution now only applies to non-boolean flags, where ``True`` acts as a

src/click/core.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3183,10 +3183,10 @@ def prompt_for_value(self, ctx: Context) -> t.Any:
31833183
default = bool(default)
31843184
return confirm(self.prompt, default)
31853185

3186-
# If show_default is set to True/False, provide this to `prompt` as well. For
3187-
# non-bool values of `show_default`, we use `prompt`'s default behavior
3186+
# If show_default is given, provide this to `prompt` as well,
3187+
# otherwise we use `prompt`'s default behavior
31883188
prompt_kwargs: t.Any = {}
3189-
if isinstance(self.show_default, bool):
3189+
if self.show_default is not None:
31903190
prompt_kwargs["show_default"] = self.show_default
31913191

31923192
return prompt(

src/click/termui.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,16 @@ def hidden_prompt_func(prompt: str) -> str:
6060
def _build_prompt(
6161
text: str,
6262
suffix: str,
63-
show_default: bool = False,
63+
show_default: bool | str = False,
6464
default: t.Any | None = None,
6565
show_choices: bool = True,
6666
type: ParamType | None = None,
6767
) -> str:
6868
prompt = text
6969
if type is not None and show_choices and isinstance(type, Choice):
7070
prompt += f" ({', '.join(map(str, type.choices))})"
71+
if isinstance(show_default, str):
72+
default = f"({show_default})"
7173
if default is not None and show_default:
7274
prompt = f"{prompt} [{_format_default(default)}]"
7375
return f"{prompt}{suffix}"
@@ -88,7 +90,7 @@ def prompt(
8890
type: ParamType | t.Any | None = None,
8991
value_proc: t.Callable[[str], t.Any] | None = None,
9092
prompt_suffix: str = ": ",
91-
show_default: bool = True,
93+
show_default: bool | str = True,
9294
err: bool = False,
9395
show_choices: bool = True,
9496
) -> t.Any:
@@ -112,13 +114,19 @@ def prompt(
112114
convert a value.
113115
:param prompt_suffix: a suffix that should be added to the prompt.
114116
:param show_default: shows or hides the default value in the prompt.
117+
If this value is a string, it shows that string
118+
in parentheses instead of the actual value.
115119
:param err: if set to true the file defaults to ``stderr`` instead of
116120
``stdout``, the same as with echo.
117121
:param show_choices: Show or hide choices if the passed type is a Choice.
118122
For example if type is a Choice of either day or week,
119123
show_choices is true and text is "Group by" then the
120124
prompt will be "Group by (day, week): ".
121125
126+
.. versionchanged:: 8.3.3
127+
``show_default`` can be a string to show a custom value instead
128+
of the actual default, matching the help text behavior.
129+
122130
.. versionchanged:: 8.3.1
123131
A space is no longer appended to the prompt.
124132

tests/test_options.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,19 @@ def test_show_default_string(runner):
12591259
assert "[default: (unlimited)]" in message
12601260

12611261

1262+
def test_string_show_default_shows_custom_string_in_prompt(runner):
1263+
@click.command()
1264+
@click.option(
1265+
"--arg1", show_default="custom", prompt=True, default="my-default-value"
1266+
)
1267+
def cmd(arg1):
1268+
pass
1269+
1270+
result = runner.invoke(cmd, input="my-input", standalone_mode=False)
1271+
assert "(custom)" in result.output
1272+
assert "my-default-value" not in result.output
1273+
1274+
12621275
class _StrictEq:
12631276
"""Object whose ``__eq__`` raises on string comparison (like semver.Version)."""
12641277

tests/test_termui.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import click._termui_impl
1010
from click._compat import WIN
1111
from click._termui_impl import Editor
12+
from click._utils import UNSET
1213
from click.exceptions import BadParameter
1314
from click.exceptions import MissingParameter
1415

@@ -717,6 +718,58 @@ def cmd(arg1):
717718
assert "my-default-value" not in result.output
718719

719720

721+
@pytest.mark.parametrize(
722+
("show_default", "default", "user_input", "in_prompt", "not_in_prompt"),
723+
[
724+
# Regular string replaces the actual default in the prompt.
725+
("custom", "actual", "\n", "(custom)", "actual"),
726+
# String with spaces.
727+
("custom label", "actual", "\n", "(custom label)", "actual"),
728+
# Unicode characters.
729+
("∞", "0", "\n", "(∞)", None),
730+
# Numeric default: custom string hides the number.
731+
("unlimited", 42, "\n", "(unlimited)", "42"),
732+
# Explicit default=None: custom string still appears, must provide input.
733+
("computed at runtime", None, "value\n", "(computed at runtime)", None),
734+
# No default kwarg at all (internal UNSET sentinel): same as None.
735+
("computed at runtime", UNSET, "value\n", "(computed at runtime)", None),
736+
# Empty string is falsy: suppresses any default display.
737+
("", "actual", "\n", None, "actual"),
738+
],
739+
ids=[
740+
"simple-string",
741+
"string-with-spaces",
742+
"unicode",
743+
"numeric-default",
744+
"default-is-none",
745+
"default-is-unset",
746+
"empty-string-is-falsy",
747+
],
748+
)
749+
def test_string_show_default_in_prompt(
750+
runner, show_default, default, user_input, in_prompt, not_in_prompt
751+
):
752+
"""When show_default is a string, the prompt should display that
753+
string in parentheses instead of the actual default value,
754+
matching the help text behavior. See pallets/click#2836."""
755+
756+
option_kwargs = {"show_default": show_default, "prompt": True}
757+
if default is not UNSET:
758+
option_kwargs["default"] = default
759+
760+
@click.command()
761+
@click.option("--arg1", **option_kwargs)
762+
def cmd(arg1):
763+
click.echo(arg1)
764+
765+
result = runner.invoke(cmd, input=user_input, standalone_mode=False)
766+
prompt_line = result.output.split("\n")[0]
767+
if in_prompt is not None:
768+
assert in_prompt in prompt_line
769+
if not_in_prompt is not None:
770+
assert not_in_prompt not in prompt_line
771+
772+
720773
REPEAT = object()
721774
"""Sentinel value to indicate that the prompt is expected to be repeated.
722775

0 commit comments

Comments
 (0)