Skip to content

Commit 376c272

Browse files
bpo-11874: fix assertion failure in argparse metavar handling (GH-1826)
- bugfix and test for fragile metavar handling in argparse (see bpo-24089, bpo-14046, bpo-25058, bpo-11874) - also fixes some incorrect tests that did not make 1-element tuples correctly (cherry picked from commit 66f02aa) Co-authored-by: wim glenn <wim.glenn@gmail.com>
1 parent a0accc0 commit 376c272

File tree

3 files changed

+40
-10
lines changed

3 files changed

+40
-10
lines changed

Lib/argparse.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,11 @@ def _format_usage(self, usage, actions, groups, prefix):
325325
if len(prefix) + len(usage) > text_width:
326326

327327
# break usage into wrappable parts
328-
part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
328+
part_regexp = (
329+
r'\(.*?\)+(?=\s|$)|'
330+
r'\[.*?\]+(?=\s|$)|'
331+
r'\S+'
332+
)
329333
opt_usage = format(optionals, groups)
330334
pos_usage = format(positionals, groups)
331335
opt_parts = _re.findall(part_regexp, opt_usage)

Lib/test/test_argparse.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4831,7 +4831,7 @@ def test_nargs_None_metavar_length0(self):
48314831
self.do_test_exception(nargs=None, metavar=tuple())
48324832

48334833
def test_nargs_None_metavar_length1(self):
4834-
self.do_test_no_exception(nargs=None, metavar=("1"))
4834+
self.do_test_no_exception(nargs=None, metavar=("1",))
48354835

48364836
def test_nargs_None_metavar_length2(self):
48374837
self.do_test_exception(nargs=None, metavar=("1", "2"))
@@ -4848,7 +4848,7 @@ def test_nargs_optional_metavar_length0(self):
48484848
self.do_test_exception(nargs="?", metavar=tuple())
48494849

48504850
def test_nargs_optional_metavar_length1(self):
4851-
self.do_test_no_exception(nargs="?", metavar=("1"))
4851+
self.do_test_no_exception(nargs="?", metavar=("1",))
48524852

48534853
def test_nargs_optional_metavar_length2(self):
48544854
self.do_test_exception(nargs="?", metavar=("1", "2"))
@@ -4865,7 +4865,7 @@ def test_nargs_zeroormore_metavar_length0(self):
48654865
self.do_test_exception(nargs="*", metavar=tuple())
48664866

48674867
def test_nargs_zeroormore_metavar_length1(self):
4868-
self.do_test_no_exception(nargs="*", metavar=("1"))
4868+
self.do_test_exception(nargs="*", metavar=("1",))
48694869

48704870
def test_nargs_zeroormore_metavar_length2(self):
48714871
self.do_test_no_exception(nargs="*", metavar=("1", "2"))
@@ -4882,7 +4882,7 @@ def test_nargs_oneormore_metavar_length0(self):
48824882
self.do_test_exception(nargs="+", metavar=tuple())
48834883

48844884
def test_nargs_oneormore_metavar_length1(self):
4885-
self.do_test_no_exception(nargs="+", metavar=("1"))
4885+
self.do_test_exception(nargs="+", metavar=("1",))
48864886

48874887
def test_nargs_oneormore_metavar_length2(self):
48884888
self.do_test_no_exception(nargs="+", metavar=("1", "2"))
@@ -4899,7 +4899,7 @@ def test_nargs_remainder_metavar_length0(self):
48994899
self.do_test_no_exception(nargs="...", metavar=tuple())
49004900

49014901
def test_nargs_remainder_metavar_length1(self):
4902-
self.do_test_no_exception(nargs="...", metavar=("1"))
4902+
self.do_test_no_exception(nargs="...", metavar=("1",))
49034903

49044904
def test_nargs_remainder_metavar_length2(self):
49054905
self.do_test_no_exception(nargs="...", metavar=("1", "2"))
@@ -4916,7 +4916,7 @@ def test_nargs_parser_metavar_length0(self):
49164916
self.do_test_exception(nargs="A...", metavar=tuple())
49174917

49184918
def test_nargs_parser_metavar_length1(self):
4919-
self.do_test_no_exception(nargs="A...", metavar=("1"))
4919+
self.do_test_no_exception(nargs="A...", metavar=("1",))
49204920

49214921
def test_nargs_parser_metavar_length2(self):
49224922
self.do_test_exception(nargs="A...", metavar=("1", "2"))
@@ -4933,7 +4933,7 @@ def test_nargs_1_metavar_length0(self):
49334933
self.do_test_exception(nargs=1, metavar=tuple())
49344934

49354935
def test_nargs_1_metavar_length1(self):
4936-
self.do_test_no_exception(nargs=1, metavar=("1"))
4936+
self.do_test_no_exception(nargs=1, metavar=("1",))
49374937

49384938
def test_nargs_1_metavar_length2(self):
49394939
self.do_test_exception(nargs=1, metavar=("1", "2"))
@@ -4950,7 +4950,7 @@ def test_nargs_2_metavar_length0(self):
49504950
self.do_test_exception(nargs=2, metavar=tuple())
49514951

49524952
def test_nargs_2_metavar_length1(self):
4953-
self.do_test_no_exception(nargs=2, metavar=("1"))
4953+
self.do_test_exception(nargs=2, metavar=("1",))
49544954

49554955
def test_nargs_2_metavar_length2(self):
49564956
self.do_test_no_exception(nargs=2, metavar=("1", "2"))
@@ -4967,7 +4967,7 @@ def test_nargs_3_metavar_length0(self):
49674967
self.do_test_exception(nargs=3, metavar=tuple())
49684968

49694969
def test_nargs_3_metavar_length1(self):
4970-
self.do_test_no_exception(nargs=3, metavar=("1"))
4970+
self.do_test_exception(nargs=3, metavar=("1",))
49714971

49724972
def test_nargs_3_metavar_length2(self):
49734973
self.do_test_exception(nargs=3, metavar=("1", "2"))
@@ -4994,6 +4994,30 @@ def test_all_exports_everything_but_modules(self):
49944994
]
49954995
self.assertEqual(sorted(items), sorted(argparse.__all__))
49964996

4997+
4998+
class TestWrappingMetavar(TestCase):
4999+
5000+
def setUp(self):
5001+
self.parser = ErrorRaisingArgumentParser(
5002+
'this_is_spammy_prog_with_a_long_name_sorry_about_the_name'
5003+
)
5004+
# this metavar was triggering library assertion errors due to usage
5005+
# message formatting incorrectly splitting on the ] chars within
5006+
metavar = '<http[s]://example:1234>'
5007+
self.parser.add_argument('--proxy', metavar=metavar)
5008+
5009+
def test_help_with_metavar(self):
5010+
help_text = self.parser.format_help()
5011+
self.assertEqual(help_text, textwrap.dedent('''\
5012+
usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name
5013+
[-h] [--proxy <http[s]://example:1234>]
5014+
5015+
optional arguments:
5016+
-h, --help show this help message and exit
5017+
--proxy <http[s]://example:1234>
5018+
'''))
5019+
5020+
49975021
def test_main():
49985022
support.run_unittest(__name__)
49995023
# Remove global references to avoid looking like we have refleaks.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Use a better regex when breaking usage into wrappable parts. Avoids bogus
2+
assertion errors from custom metavar strings.

0 commit comments

Comments
 (0)