Skip to content

Commit 4dd8f23

Browse files
picnixzhugovk
authored andcommitted
pythongh-89083: add support for UUID version 8 (RFC 9562) (python#123224)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
1 parent c19c798 commit 4dd8f23

File tree

5 files changed

+109
-19
lines changed

5 files changed

+109
-19
lines changed

Doc/library/uuid.rst

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
:mod:`!uuid` --- UUID objects according to :rfc:`4122`
1+
:mod:`!uuid` --- UUID objects according to :rfc:`9562`
22
======================================================
33

44
.. module:: uuid
5-
:synopsis: UUID objects (universally unique identifiers) according to RFC 4122
5+
:synopsis: UUID objects (universally unique identifiers) according to RFC 9562
66
.. moduleauthor:: Ka-Ping Yee <ping@zesty.ca>
77
.. sectionauthor:: George Yoshida <quiver@users.sourceforge.net>
88

@@ -12,7 +12,8 @@
1212

1313
This module provides immutable :class:`UUID` objects (the :class:`UUID` class)
1414
and the functions :func:`uuid1`, :func:`uuid3`, :func:`uuid4`, :func:`uuid5` for
15-
generating version 1, 3, 4, and 5 UUIDs as specified in :rfc:`4122`.
15+
generating version 1, 3, 4, 5, and 8 UUIDs as specified in :rfc:`9562` (which
16+
supersedes :rfc:`4122`).
1617

1718
If all you want is a unique ID, you should probably call :func:`uuid1` or
1819
:func:`uuid4`. Note that :func:`uuid1` may compromise privacy since it creates
@@ -65,7 +66,7 @@ which relays any information about the UUID's safety, using this enumeration:
6566

6667
Exactly one of *hex*, *bytes*, *bytes_le*, *fields*, or *int* must be given.
6768
The *version* argument is optional; if given, the resulting UUID will have its
68-
variant and version number set according to :rfc:`4122`, overriding bits in the
69+
variant and version number set according to :rfc:`9562`, overriding bits in the
6970
given *hex*, *bytes*, *bytes_le*, *fields*, or *int*.
7071

7172
Comparison of UUID objects are made by way of comparing their
@@ -137,7 +138,7 @@ which relays any information about the UUID's safety, using this enumeration:
137138

138139
.. attribute:: UUID.urn
139140

140-
The UUID as a URN as specified in :rfc:`4122`.
141+
The UUID as a URN as specified in :rfc:`9562`.
141142

142143

143144
.. attribute:: UUID.variant
@@ -149,9 +150,13 @@ which relays any information about the UUID's safety, using this enumeration:
149150

150151
.. attribute:: UUID.version
151152

152-
The UUID version number (1 through 5, meaningful only when the variant is
153+
The UUID version number (1 through 8, meaningful only when the variant is
153154
:const:`RFC_4122`).
154155

156+
.. versionchanged:: next
157+
Added UUID version 8.
158+
159+
155160
.. attribute:: UUID.is_safe
156161

157162
An enumeration of :class:`SafeUUID` which indicates whether the platform
@@ -216,6 +221,23 @@ The :mod:`uuid` module defines the following functions:
216221

217222
.. index:: single: uuid5
218223

224+
225+
.. function:: uuid8(a=None, b=None, c=None)
226+
227+
Generate a pseudo-random UUID according to
228+
:rfc:`RFC 9562, §5.8 <9562#section-5.8>`.
229+
230+
When specified, the parameters *a*, *b* and *c* are expected to be
231+
positive integers of 48, 12 and 62 bits respectively. If they exceed
232+
their expected bit count, only their least significant bits are kept;
233+
non-specified arguments are substituted for a pseudo-random integer of
234+
appropriate size.
235+
236+
.. versionadded:: next
237+
238+
.. index:: single: uuid8
239+
240+
219241
The :mod:`uuid` module defines the following namespace identifiers for use with
220242
:func:`uuid3` or :func:`uuid5`.
221243

@@ -252,7 +274,9 @@ of the :attr:`~UUID.variant` attribute:
252274

253275
.. data:: RFC_4122
254276

255-
Specifies the UUID layout given in :rfc:`4122`.
277+
Specifies the UUID layout given in :rfc:`4122`. This constant is kept
278+
for backward compatibility even though :rfc:`4122` has been superseded
279+
by :rfc:`9562`.
256280

257281

258282
.. data:: RESERVED_MICROSOFT
@@ -267,7 +291,7 @@ of the :attr:`~UUID.variant` attribute:
267291

268292
.. seealso::
269293

270-
:rfc:`4122` - A Universally Unique IDentifier (UUID) URN Namespace
294+
:rfc:`9562` - A Universally Unique IDentifier (UUID) URN Namespace
271295
This specification defines a Uniform Resource Name namespace for UUIDs, the
272296
internal format of UUIDs, and methods of generating UUIDs.
273297

@@ -283,7 +307,7 @@ The :mod:`uuid` module can be executed as a script from the command line.
283307

284308
.. code-block:: sh
285309
286-
python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME]
310+
python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5,uuid8}] [-n NAMESPACE] [-N NAME]
287311
288312
The following options are accepted:
289313

