Skip to content

Commit 1136bd2

Browse files
committed
Merge remote-tracking branch 'upstream/main' into jit/gc
2 parents a166b47 + c90863a commit 1136bd2

File tree

13 files changed

+227
-16
lines changed

13 files changed

+227
-16
lines changed

Doc/library/argparse.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,27 @@ are set.
645645

646646
.. versionadded:: 3.14
647647

648+
To highlight inline code in your description or epilog text, you can use
649+
backticks::
650+
651+
>>> parser = argparse.ArgumentParser(
652+
... formatter_class=argparse.RawDescriptionHelpFormatter,
653+
... epilog='''Examples:
654+
... `python -m myapp --verbose`
655+
... `python -m myapp --config settings.json`
656+
... ''')
657+
658+
When colors are enabled, the text inside backticks will be displayed in a
659+
distinct color to help examples stand out. When colors are disabled, backticks
660+
are preserved as-is, which is readable in plain text.
661+
662+
.. note::
663+
664+
Backtick markup only applies to description and epilog text. It does not
665+
apply to individual argument ``help`` strings.
666+
667+
.. versionadded:: 3.15
668+
648669

649670
The add_argument() method
650671
-------------------------

Doc/library/importlib.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ Functions
210210
:exc:`ModuleNotFoundError` is raised when the module being reloaded lacks
211211
a :class:`~importlib.machinery.ModuleSpec`.
212212

213+
.. versionchanged:: next
214+
If *module* is a lazy module that has not yet been materialized (i.e.,
215+
loaded via :class:`importlib.util.LazyLoader` and not yet accessed),
216+
calling :func:`reload` is a no-op and returns the module unchanged.
217+
This prevents the reload from unintentionally triggering the lazy load.
218+
213219
.. warning::
214220
This function is not thread-safe. Calling it from multiple threads can result
215221
in unexpected behavior. It's recommended to use the :class:`threading.Lock`

Doc/library/typing.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2869,8 +2869,8 @@ ABCs and Protocols for working with I/O
28692869
---------------------------------------
28702870

28712871
.. class:: IO[AnyStr]
2872-
TextIO[AnyStr]
2873-
BinaryIO[AnyStr]
2872+
TextIO
2873+
BinaryIO
28742874

28752875
Generic class ``IO[AnyStr]`` and its subclasses ``TextIO(IO[str])``
28762876
and ``BinaryIO(IO[bytes])``

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,10 @@ argparse
423423
default to ``True``. This enables suggestions for mistyped arguments by default.
424424
(Contributed by Jakob Schluse in :gh:`140450`.)
425425

426+
* Added backtick markup support in description and epilog text to highlight
427+
inline code when color output is enabled.
428+
(Contributed by Savannah Ostrowski in :gh:`142390`.)
429+
426430
calendar
427431
--------
428432

Lib/argparse.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,27 @@ def _format_text(self, text):
517517
text = text % dict(prog=self._prog)
518518
text_width = max(self._width - self._current_indent, 11)
519519
indent = ' ' * self._current_indent
520-
return self._fill_text(text, text_width, indent) + '\n\n'
520+
text = self._fill_text(text, text_width, indent)
521+
text = self._apply_text_markup(text)
522+
return text + '\n\n'
523+
524+
def _apply_text_markup(self, text):
525+
"""Apply color markup to text.
526+
527+
Supported markup:
528+
`...` - inline code (rendered with prog_extra color)
529+
530+
When colors are disabled, backticks are preserved as-is.
531+
"""
532+
t = self._theme
533+
if not t.reset:
534+
return text
535+
text = _re.sub(
536+
r'`([^`]+)`',
537+
rf'{t.prog_extra}\1{t.reset}',
538+
text,
539+
)
540+
return text
521541

522542
def _format_action(self, action):
523543
# determine the required width and the entry label

