Skip to content
Open
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
8 changes: 5 additions & 3 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ def _get_actions_usage_parts(self, actions, groups):
return parts, pos_start

def _format_text(self, text):
text = str(text)
if '%(prog)' in text:
text = text % dict(prog=self._prog)
text_width = max(self._width - self._current_indent, 11)
Expand All @@ -564,6 +565,7 @@ def _apply_text_markup(self, text):
When colors are disabled, backticks are preserved as-is.
"""
text = str(text)
t = self._theme
if not t.reset:
return text
Expand Down Expand Up @@ -610,9 +612,9 @@ def _format_action(self, action):
parts = [action_header]

# if there was help for the action, add lines of help text
if action.help and action.help.strip():
if action.help:
help_text = self._expand_help(action)
if help_text:
if help_text.strip():
help_lines = self._split_lines(help_text, help_width)
parts.append('%*s%s\n' % (indent_first, '', help_lines[0]))
for line in help_lines[1:]:
Expand Down Expand Up @@ -711,7 +713,7 @@ def _format_args(self, action, default_metavar):
return result

def _expand_help(self, action):
help_string = self._get_help_string(action)
help_string = str(self._get_help_string(action))
if '%' not in help_string:
return self._apply_text_markup(help_string)
params = dict(vars(action), prog=self._prog)
Expand Down
73 changes: 73 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -7398,6 +7398,19 @@ def setUp(self):
lambda *args, **kwargs: True))
self.theme = _colorize.get_theme(force_color=True).argparse

class _LazyStr:
def __init__(self, message):
self._message = message

def __str__(self):
return self._message

def __contains__(self, item):
return item in self._message

def __mod__(self, other):
return self._message % other

def test_argparse_color(self):
# Arrange: create a parser with a bit of everything
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -7736,6 +7749,38 @@ def test_backtick_markup_in_description(self):
)
self.assertNotIn("`", help_text)

def test_backtick_markup_in_description_accepts_string_like_proxy(self):
parser = argparse.ArgumentParser(
prog='PROG',
color=True,
description=self._LazyStr(
'Run `python myapp` or ``python -m myapp`` to start.'
),
)

prog_extra = self.theme.prog_extra
reset = self.theme.reset

help_text = parser.format_help()
self.assertIn(
f'Run {prog_extra}python myapp{reset} or '
f'{prog_extra}python -m myapp{reset} to start.',
help_text,
)

def test_backtick_markup_in_epilog_accepts_string_like_proxy(self):
parser = argparse.ArgumentParser(
prog='myapp',
color=True,
epilog=self._LazyStr('Run `%(prog)s --help` for more info.'),
)

prog_extra = self.theme.prog_extra
reset = self.theme.reset

help_text = parser.format_help()
self.assertIn(f'{prog_extra}myapp --help{reset}', help_text)

def test_backtick_markup_multiple(self):
parser = argparse.ArgumentParser(
prog='PROG',
Expand Down Expand Up @@ -7837,6 +7882,34 @@ def test_backtick_markup_in_argument_help_color_disabled(self):
self.assertIn("set the `foo` value", help_text)
self.assertNotIn("\x1b[", help_text)

def test_backtick_markup_in_argument_help_accepts_string_like_proxy(self):
parser = argparse.ArgumentParser(prog="PROG", color=True)
parser.add_argument(
"--foo", help=self._LazyStr("set the `foo` value")
)

prog_extra = self.theme.prog_extra
reset = self.theme.reset

help_text = parser.format_help()
self.assertIn(f"set the {prog_extra}foo{reset} value", help_text)

def test_argument_help_interpolation_accepts_string_like_proxy(self):
parser = argparse.ArgumentParser(prog="PROG", color=True)
parser.add_argument(
"--foo",
default="bar",
help=self._LazyStr("set `foo` (default: %(default)s)"),
)

prog_extra = self.theme.prog_extra
interp = self.theme.interpolated_value
reset = self.theme.reset

help_text = parser.format_help()
self.assertIn(f"set {prog_extra}foo{reset}", help_text)
self.assertIn(f"default: {interp}bar{reset}", help_text)

def test_help_with_format_specifiers(self):
# GH-142950: format specifiers like %x should work with color=True
parser = argparse.ArgumentParser(prog='PROG', color=True)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a regression in :mod:`argparse` so colorized help formatting once again
accepts string-like proxy objects for descriptions, epilogs, and argument
help text.
Loading