]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40527: Fix command line argument parsing (GH-19955)
authorVictor Stinner <vstinner@python.org>
Wed, 6 May 2020 13:22:17 +0000 (15:22 +0200)
committerGitHub <noreply@github.com>
Wed, 6 May 2020 13:22:17 +0000 (22:22 +0900)
Lib/test/test_cmd_line.py
Misc/NEWS.d/next/Core and Builtins/2020-05-06-14-52-35.bpo-40527.gTNKuy.rst [new file with mode: 0644]
Python/getopt.c

index ee96473322dba084596bda51f375213a56d2253e..724402533038d4aec21e776c08677875b5d5cb97 100644 (file)
@@ -756,6 +756,17 @@ class CmdLineTest(unittest.TestCase):
         self.assertEqual(proc.returncode, 0, proc)
         self.assertEqual(proc.stdout.strip(), b'0')
 
+    def test_parsing_error(self):
+        args = [sys.executable, '-I', '--unknown-option']
+        proc = subprocess.run(args,
+                              stdout=subprocess.PIPE,
+                              stderr=subprocess.PIPE,
+                              text=True)
+        err_msg = "unknown option --unknown-option\nusage: "
+        self.assertTrue(proc.stderr.startswith(err_msg), proc.stderr)
+        self.assertNotEqual(proc.returncode, 0)
+
+
 @unittest.skipIf(interpreter_requires_environment(),
                  'Cannot run -I tests when PYTHON env vars are required.')
 class IgnoreEnvironmentTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-06-14-52-35.bpo-40527.gTNKuy.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-06-14-52-35.bpo-40527.gTNKuy.rst
new file mode 100644 (file)
index 0000000..19b8888
--- /dev/null
@@ -0,0 +1,2 @@
+Fix command line argument parsing: no longer write errors multiple times
+into stderr.
index 708d9ce496287c508b163d46695d557d50b0b056..2e3891aae2d16ae0ab09b3a75190f43bf1909cfa 100644 (file)
@@ -101,7 +101,9 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex)
     if (option == L'-') {
         // Parse long option.
         if (*opt_ptr == L'\0') {
-            fprintf(stderr, "expected long option\n");
+            if (_PyOS_opterr) {
+                fprintf(stderr, "expected long option\n");
+            }
             return -1;
         }
         *longindex = 0;
@@ -111,7 +113,9 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex)
                 break;
         }
         if (!opt->name) {
-            fprintf(stderr, "unknown option %ls\n", argv[_PyOS_optind - 1]);
+            if (_PyOS_opterr) {
+                fprintf(stderr, "unknown option %ls\n", argv[_PyOS_optind - 1]);
+            }
             return '_';
         }
         opt_ptr = L"";
@@ -119,8 +123,10 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex)
             return opt->val;
         }
         if (_PyOS_optind >= argc) {
-            fprintf(stderr, "Argument expected for the %ls options\n",
-                    argv[_PyOS_optind - 1]);
+            if (_PyOS_opterr) {
+                fprintf(stderr, "Argument expected for the %ls options\n",
+                        argv[_PyOS_optind - 1]);
+            }
             return '_';
         }
         _PyOS_optarg = argv[_PyOS_optind++];
@@ -128,14 +134,16 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex)
     }
 
     if (option == 'J') {
-        if (_PyOS_opterr)
+        if (_PyOS_opterr) {
             fprintf(stderr, "-J is reserved for Jython\n");
+        }
         return '_';
     }
 
     if ((ptr = wcschr(SHORT_OPTS, option)) == NULL) {
-        if (_PyOS_opterr)
+        if (_PyOS_opterr) {
             fprintf(stderr, "Unknown option: -%c\n", (char)option);
+        }
         return '_';
     }
 
@@ -147,9 +155,10 @@ int _PyOS_GetOpt(Py_ssize_t argc, wchar_t * const *argv, int *longindex)
 
         else {
             if (_PyOS_optind >= argc) {
-                if (_PyOS_opterr)
+                if (_PyOS_opterr) {
                     fprintf(stderr,
                         "Argument expected for the -%c option\n", (char)option);
+                }
                 return '_';
             }