[Python-checkins] bpo-11874: fix assertion failure in argparse metavar handling (GH-1826)

Miss Islington (bot) webhook-mailer at python.org
Fri Jun 8 07:06:01 EDT 2018


https://github.com/python/cpython/commit/376c272d68cca0975ff0be3d12abf5f67da342d7
commit: 376c272d68cca0975ff0be3d12abf5f67da342d7
branch: 3.6
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-06-08T04:05:58-07:00
summary:

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 66f02aa32f1e4adb9f24cf186f8c495399d5ce9b)

Co-authored-by: wim glenn <wim.glenn at gmail.com>

files:
A Misc/NEWS.d/next/Library/2018-05-23-00-26-27.bpo-11874.glK5iP.rst
M Lib/argparse.py
M Lib/test/test_argparse.py

diff --git a/Lib/argparse.py b/Lib/argparse.py
index b69c5adfa072..1dacb7e8a009 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -325,7 +325,11 @@ def _format_usage(self, usage, actions, groups, prefix):
             if len(prefix) + len(usage) > text_width:
 
                 # break usage into wrappable parts
-                part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
+                part_regexp = (
+                    r'\(.*?\)+(?=\s|$)|'
+                    r'\[.*?\]+(?=\s|$)|'
+                    r'\S+'
+                )
                 opt_usage = format(optionals, groups)
                 pos_usage = format(positionals, groups)
                 opt_parts = _re.findall(part_regexp, opt_usage)
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index a5c4a8ec97d7..41a7f4145815 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -4831,7 +4831,7 @@ def test_nargs_None_metavar_length0(self):
         self.do_test_exception(nargs=None, metavar=tuple())
 
     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):
         self.do_test_exception(nargs=None, metavar=("1", "2"))
@@ -4848,7 +4848,7 @@ def test_nargs_optional_metavar_length0(self):
         self.do_test_exception(nargs="?", metavar=tuple())
 
     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):
         self.do_test_exception(nargs="?", metavar=("1", "2"))
@@ -4865,7 +4865,7 @@ def test_nargs_zeroormore_metavar_length0(self):
         self.do_test_exception(nargs="*", metavar=tuple())
 
     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):
         self.do_test_no_exception(nargs="*", metavar=("1", "2"))
@@ -4882,7 +4882,7 @@ def test_nargs_oneormore_metavar_length0(self):
         self.do_test_exception(nargs="+", metavar=tuple())
 
     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):
         self.do_test_no_exception(nargs="+", metavar=("1", "2"))
@@ -4899,7 +4899,7 @@ def test_nargs_remainder_metavar_length0(self):
         self.do_test_no_exception(nargs="...", metavar=tuple())
 
     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):
         self.do_test_no_exception(nargs="...", metavar=("1", "2"))
@@ -4916,7 +4916,7 @@ def test_nargs_parser_metavar_length0(self):
         self.do_test_exception(nargs="A...", metavar=tuple())
 
     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):
         self.do_test_exception(nargs="A...", metavar=("1", "2"))
@@ -4933,7 +4933,7 @@ def test_nargs_1_metavar_length0(self):
         self.do_test_exception(nargs=1, metavar=tuple())
 
     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):
         self.do_test_exception(nargs=1, metavar=("1", "2"))
@@ -4950,7 +4950,7 @@ def test_nargs_2_metavar_length0(self):
         self.do_test_exception(nargs=2, metavar=tuple())
 
     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):
         self.do_test_no_exception(nargs=2, metavar=("1", "2"))
@@ -4967,7 +4967,7 @@ def test_nargs_3_metavar_length0(self):
         self.do_test_exception(nargs=3, metavar=tuple())
 
     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):
         self.do_test_exception(nargs=3, metavar=("1", "2"))
@@ -4994,6 +4994,30 @@ def test_all_exports_everything_but_modules(self):
         ]
         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():
     support.run_unittest(__name__)
     # Remove global references to avoid looking like we have refleaks.
diff --git a/Misc/NEWS.d/next/Library/2018-05-23-00-26-27.bpo-11874.glK5iP.rst b/Misc/NEWS.d/next/Library/2018-05-23-00-26-27.bpo-11874.glK5iP.rst
new file mode 100644
index 000000000000..6c75f142c4be
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-05-23-00-26-27.bpo-11874.glK5iP.rst
@@ -0,0 +1,2 @@
+Use a better regex when breaking usage into wrappable parts. Avoids bogus
+assertion errors from custom metavar strings.



More information about the Python-checkins mailing list