Doc/whatsnew/3.14.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,14 @@ unittest
517517
(Contributed by Jacob Walls in :gh:`80958`.)
518518

519519

520+
uuid
521+
----
522+
523+
* Add support for UUID version 8 via :func:`uuid.uuid8` as specified
524+
in :rfc:`9562`.
525+
(Contributed by Bénédikt Tran in :gh:`89083`.)
526+
527+
520528
.. Add improved modules above alphabetically, not here at the end.
521529
522530
Optimizations

Lib/test/test_uuid.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
import io
99
import os
1010
import pickle
11+
import random
1112
import sys
1213
import weakref
14+
from itertools import product
1315
from unittest import mock
1416

1517
py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid'])
@@ -267,7 +269,7 @@ def test_exceptions(self):
267269

268270
# Version number out of range.
269271
badvalue(lambda: self.uuid.UUID('00'*16, version=0))
270-
badvalue(lambda: self.uuid.UUID('00'*16, version=6))
272+
badvalue(lambda: self.uuid.UUID('00'*16, version=42))
271273

272274
# Integer value out of range.
273275
badvalue(lambda: self.uuid.UUID(int=-1))
@@ -681,6 +683,37 @@ def test_uuid5(self):
681683
equal(u, self.uuid.UUID(v))
682684
equal(str(u), v)
683685

686+
def test_uuid8(self):
687+
equal = self.assertEqual
688+
u = self.uuid.uuid8()
689+
690+
equal(u.variant, self.uuid.RFC_4122)
691+
equal(u.version, 8)
692+
693+
for (_, hi, mid, lo) in product(
694+
range(10), # repeat 10 times
695+
[None, 0, random.getrandbits(48)],
696+
[None, 0, random.getrandbits(12)],
697+
[None, 0, random.getrandbits(62)],
698+
):
699+
u = self.uuid.uuid8(hi, mid, lo)
700+
equal(u.variant, self.uuid.RFC_4122)
701+
equal(u.version, 8)
702+
if hi is not None:
703+
equal((u.int >> 80) & 0xffffffffffff, hi)
704+
if mid is not None:
705+
equal((u.int >> 64) & 0xfff, mid)
706+
if lo is not None:
707+
equal(u.int & 0x3fffffffffffffff, lo)
708+
709+
def test_uuid8_uniqueness(self):
710+
# Test that UUIDv8-generated values are unique
711+
# (up to a negligible probability of failure).
712+
u1 = self.uuid.uuid8()
713+
u2 = self.uuid.uuid8()
714+
self.assertNotEqual(u1.int, u2.int)
715+
self.assertEqual(u1.version, u2.version)
716+
684717
@support.requires_fork()
685718
def testIssue8621(self):
686719
# On at least some versions of OSX self.uuid.uuid4 generates

Lib/uuid.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
r"""UUID objects (universally unique identifiers) according to RFC 4122.
1+
r"""UUID objects (universally unique identifiers) according to RFC 4122/9562.
22
33
This module provides immutable UUID objects (class UUID) and the functions
4-
uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
5-
UUIDs as specified in RFC 4122.
4+
uuid1(), uuid3(), uuid4(), uuid5(), and uuid8() for generating version 1, 3,
5+
4, 5, and 8 UUIDs as specified in RFC 4122/9562.
66
77
If all you want is a unique ID, you should probably call uuid1() or uuid4().
88
Note that uuid1() may compromise privacy since it creates a UUID containing
@@ -124,12 +124,12 @@ class UUID:
124124
125125
int the UUID as a 128-bit integer
126126
127-
urn the UUID as a URN as specified in RFC 4122
127+
urn the UUID as a URN as specified in RFC 4122/9562
128128
129129
variant the UUID variant (one of the constants RESERVED_NCS,
130130
RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
131131
132-
version the UUID version number (1 through 5, meaningful only
132+
version the UUID version number (1 through 8, meaningful only
133133
when the variant is RFC_4122)
134134
135135
is_safe An enum indicating whether the UUID has been generated in
@@ -214,9 +214,9 @@ def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
214214
if not 0 <= int < 1<<128:
215215
raise ValueError('int is out of range (need a 128-bit value)')
216216
if version is not None:
217-
if not 1 <= version <= 5:
217+
if not 1 <= version <= 8:
218218
raise ValueError('illegal version number')
219-
# Set the variant to RFC 4122.
219+
# Set the variant to RFC 4122/9562.
220220
int &= ~(0xc000 << 48)
221221
int |= 0x8000 << 48
222222
# Set the version number.
@@ -355,7 +355,7 @@ def variant(self):
355355

356356
@property
357357
def version(self):
358-
# The version bits are only meaningful for RFC 4122 UUIDs.
358+
# The version bits are only meaningful for RFC 4122/9562 UUIDs.
359359
if self.variant == RFC_4122:
360360
return int((self.int >> 76) & 0xf)
361361

@@ -719,14 +719,37 @@ def uuid5(namespace, name):
719719
hash = sha1(namespace.bytes + name).digest()
720720
return UUID(bytes=hash[:16], version=5)
721721

722+
def uuid8(a=None, b=None, c=None):
723+
"""Generate a UUID from three custom blocks.
724+
725+
* 'a' is the first 48-bit chunk of the UUID (octets 0-5);
726+
* 'b' is the mid 12-bit chunk (octets 6-7);
727+
* 'c' is the last 62-bit chunk (octets 8-15).
728+
729+
When a value is not specified, a pseudo-random value is generated.
730+
"""
731+
if a is None:
732+
import random
733+
a = random.getrandbits(48)
734+
if b is None:
735+
import random
736+
b = random.getrandbits(12)
737+
if c is None:
738+
import random
739+
c = random.getrandbits(62)
740+
int_uuid_8 = (a & 0xffff_ffff_ffff) << 80
741+
int_uuid_8 |= (b & 0xfff) << 64
742+
int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff
743+
return UUID(int=int_uuid_8, version=8)
722744

723745
def main():
724746
"""Run the uuid command line interface."""
725747
uuid_funcs = {
726748
"uuid1": uuid1,
727749
"uuid3": uuid3,
728750
"uuid4": uuid4,
729-
"uuid5": uuid5
751+
"uuid5": uuid5,
752+
"uuid8": uuid8,
730753
}
731754
uuid_namespace_funcs = ("uuid3", "uuid5")
732755
namespaces = {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :func:`uuid.uuid8` for generating UUIDv8 objects as specified in
2+
:rfc:`9562`. Patch by Bénédikt Tran

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy