Skip to content

Commit 4e6bd24

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 458ed1b commit 4e6bd24

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
@@ -324,7 +324,11 @@ def _format_usage(self, usage, actions, groups, prefix):
324324
if len(prefix) + len(usage) > text_width:
325325

326326
# break usage into wrappable parts
327-
part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
327+
part_regexp = (
328+
r'\(.*?\)+(?=\s|$)|'
329+
r'\[.*?\]+(?=\s|$)|'
330+
r'\S+'
331+
)
328332
opt_usage = format(optionals, groups)
329333
pos_usage = format(positionals, groups)
330334
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
@@ -4652,7 +4652,7 @@ def test_nargs_None_metavar_length0(self):
46524652
self.do_test_exception(nargs=None, metavar=tuple())
46534653

46544654
def test_nargs_None_metavar_length1(self):
4655-
self.do_test_no_exception(nargs=None, metavar=("1"))
4655+
self.do_test_no_exception(nargs=None, metavar=("1",))
46564656

46574657
def test_nargs_None_metavar_length2(self):
46584658
self.do_test_exception(nargs=None, metavar=("1", "2"))
@@ -4669,7 +4669,7 @@ def test_nargs_optional_metavar_length0(self):
46694669
self.do_test_exception(nargs="?", metavar=tuple())
46704670

46714671
def test_nargs_optional_metavar_length1(self):
4672-
self.do_test_no_exception(nargs="?", metavar=("1"))
4672+
self.do_test_no_exception(nargs="?", metavar=("1",))
46734673

46744674
def test_nargs_optional_metavar_length2(self):
46754675
self.do_test_exception(nargs="?", metavar=("1", "2"))
@@ -4686,7 +4686,7 @@ def test_nargs_zeroormore_metavar_length0(self):
46864686
self.do_test_exception(nargs="*", metavar=tuple())
46874687

46884688
def test_nargs_zeroormore_metavar_length1(self):
4689-
self.do_test_no_exception(nargs="*", metavar=("1"))
4689+
self.do_test_exception(nargs="*", metavar=("1",))
46904690

46914691
def test_nargs_zeroormore_metavar_length2(self):
46924692
self.do_test_no_exception(nargs="*", metavar=("1", "2"))
@@ -4703,7 +4703,7 @@ def test_nargs_oneormore_metavar_length0(self):
47034703
self.do_test_exception(nargs="+", metavar=tuple())
47044704

47054705
def test_nargs_oneormore_metavar_length1(self):
4706-
self.do_test_no_exception(nargs="+", metavar=("1"))
4706+
self.do_test_exception(nargs="+", metavar=("1",))
47074707

47084708
def test_nargs_oneormore_metavar_length2(self):
47094709
self.do_test_no_exception(nargs="+", metavar=("1", "2"))
@@ -4720,7 +4720,7 @@ def test_nargs_remainder_metavar_length0(self):
47204720
self.do_test_no_exception(nargs="...", metavar=tuple())
47214721

47224722
def test_nargs_remainder_metavar_length1(self):
4723-
self.do_test_no_exception(nargs="...", metavar=("1"))
4723+
self.do_test_no_exception(nargs="...", metavar=("1",))
47244724

47254725
def test_nargs_remainder_metavar_length2(self):
47264726
self.do_test_no_exception(nargs="...", metavar=("1", "2"))
@@ -4737,7 +4737,7 @@ def test_nargs_parser_metavar_length0(self):
47374737
self.do_test_exception(nargs="A...", metavar=tuple())
47384738

47394739
def test_nargs_parser_metavar_length1(self):
4740-
self.do_test_no_exception(nargs="A...", metavar=("1"))
4740+
self.do_test_no_exception(nargs="A...", metavar=("1",))
47414741

47424742
def test_nargs_parser_metavar_length2(self):
47434743
self.do_test_exception(nargs="A...", metavar=("1", "2"))
@@ -4754,7 +4754,7 @@ def test_nargs_1_metavar_length0(self):
47544754
self.do_test_exception(nargs=1, metavar=tuple())
47554755

47564756
def test_nargs_1_metavar_length1(self):
4757-
self.do_test_no_exception(nargs=1, metavar=("1"))
4757+
self.do_test_no_exception(nargs=1, metavar=("1",))
47584758

47594759
def test_nargs_1_metavar_length2(self):
47604760
self.do_test_exception(nargs=1, metavar=("1", "2"))
@@ -4771,7 +4771,7 @@ def test_nargs_2_metavar_length0(self):
47714771
self.do_test_exception(nargs=2, metavar=tuple())
47724772

47734773
def test_nargs_2_metavar_length1(self):
4774-
self.do_test_no_exception(nargs=2, metavar=("1"))
4774+
self.do_test_exception(nargs=2, metavar=("1",))
47754775

47764776
def test_nargs_2_metavar_length2(self):
47774777
self.do_test_no_exception(nargs=2, metavar=("1", "2"))
@@ -4788,7 +4788,7 @@ def test_nargs_3_metavar_length0(self):
47884788
self.do_test_exception(nargs=3, metavar=tuple())
47894789

47904790
def test_nargs_3_metavar_length1(self):
4791-
self.do_test_no_exception(nargs=3, metavar=("1"))
4791+
self.do_test_exception(nargs=3, metavar=("1",))
47924792

47934793
def test_nargs_3_metavar_length2(self):
47944794
self.do_test_exception(nargs=3, metavar=("1", "2"))
@@ -4815,6 +4815,30 @@ def test_all_exports_everything_but_modules(self):
48154815
]
48164816
self.assertEqual(sorted(items), sorted(argparse.__all__))
48174817

4818+
4819+
class TestWrappingMetavar(TestCase):
4820+
4821+
def setUp(self):
4822+
self.parser = ErrorRaisingArgumentParser(
4823+
'this_is_spammy_prog_with_a_long_name_sorry_about_the_name'
4824+
)
4825+
# this metavar was triggering library assertion errors due to usage
4826+
# message formatting incorrectly splitting on the ] chars within
4827+
metavar = '<http[s]://example:1234>'
4828+
self.parser.add_argument('--proxy', metavar=metavar)
4829+
4830+
def test_help_with_metavar(self):
4831+
help_text = self.parser.format_help()
4832+
self.assertEqual(help_text, textwrap.dedent('''\
4833+
usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name
4834+
[-h] [--proxy <http[s]://example:1234>]
4835+
4836+
optional arguments:
4837+
-h, --help show this help message and exit
4838+
--proxy <http[s]://example:1234>
4839+
'''))
4840+
4841+
48184842
def test_main():
48194843
# silence warnings about version argument - these are expected
48204844
with test_support.check_warnings(
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)