https://github.com/python/cpython/commit/97b4246f6463b8033520589246b07e82d62690df
commit: 97b4246f6463b8033520589246b07e82d62690df
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: picnixz <[email protected]>
date: 2026-04-29T17:00:13+02:00
summary:

[3.14] gh-148740: Fix `uuid` CLI with custom UUIDs for UUIDv3/v5 namespaces 
(GH-148741) (#149152)

gh-148740: Fix `uuid` CLI with custom UUIDs for UUIDv3/v5 namespaces (GH-148741)
(cherry picked from commit f1588d460db97086f5ea28b5797ccdcfffb0307f)

Co-authored-by: Jansen Price <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-04-18-17-37-13.gh-issue-148740.sYnFi0.rst
M Lib/test/test_uuid.py
M Lib/uuid.py

diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py
index 0e1a723ce3a151..c8400da3ae987d 100755
--- a/Lib/test/test_uuid.py
+++ b/Lib/test/test_uuid.py
@@ -1176,6 +1176,47 @@ def test_cli_name_required_for_uuid3(self, mock_err):
         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()
@@ -1204,8 +1245,8 @@ def test_cli_uuid4_outputted_with_count(self):
             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()
@@ -1215,11 +1256,13 @@ def 
test_cli_uuid3_ouputted_with_valid_namespace_and_name(self):
 
         # 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()
@@ -1231,6 +1274,17 @@ def 
test_cli_uuid5_ouputted_with_valid_namespace_and_name(self):
         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 313f2fc46cb346..043ad7bcbde849 100644
--- a/Lib/uuid.py
+++ b/Lib/uuid.py
@@ -961,7 +961,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")
@@ -983,7 +983,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 00000000000000..7e49cedda7beb2
--- /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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to