From: Jansen Price Date: Wed, 29 Apr 2026 13:44:22 +0000 (-0500) Subject: gh-148740: Fix `uuid` CLI with custom UUIDs for UUIDv3/v5 namespaces (#148741) X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=f1588d460db97086f5ea28b5797ccdcfffb0307f;p=thirdparty%2FPython%2Fcpython.git gh-148740: Fix `uuid` CLI with custom UUIDs for UUIDv3/v5 namespaces (#148741) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 5f9ab048cdeb..055be2994bf4 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -1182,6 +1182,47 @@ class CommandLineTestCases: self.assertEqual(cm.exception.code, 2) self.assertIn("error: Incorrect number of arguments", mock_err.getvalue()) + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"]) + def test_cli_uuid3_outputted_with_valid_namespace_and_name(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output should be in the form of uuid3 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 3) + + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-n", + "0d6a16cc-34a7-47d8-b660-214d0ae184d2", + "-N", "some.user"]) + def test_cli_uuid3_outputted_with_custom_namespace_and_name(self): + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + self.uuid.main() + + output = stdout.getvalue().strip() + uuid_output = self.uuid.UUID(output) + + # Output should be in the form of uuid3 + self.assertEqual(output, str(uuid_output)) + self.assertEqual(uuid_output.version, 3) + + @mock.patch.object(sys, "argv", + ["", "-u", "uuid3", "-n", "any UUID", "-N", "python.org"]) + @mock.patch('sys.stderr', new_callable=io.StringIO) + def test_cli_uuid3_with_invalid_namespace(self, mock_err): + with self.assertRaises(SystemExit) as cm: + self.uuid.main() + # Check that exception code is the same as argparse.ArgumentParser.error + self.assertEqual(cm.exception.code, 2) + self.assertIn("error: badly formed hexadecimal UUID string", + mock_err.getvalue()) + @mock.patch.object(sys, "argv", [""]) def test_cli_uuid4_outputted_with_no_args(self): stdout = io.StringIO() @@ -1210,8 +1251,8 @@ class CommandLineTestCases: self.assertEqual(uuid_output.version, 4) @mock.patch.object(sys, "argv", - ["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"]) - def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self): + ["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"]) + def test_cli_uuid5_outputted_with_valid_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): self.uuid.main() @@ -1221,11 +1262,13 @@ class CommandLineTestCases: # Output should be in the form of uuid5 self.assertEqual(output, str(uuid_output)) - self.assertEqual(uuid_output.version, 3) + self.assertEqual(uuid_output.version, 5) @mock.patch.object(sys, "argv", - ["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"]) - def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self): + ["", "-u", "uuid5", "-n", + "0d6a16cc-34a7-47d8-b660-214d0ae184d2", + "-N", "some.user"]) + def test_cli_uuid5_ouputted_with_custom_namespace_and_name(self): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): self.uuid.main() @@ -1237,6 +1280,17 @@ class CommandLineTestCases: self.assertEqual(output, str(uuid_output)) self.assertEqual(uuid_output.version, 5) + @mock.patch.object(sys, "argv", + ["", "-u", "uuid5", "-n", "any UUID", "-N", "python.org"]) + @mock.patch('sys.stderr', new_callable=io.StringIO) + def test_cli_uuid5_with_invalid_namespace(self, mock_err): + with self.assertRaises(SystemExit) as cm: + self.uuid.main() + # Check that exception code is the same as argparse.ArgumentParser.error + self.assertEqual(cm.exception.code, 2) + self.assertIn("error: badly formed hexadecimal UUID string", + mock_err.getvalue()) + @mock.patch.object(sys, "argv", ["", "-u", "uuid6"]) def test_cli_uuid6(self): self.do_test_standalone_uuid(6) diff --git a/Lib/uuid.py b/Lib/uuid.py index c0150a59d7cb..8c59581464b0 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -962,7 +962,7 @@ def main(): default="uuid4", help="function to generate the UUID") parser.add_argument("-n", "--namespace", - choices=["any UUID", *namespaces.keys()], + metavar=f"{{any UUID,{','.join(namespaces)}}}", help="uuid3/uuid5 only: " "a UUID, or a well-known predefined UUID addressed " "by namespace name") @@ -984,7 +984,13 @@ def main(): f"{args.uuid} requires a namespace and a name. " "Run 'python -m uuid -h' for more information." ) - namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace) + if namespace in namespaces: + namespace = namespaces[namespace] + else: + try: + namespace = UUID(namespace) + except ValueError as exc: + parser.error(f"{exc}: {args.namespace!r}") for _ in range(args.count): print(uuid_func(namespace, name)) else: diff --git a/Misc/NEWS.d/next/Library/2026-04-18-17-37-13.gh-issue-148740.sYnFi0.rst b/Misc/NEWS.d/next/Library/2026-04-18-17-37-13.gh-issue-148740.sYnFi0.rst new file mode 100644 index 000000000000..7e49cedda7be --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-18-17-37-13.gh-issue-148740.sYnFi0.rst @@ -0,0 +1,2 @@ +Fix usage for :mod:`uuid` command-line interface to support a custom namespace be +provided for uuid3 and uuid5.