Lib/importlib/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ def reload(module):
9797
The module must have been successfully imported before.
9898
9999
"""
100+
# If a LazyModule has not yet been materialized, reload is a no-op.
101+
if importlib_util := sys.modules.get('importlib.util'):
102+
if lazy_module_type := getattr(importlib_util, '_LazyModule', None):
103+
if isinstance(module, lazy_module_type):
104+
return module
100105
try:
101106
name = module.__spec__.name
102107
except AttributeError:

Lib/test/test_argparse.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7568,6 +7568,101 @@ def test_error_and_warning_not_colorized_when_disabled(self):
75687568
self.assertNotIn('\x1b[', warn)
75697569
self.assertIn('warning:', warn)
75707570

7571+
def test_backtick_markup_in_epilog(self):
7572+
parser = argparse.ArgumentParser(
7573+
prog='PROG',
7574+
color=True,
7575+
epilog='Example: `python -m myapp --verbose`',
7576+
)
7577+
7578+
prog_extra = self.theme.prog_extra
7579+
reset = self.theme.reset
7580+
7581+
help_text = parser.format_help()
7582+
self.assertIn(f'Example: {prog_extra}python -m myapp --verbose{reset}',
7583+
help_text)
7584+
self.assertNotIn('`', help_text)
7585+
7586+
def test_backtick_markup_in_description(self):
7587+
parser = argparse.ArgumentParser(
7588+
prog='PROG',
7589+
color=True,
7590+
description='Run `python -m myapp` to start.',
7591+
)
7592+
7593+
prog_extra = self.theme.prog_extra
7594+
reset = self.theme.reset
7595+
7596+
help_text = parser.format_help()
7597+
self.assertIn(f'Run {prog_extra}python -m myapp{reset} to start.',
7598+
help_text)
7599+
7600+
def test_backtick_markup_multiple(self):
7601+
parser = argparse.ArgumentParser(
7602+
prog='PROG',
7603+
color=True,
7604+
epilog='Try `app run` or `app test`.',
7605+
)
7606+
7607+
prog_extra = self.theme.prog_extra
7608+
reset = self.theme.reset
7609+
7610+
help_text = parser.format_help()
7611+
self.assertIn(f'{prog_extra}app run{reset}', help_text)
7612+
self.assertIn(f'{prog_extra}app test{reset}', help_text)
7613+
7614+
def test_backtick_markup_not_applied_when_color_disabled(self):
7615+
# When color is disabled, backticks are preserved as-is
7616+
parser = argparse.ArgumentParser(
7617+
prog='PROG',
7618+
color=False,
7619+
epilog='Example: `python -m myapp`',
7620+
)
7621+
7622+
help_text = parser.format_help()
7623+
self.assertIn('`python -m myapp`', help_text)
7624+
self.assertNotIn('\x1b[', help_text)
7625+
7626+
def test_backtick_markup_with_format_string(self):
7627+
parser = argparse.ArgumentParser(
7628+
prog='myapp',
7629+
color=True,
7630+
epilog='Run `%(prog)s --help` for more info.',
7631+
)
7632+
7633+
prog_extra = self.theme.prog_extra
7634+
reset = self.theme.reset
7635+
7636+
help_text = parser.format_help()
7637+
self.assertIn(f'{prog_extra}myapp --help{reset}', help_text)
7638+
7639+
def test_backtick_markup_in_subparser(self):
7640+
parser = argparse.ArgumentParser(prog='PROG', color=True)
7641+
subparsers = parser.add_subparsers()
7642+
sub = subparsers.add_parser(
7643+
'sub',
7644+
description='Run `PROG sub --foo` to start.',
7645+
)
7646+
7647+
prog_extra = self.theme.prog_extra
7648+
reset = self.theme.reset
7649+
7650+
help_text = sub.format_help()
7651+
self.assertIn(f'{prog_extra}PROG sub --foo{reset}', help_text)
7652+
7653+
def test_backtick_markup_special_regex_chars(self):
7654+
parser = argparse.ArgumentParser(
7655+
prog='PROG',
7656+
color=True,
7657+
epilog='`grep "foo.*bar" | sort`',
7658+
)
7659+
7660+
prog_extra = self.theme.prog_extra
7661+
reset = self.theme.reset
7662+
7663+
help_text = parser.format_help()
7664+
self.assertIn(f'{prog_extra}grep "foo.*bar" | sort{reset}', help_text)
7665+
75717666
def test_print_help_uses_target_file_for_color_decision(self):
75727667
parser = argparse.ArgumentParser(prog='PROG', color=True)
75737668
parser.add_argument('--opt')

Lib/test/test_importlib/test_lazy.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from test.support import threading_helper
1111
from test.test_importlib import util as test_util
1212

13+
# Make sure sys.modules[util] is in sync with the import.
14+
# That is needed as other tests may reload util.
15+
sys.modules['importlib.util'] = util
1316

1417
class CollectInit:
1518

@@ -192,7 +195,7 @@ def test_lazy_self_referential_modules(self):
192195
sys.modules['json'] = module
193196
loader.exec_module(module)
194197

195-
# Trigger load with attribute lookup, ensure expected behavior
198+
# Trigger load with attribute lookup, ensure expected behavior.
196199
test_load = module.loads('{}')
197200
self.assertEqual(test_load, {})
198201

@@ -224,6 +227,26 @@ def __delattr__(self, name):
224227
with self.assertRaises(AttributeError):
225228
del module.CONSTANT
226229

230+
def test_reload(self):
231+
# Reloading a lazy module that hasn't been materialized is a no-op.
232+
module = self.new_module()
233+
sys.modules[TestingImporter.module_name] = module
234+
235+
# Change the source code to add a new attribute.
236+
TestingImporter.source_code = 'attr = 42\nnew_attr = 123\n__name__ = {!r}'.format(TestingImporter.mutated_name)
237+
self.assertIsInstance(module, util._LazyModule)
238+
239+
# Reload the module (should be a no-op since not materialized).
240+
reloaded = importlib.reload(module)
241+
self.assertIs(reloaded, module)
242+
self.assertIsInstance(module, util._LazyModule)
243+
244+
# Access the new attribute (should trigger materialization, and new_attr should exist).
245+
self.assertEqual(module.attr, 42)
246+
self.assertNotIsInstance(module, util._LazyModule)
247+
self.assertTrue(hasattr(module, 'new_attr'))
248+
self.assertEqual(module.new_attr, 123)
249+
227250

228251
if __name__ == '__main__':
229252
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make importlib.reload no-op for lazy modules.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add backtick markup support in :mod:`argparse` description and epilog text to highlight inline code when color output is enabled.

0 commit comments

Comments
 (0)