Commit 66f02aa3 authored by wim glenn's avatar wim glenn Committed by Nick Coghlan

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
parent ff6c0772
...@@ -327,7 +327,11 @@ class HelpFormatter(object): ...@@ -327,7 +327,11 @@ class HelpFormatter(object):
if len(prefix) + len(usage) > text_width: if len(prefix) + len(usage) > text_width:
# break usage into wrappable parts # break usage into wrappable parts
part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' part_regexp = (
r'\(.*?\)+(?=\s|$)|'
r'\[.*?\]+(?=\s|$)|'
r'\S+'
)
opt_usage = format(optionals, groups) opt_usage = format(optionals, groups)
pos_usage = format(positionals, groups) pos_usage = format(positionals, groups)
opt_parts = _re.findall(part_regexp, opt_usage) opt_parts = _re.findall(part_regexp, opt_usage)
......
...@@ -4955,7 +4955,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -4955,7 +4955,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs=None, metavar=tuple()) self.do_test_exception(nargs=None, metavar=tuple())
def test_nargs_None_metavar_length1(self): def test_nargs_None_metavar_length1(self):
self.do_test_no_exception(nargs=None, metavar=("1")) self.do_test_no_exception(nargs=None, metavar=("1",))
def test_nargs_None_metavar_length2(self): def test_nargs_None_metavar_length2(self):
self.do_test_exception(nargs=None, metavar=("1", "2")) self.do_test_exception(nargs=None, metavar=("1", "2"))
...@@ -4972,7 +4972,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -4972,7 +4972,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs="?", metavar=tuple()) self.do_test_exception(nargs="?", metavar=tuple())
def test_nargs_optional_metavar_length1(self): def test_nargs_optional_metavar_length1(self):
self.do_test_no_exception(nargs="?", metavar=("1")) self.do_test_no_exception(nargs="?", metavar=("1",))
def test_nargs_optional_metavar_length2(self): def test_nargs_optional_metavar_length2(self):
self.do_test_exception(nargs="?", metavar=("1", "2")) self.do_test_exception(nargs="?", metavar=("1", "2"))
...@@ -4989,7 +4989,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -4989,7 +4989,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs="*", metavar=tuple()) self.do_test_exception(nargs="*", metavar=tuple())
def test_nargs_zeroormore_metavar_length1(self): def test_nargs_zeroormore_metavar_length1(self):
self.do_test_no_exception(nargs="*", metavar=("1")) self.do_test_exception(nargs="*", metavar=("1",))
def test_nargs_zeroormore_metavar_length2(self): def test_nargs_zeroormore_metavar_length2(self):
self.do_test_no_exception(nargs="*", metavar=("1", "2")) self.do_test_no_exception(nargs="*", metavar=("1", "2"))
...@@ -5006,7 +5006,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -5006,7 +5006,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs="+", metavar=tuple()) self.do_test_exception(nargs="+", metavar=tuple())
def test_nargs_oneormore_metavar_length1(self): def test_nargs_oneormore_metavar_length1(self):
self.do_test_no_exception(nargs="+", metavar=("1")) self.do_test_exception(nargs="+", metavar=("1",))
def test_nargs_oneormore_metavar_length2(self): def test_nargs_oneormore_metavar_length2(self):
self.do_test_no_exception(nargs="+", metavar=("1", "2")) self.do_test_no_exception(nargs="+", metavar=("1", "2"))
...@@ -5023,7 +5023,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -5023,7 +5023,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_no_exception(nargs="...", metavar=tuple()) self.do_test_no_exception(nargs="...", metavar=tuple())
def test_nargs_remainder_metavar_length1(self): def test_nargs_remainder_metavar_length1(self):
self.do_test_no_exception(nargs="...", metavar=("1")) self.do_test_no_exception(nargs="...", metavar=("1",))
def test_nargs_remainder_metavar_length2(self): def test_nargs_remainder_metavar_length2(self):
self.do_test_no_exception(nargs="...", metavar=("1", "2")) self.do_test_no_exception(nargs="...", metavar=("1", "2"))
...@@ -5040,7 +5040,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -5040,7 +5040,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs="A...", metavar=tuple()) self.do_test_exception(nargs="A...", metavar=tuple())
def test_nargs_parser_metavar_length1(self): def test_nargs_parser_metavar_length1(self):
self.do_test_no_exception(nargs="A...", metavar=("1")) self.do_test_no_exception(nargs="A...", metavar=("1",))
def test_nargs_parser_metavar_length2(self): def test_nargs_parser_metavar_length2(self):
self.do_test_exception(nargs="A...", metavar=("1", "2")) self.do_test_exception(nargs="A...", metavar=("1", "2"))
...@@ -5057,7 +5057,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -5057,7 +5057,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs=1, metavar=tuple()) self.do_test_exception(nargs=1, metavar=tuple())
def test_nargs_1_metavar_length1(self): def test_nargs_1_metavar_length1(self):
self.do_test_no_exception(nargs=1, metavar=("1")) self.do_test_no_exception(nargs=1, metavar=("1",))
def test_nargs_1_metavar_length2(self): def test_nargs_1_metavar_length2(self):
self.do_test_exception(nargs=1, metavar=("1", "2")) self.do_test_exception(nargs=1, metavar=("1", "2"))
...@@ -5074,7 +5074,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -5074,7 +5074,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs=2, metavar=tuple()) self.do_test_exception(nargs=2, metavar=tuple())
def test_nargs_2_metavar_length1(self): def test_nargs_2_metavar_length1(self):
self.do_test_no_exception(nargs=2, metavar=("1")) self.do_test_exception(nargs=2, metavar=("1",))
def test_nargs_2_metavar_length2(self): def test_nargs_2_metavar_length2(self):
self.do_test_no_exception(nargs=2, metavar=("1", "2")) self.do_test_no_exception(nargs=2, metavar=("1", "2"))
...@@ -5091,7 +5091,7 @@ class TestAddArgumentMetavar(TestCase): ...@@ -5091,7 +5091,7 @@ class TestAddArgumentMetavar(TestCase):
self.do_test_exception(nargs=3, metavar=tuple()) self.do_test_exception(nargs=3, metavar=tuple())
def test_nargs_3_metavar_length1(self): def test_nargs_3_metavar_length1(self):
self.do_test_no_exception(nargs=3, metavar=("1")) self.do_test_exception(nargs=3, metavar=("1",))
def test_nargs_3_metavar_length2(self): def test_nargs_3_metavar_length2(self):
self.do_test_exception(nargs=3, metavar=("1", "2")) self.do_test_exception(nargs=3, metavar=("1", "2"))
...@@ -5118,6 +5118,30 @@ class TestImportStar(TestCase): ...@@ -5118,6 +5118,30 @@ class TestImportStar(TestCase):
] ]
self.assertEqual(sorted(items), sorted(argparse.__all__)) self.assertEqual(sorted(items), sorted(argparse.__all__))
class TestWrappingMetavar(TestCase):
def setUp(self):
self.parser = ErrorRaisingArgumentParser(
'this_is_spammy_prog_with_a_long_name_sorry_about_the_name'
)
# this metavar was triggering library assertion errors due to usage
# message formatting incorrectly splitting on the ] chars within
metavar = '<http[s]://example:1234>'
self.parser.add_argument('--proxy', metavar=metavar)
def test_help_with_metavar(self):
help_text = self.parser.format_help()
self.assertEqual(help_text, textwrap.dedent('''\
usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name
[-h] [--proxy <http[s]://example:1234>]
optional arguments:
-h, --help show this help message and exit
--proxy <http[s]://example:1234>
'''))
def test_main(): def test_main():
support.run_unittest(__name__) support.run_unittest(__name__)
# Remove global references to avoid looking like we have refleaks. # Remove global references to avoid looking like we have refleaks.
......
Use a better regex when breaking usage into wrappable parts. Avoids bogus
assertion errors from custom metavar strings.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment