Skip to content

Commit 66f02aa

Browse files
wimglennncoghlan
authored andcommitted
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
1 parent ff6c077 commit 66f02aa

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

329329
# break usage into wrappable parts
330-
part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
330+
part_regexp = (
331+
r'\(.*?\)+(?=\s|$)|'
332+
r'\[.*?\]+(?=\s|$)|'
333+
r'\S+'
334+
)
331335
opt_usage = format(optionals, groups)
332336
pos_usage = format(positionals, groups)
333337
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
@@ -4955,7 +4955,7 @@ def test_nargs_None_metavar_length0(self):
49554955
self.do_test_exception(nargs=None, metavar=tuple())
49564956

49574957
def test_nargs_None_metavar_length1(self):
4958-
self.do_test_no_exception(nargs=None, metavar=("1"))
4958+
self.do_test_no_exception(nargs=None, metavar=("1",))
49594959

49604960
def test_nargs_None_metavar_length2(self):
49614961
self.do_test_exception(nargs=None, metavar=("1", "2"))
@@ -4972,7 +4972,7 @@ def test_nargs_optional_metavar_length0(self):
49724972
self.do_test_exception(nargs="?", metavar=tuple())
49734973

49744974
def test_nargs_optional_metavar_length1(self):
4975-
self.do_test_no_exception(nargs="?", metavar=("1"))
4975+
self.do_test_no_exception(nargs="?", metavar=("1",))
49764976

49774977
def test_nargs_optional_metavar_length2(self):
49784978
self.do_test_exception(nargs="?", metavar=("1", "2"))
@@ -4989,7 +4989,7 @@ def test_nargs_zeroormore_metavar_length0(self):
49894989
self.do_test_exception(nargs="*", metavar=tuple())
49904990

49914991
def test_nargs_zeroormore_metavar_length1(self):
4992-
self.do_test_no_exception(nargs="*", metavar=("1"))
4992+
self.do_test_exception(nargs="*", metavar=("1",))
49934993

49944994
def test_nargs_zeroormore_metavar_length2(self):
49954995
self.do_test_no_exception(nargs="*", metavar=("1", "2"))
@@ -5006,7 +5006,7 @@ def test_nargs_oneormore_metavar_length0(self):
50065006
self.do_test_exception(nargs="+", metavar=tuple())
50075007

50085008
def test_nargs_oneormore_metavar_length1(self):
5009-
self.do_test_no_exception(nargs="+", metavar=("1"))
5009+
self.do_test_exception(nargs="+", metavar=("1",))
50105010

50115011
def test_nargs_oneormore_metavar_length2(self):
50125012
self.do_test_no_exception(nargs="+", metavar=("1", "2"))
@@ -5023,7 +5023,7 @@ def test_nargs_remainder_metavar_length0(self):
50235023
self.do_test_no_exception(nargs="...", metavar=tuple())
50245024

50255025
def test_nargs_remainder_metavar_length1(self):
5026-
self.do_test_no_exception(nargs="...", metavar=("1"))
5026+
self.do_test_no_exception(nargs="...", metavar=("1",))
50275027

50285028
def test_nargs_remainder_metavar_length2(self):
50295029
self.do_test_no_exception(nargs="...", metavar=("1", "2"))
@@ -5040,7 +5040,7 @@ def test_nargs_parser_metavar_length0(self):
50405040
self.do_test_exception(nargs="A...", metavar=tuple())
50415041

50425042
def test_nargs_parser_metavar_length1(self):
5043-
self.do_test_no_exception(nargs="A...", metavar=("1"))
5043+
self.do_test_no_exception(nargs="A...", metavar=("1",))
50445044

50455045
def test_nargs_parser_metavar_length2(self):
50465046
self.do_test_exception(nargs="A...", metavar=("1", "2"))
@@ -5057,7 +5057,7 @@ def test_nargs_1_metavar_length0(self):
50575057
self.do_test_exception(nargs=1, metavar=tuple())
50585058

50595059
def test_nargs_1_metavar_length1(self):
5060-
self.do_test_no_exception(nargs=1, metavar=("1"))
5060+
self.do_test_no_exception(nargs=1, metavar=("1",))
50615061

50625062
def test_nargs_1_metavar_length2(self):
50635063
self.do_test_exception(nargs=1, metavar=("1", "2"))
@@ -5074,7 +5074,7 @@ def test_nargs_2_metavar_length0(self):
50745074
self.do_test_exception(nargs=2, metavar=tuple())
50755075

50765076
def test_nargs_2_metavar_length1(self):
5077-
self.do_test_no_exception(nargs=2, metavar=("1"))
5077+
self.do_test_exception(nargs=2, metavar=("1",))
50785078

50795079
def test_nargs_2_metavar_length2(self):
50805080
self.do_test_no_exception(nargs=2, metavar=("1", "2"))
@@ -5091,7 +5091,7 @@ def test_nargs_3_metavar_length0(self):
50915091
self.do_test_exception(nargs=3, metavar=tuple())
50925092

50935093
def test_nargs_3_metavar_length1(self):
5094-
self.do_test_no_exception(nargs=3, metavar=("1"))
5094+
self.do_test_exception(nargs=3, metavar=("1",))
50955095

50965096
def test_nargs_3_metavar_length2(self):
50975097
self.do_test_exception(nargs=3, metavar=("1", "2"))
@@ -5118,6 +5118,30 @@ def test_all_exports_everything_but_modules(self):
51185118
]
51195119
self.assertEqual(sorted(items), sorted(argparse.__all__))
51205120

5121+
5122+
class TestWrappingMetavar(TestCase):
5123+
5124+
def setUp(self):
5125+
self.parser = ErrorRaisingArgumentParser(
5126+
'this_is_spammy_prog_with_a_long_name_sorry_about_the_name'
5127+
)
5128+
# this metavar was triggering library assertion errors due to usage
5129+
# message formatting incorrectly splitting on the ] chars within
5130+
metavar = '<http[s]://example:1234>'
5131+
self.parser.add_argument('--proxy', metavar=metavar)
5132+
5133+
def test_help_with_metavar(self):
5134+
help_text = self.parser.format_help()
5135+
self.assertEqual(help_text, textwrap.dedent('''\
5136+
usage: this_is_spammy_prog_with_a_long_name_sorry_about_the_name
5137+
[-h] [--proxy <http[s]://example:1234>]
5138+
5139+
optional arguments:
5140+
-h, --help show this help message and exit
5141+
--proxy <http[s]://example:1234>
5142+
'''))
5143+
5144+
51215145
def test_main():
51225146
support.run_unittest(__name__)
51235147
# 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)