Skip to content

Commit 97b4246

Browse files
miss-islingtonsumpygumppicnixz
authored
[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 f1588d4) Co-authored-by: Jansen Price <sumpygump@gmail.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 098880d commit 97b4246

3 files changed

Lines changed: 69 additions & 7 deletions

File tree

Lib/test/test_uuid.py

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,47 @@ def test_cli_name_required_for_uuid3(self, mock_err):
11761176
self.assertEqual(cm.exception.code, 2)
11771177
self.assertIn("error: Incorrect number of arguments", mock_err.getvalue())
11781178

1179+
@mock.patch.object(sys, "argv",
1180+
["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"])
1181+
def test_cli_uuid3_outputted_with_valid_namespace_and_name(self):
1182+
stdout = io.StringIO()
1183+
with contextlib.redirect_stdout(stdout):
1184+
self.uuid.main()
1185+
1186+
output = stdout.getvalue().strip()
1187+
uuid_output = self.uuid.UUID(output)
1188+
1189+
# Output should be in the form of uuid3
1190+
self.assertEqual(output, str(uuid_output))
1191+
self.assertEqual(uuid_output.version, 3)
1192+
1193+
@mock.patch.object(sys, "argv",
1194+
["", "-u", "uuid3", "-n",
1195+
"0d6a16cc-34a7-47d8-b660-214d0ae184d2",
1196+
"-N", "some.user"])
1197+
def test_cli_uuid3_outputted_with_custom_namespace_and_name(self):
1198+
stdout = io.StringIO()
1199+
with contextlib.redirect_stdout(stdout):
1200+
self.uuid.main()
1201+
1202+
output = stdout.getvalue().strip()
1203+
uuid_output = self.uuid.UUID(output)
1204+
1205+
# Output should be in the form of uuid3
1206+
self.assertEqual(output, str(uuid_output))
1207+
self.assertEqual(uuid_output.version, 3)
1208+
1209+
@mock.patch.object(sys, "argv",
1210+
["", "-u", "uuid3", "-n", "any UUID", "-N", "python.org"])
1211+
@mock.patch('sys.stderr', new_callable=io.StringIO)
1212+
def test_cli_uuid3_with_invalid_namespace(self, mock_err):
1213+
with self.assertRaises(SystemExit) as cm:
1214+
self.uuid.main()
1215+
# Check that exception code is the same as argparse.ArgumentParser.error
1216+
self.assertEqual(cm.exception.code, 2)
1217+
self.assertIn("error: badly formed hexadecimal UUID string",
1218+
mock_err.getvalue())
1219+
11791220
@mock.patch.object(sys, "argv", [""])
11801221
def test_cli_uuid4_outputted_with_no_args(self):
11811222
stdout = io.StringIO()
@@ -1204,8 +1245,8 @@ def test_cli_uuid4_outputted_with_count(self):
12041245
self.assertEqual(uuid_output.version, 4)
12051246

12061247
@mock.patch.object(sys, "argv",
1207-
["", "-u", "uuid3", "-n", "@dns", "-N", "python.org"])
1208-
def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self):
1248+
["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"])
1249+
def test_cli_uuid5_outputted_with_valid_namespace_and_name(self):
12091250
stdout = io.StringIO()
12101251
with contextlib.redirect_stdout(stdout):
12111252
self.uuid.main()
@@ -1215,11 +1256,13 @@ def test_cli_uuid3_ouputted_with_valid_namespace_and_name(self):
12151256

12161257
# Output should be in the form of uuid5
12171258
self.assertEqual(output, str(uuid_output))
1218-
self.assertEqual(uuid_output.version, 3)
1259+
self.assertEqual(uuid_output.version, 5)
12191260

12201261
@mock.patch.object(sys, "argv",
1221-
["", "-u", "uuid5", "-n", "@dns", "-N", "python.org"])
1222-
def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self):
1262+
["", "-u", "uuid5", "-n",
1263+
"0d6a16cc-34a7-47d8-b660-214d0ae184d2",
1264+
"-N", "some.user"])
1265+
def test_cli_uuid5_ouputted_with_custom_namespace_and_name(self):
12231266
stdout = io.StringIO()
12241267
with contextlib.redirect_stdout(stdout):
12251268
self.uuid.main()
@@ -1231,6 +1274,17 @@ def test_cli_uuid5_ouputted_with_valid_namespace_and_name(self):
12311274
self.assertEqual(output, str(uuid_output))
12321275
self.assertEqual(uuid_output.version, 5)
12331276

1277+
@mock.patch.object(sys, "argv",
1278+
["", "-u", "uuid5", "-n", "any UUID", "-N", "python.org"])
1279+
@mock.patch('sys.stderr', new_callable=io.StringIO)
1280+
def test_cli_uuid5_with_invalid_namespace(self, mock_err):
1281+
with self.assertRaises(SystemExit) as cm:
1282+
self.uuid.main()
1283+
# Check that exception code is the same as argparse.ArgumentParser.error
1284+
self.assertEqual(cm.exception.code, 2)
1285+
self.assertIn("error: badly formed hexadecimal UUID string",
1286+
mock_err.getvalue())
1287+
12341288
@mock.patch.object(sys, "argv", ["", "-u", "uuid6"])
12351289
def test_cli_uuid6(self):
12361290
self.do_test_standalone_uuid(6)

Lib/uuid.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ def main():
961961
default="uuid4",
962962
help="function to generate the UUID")
963963
parser.add_argument("-n", "--namespace",
964-
choices=["any UUID", *namespaces.keys()],
964+
metavar=f"{{any UUID,{','.join(namespaces)}}}",
965965
help="uuid3/uuid5 only: "
966966
"a UUID, or a well-known predefined UUID addressed "
967967
"by namespace name")
@@ -983,7 +983,13 @@ def main():
983983
f"{args.uuid} requires a namespace and a name. "
984984
"Run 'python -m uuid -h' for more information."
985985
)
986-
namespace = namespaces[namespace] if namespace in namespaces else UUID(namespace)
986+
if namespace in namespaces:
987+
namespace = namespaces[namespace]
988+
else:
989+
try:
990+
namespace = UUID(namespace)
991+
except ValueError as exc:
992+
parser.error(f"{exc}: {args.namespace!r}")
987993
for _ in range(args.count):
988994
print(uuid_func(namespace, name))
989995
else:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix usage for :mod:`uuid` command-line interface to support a custom namespace be
2+
provided for uuid3 and uuid5.

0 commit comments

Comments
 (0)