Skip to content

Commit 413b7db

Browse files
authored
gh-94684: uuid: support bytes in the name argument to uuid3/5 (#94709)
RFC 4122 does not specify that name should be a string, so for completness the functions should also support a name given as a raw byte sequence.
1 parent f1e3eee commit 413b7db

File tree

4 files changed

+43
-6
lines changed

4 files changed

+43
-6
lines changed

Doc/library/uuid.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ The :mod:`uuid` module defines the following functions:
186186
.. function:: uuid3(namespace, name)
187187

188188
Generate a UUID based on the MD5 hash of a namespace identifier (which is a
189-
UUID) and a name (which is a string).
189+
UUID) and a name (which is a :class:`bytes` object or a string
190+
that will be encoded using UTF-8).
190191

191192
.. index:: single: uuid3
192193

@@ -201,7 +202,8 @@ The :mod:`uuid` module defines the following functions:
201202
.. function:: uuid5(namespace, name)
202203

203204
Generate a UUID based on the SHA-1 hash of a namespace identifier (which is a
204-
UUID) and a name (which is a string).
205+
UUID) and a name (which is a :class:`bytes` object or a string
206+
that will be encoded using UTF-8).
205207

206208
.. index:: single: uuid5
207209

Lib/test/test_uuid.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,22 @@ def test_uuid1_time(self):
600600
def test_uuid3(self):
601601
equal = self.assertEqual
602602

603-
# Test some known version-3 UUIDs.
603+
# Test some known version-3 UUIDs with name passed as a byte object
604+
for u, v in [(self.uuid.uuid3(self.uuid.NAMESPACE_DNS, b'python.org'),
605+
'6fa459ea-ee8a-3ca4-894e-db77e160355e'),
606+
(self.uuid.uuid3(self.uuid.NAMESPACE_URL, b'http://python.org/'),
607+
'9fe8e8c4-aaa8-32a9-a55c-4535a88b748d'),
608+
(self.uuid.uuid3(self.uuid.NAMESPACE_OID, b'1.3.6.1'),
609+
'dd1a1cef-13d5-368a-ad82-eca71acd4cd1'),
610+
(self.uuid.uuid3(self.uuid.NAMESPACE_X500, b'c=ca'),
611+
'658d3002-db6b-3040-a1d1-8ddd7d189a4d'),
612+
]:
613+
equal(u.variant, self.uuid.RFC_4122)
614+
equal(u.version, 3)
615+
equal(u, self.uuid.UUID(v))
616+
equal(str(u), v)
617+
618+
# Test some known version-3 UUIDs with name passed as a string
604619
for u, v in [(self.uuid.uuid3(self.uuid.NAMESPACE_DNS, 'python.org'),
605620
'6fa459ea-ee8a-3ca4-894e-db77e160355e'),
606621
(self.uuid.uuid3(self.uuid.NAMESPACE_URL, 'http://python.org/'),
@@ -632,7 +647,22 @@ def test_uuid4(self):
632647
def test_uuid5(self):
633648
equal = self.assertEqual
634649

635-
# Test some known version-5 UUIDs.
650+
# Test some known version-5 UUIDs with names given as byte objects
651+
for u, v in [(self.uuid.uuid5(self.uuid.NAMESPACE_DNS, b'python.org'),
652+
'886313e1-3b8a-5372-9b90-0c9aee199e5d'),
653+
(self.uuid.uuid5(self.uuid.NAMESPACE_URL, b'http://python.org/'),
654+
'4c565f0d-3f5a-5890-b41b-20cf47701c5e'),
655+
(self.uuid.uuid5(self.uuid.NAMESPACE_OID, b'1.3.6.1'),
656+
'1447fa61-5277-5fef-a9b3-fbc6e44f4af3'),
657+
(self.uuid.uuid5(self.uuid.NAMESPACE_X500, b'c=ca'),
658+
'cc957dd1-a972-5349-98cd-874190002798'),
659+
]:
660+
equal(u.variant, self.uuid.RFC_4122)
661+
equal(u.version, 5)
662+
equal(u, self.uuid.UUID(v))
663+
equal(str(u), v)
664+
665+
# Test some known version-5 UUIDs with names given as strings
636666
for u, v in [(self.uuid.uuid5(self.uuid.NAMESPACE_DNS, 'python.org'),
637667
'886313e1-3b8a-5372-9b90-0c9aee199e5d'),
638668
(self.uuid.uuid5(self.uuid.NAMESPACE_URL, 'http://python.org/'),

Lib/uuid.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,9 +711,11 @@ def uuid1(node=None, clock_seq=None):
711711

712712
def uuid3(namespace, name):
713713
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
714+
if isinstance(name, str):
715+
name = bytes(name, "utf-8")
714716
from hashlib import md5
715717
digest = md5(
716-
namespace.bytes + bytes(name, "utf-8"),
718+
namespace.bytes + name,
717719
usedforsecurity=False
718720
).digest()
719721
return UUID(bytes=digest[:16], version=3)
@@ -724,8 +726,10 @@ def uuid4():
724726

725727
def uuid5(namespace, name):
726728
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
729+
if isinstance(name, str):
730+
name = bytes(name, "utf-8")
727731
from hashlib import sha1
728-
hash = sha1(namespace.bytes + bytes(name, "utf-8")).digest()
732+
hash = sha1(namespace.bytes + name).digest()
729733
return UUID(bytes=hash[:16], version=5)
730734

731735

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Now :func:`uuid.uuid3` and :func:`uuid.uuid5` functions support :class:`bytes` objects as their *name* argument.

0 commit comments

Comments
 (0)