diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 82b9d0f86ba421e..80b1e27366bcc59 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -677,7 +677,7 @@ def test_unknown_options(self): for option in ('-z', '--long-option', '---'): with self.subTest(option=option): rc, out, err = assert_python_failure('-E', option) - errmsg = f'Unknown option: {option}'.encode() + errmsg = f"Invalid option '{option}'".encode() self.assertIn(errmsg, err) self.assertEqual(err.splitlines().count(errmsg), 1) self.assertEqual(b'', out) @@ -685,15 +685,21 @@ def test_unknown_options(self): # Add "without='-E'" to prevent _assert_python to append -E # to env_vars and change the output of stderr rc, out, err = assert_python_failure('-z', without='-E') - self.assertIn(b'Unknown option: -z', err) - self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1) + self.assertIn(b"Invalid option '-z'", err) + self.assertEqual(err.splitlines().count(b"Invalid option '-z'"), 1) self.assertEqual(b'', out) rc, out, err = assert_python_failure('-a', '-z', without='-E') - self.assertIn(b'Unknown option: -a', err) + self.assertIn(b"Invalid option '-a'", err) # only the first unknown option is reported - self.assertNotIn(b'Unknown option: -z', err) - self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1) + self.assertNotIn(b"Invalid option '-z'", err) + self.assertEqual(err.splitlines().count(b"Invalid option '-a'"), 1) + self.assertEqual(b'', out) + + # gh-132414: a quoted "-q -i" arrives as one argument; the space + # after -q must be visible in the error message + rc, out, err = assert_python_failure('-q -i', without='-E') + self.assertIn(b"Invalid option '- '", err) self.assertEqual(b'', out) @unittest.skipIf(interpreter_requires_environment(), @@ -1162,7 +1168,7 @@ def test_parsing_error(self): stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - err_msg = "Unknown option: --unknown-option\nusage: " + err_msg = "Invalid option '--unknown-option'\nusage: " self.assertStartsWith(proc.stderr, err_msg) self.assertNotEqual(proc.returncode, 0) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-12-44-38.gh-issue-132414.uwxmd5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-12-44-38.gh-issue-132414.uwxmd5.rst new file mode 100644 index 000000000000000..5f795e987650af0 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-21-12-44-38.gh-issue-132414.uwxmd5.rst @@ -0,0 +1,4 @@ +Quote the invalid option in the error message for an unknown command-line +option: ``Invalid option '-z'`` instead of ``Unknown option: -z``. A stray +space, as passed by a shebang line like ``#!/usr/bin/python3 -q -i``, is now +reported as ``Invalid option '- '``. diff --git a/Python/getopt.c b/Python/getopt.c index 2dcabc9962d25f3..f6a3ef960a4021b 100644 --- a/Python/getopt.c +++ b/Python/getopt.c @@ -137,7 +137,7 @@ _PyOS_GetOpt(struct _PyOS_GetOpt *getopt) if (!opt->name) { if (error) { - fprintf(stderr, "Unknown option: %ls\n", argv[getopt->index - 1]); + fprintf(stderr, "Invalid option '%ls'\n", argv[getopt->index - 1]); } return '_'; } @@ -160,7 +160,7 @@ _PyOS_GetOpt(struct _PyOS_GetOpt *getopt) wchar_t *ptr = wcschr(SHORT_OPTS, option); if (ptr == NULL) { if (error) { - fprintf(stderr, "Unknown option: -%lc\n", option); + fprintf(stderr, "Invalid option '-%lc'\n", option); } return '_'; }