mirror of https://github.com/python/cpython
gh-89083: add support for UUID version 8 (RFC 9562) (#123224)
Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
parent
a83472f49b
commit
03924b5dee
|
@ -1,8 +1,8 @@
|
||||||
:mod:`!uuid` --- UUID objects according to :rfc:`4122`
|
:mod:`!uuid` --- UUID objects according to :rfc:`9562`
|
||||||
======================================================
|
======================================================
|
||||||
|
|
||||||
.. module:: uuid
|
.. module:: uuid
|
||||||
:synopsis: UUID objects (universally unique identifiers) according to RFC 4122
|
:synopsis: UUID objects (universally unique identifiers) according to RFC 9562
|
||||||
.. moduleauthor:: Ka-Ping Yee <ping@zesty.ca>
|
.. moduleauthor:: Ka-Ping Yee <ping@zesty.ca>
|
||||||
.. sectionauthor:: George Yoshida <quiver@users.sourceforge.net>
|
.. sectionauthor:: George Yoshida <quiver@users.sourceforge.net>
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
This module provides immutable :class:`UUID` objects (the :class:`UUID` class)
|
This module provides immutable :class:`UUID` objects (the :class:`UUID` class)
|
||||||
and the functions :func:`uuid1`, :func:`uuid3`, :func:`uuid4`, :func:`uuid5` for
|
and the functions :func:`uuid1`, :func:`uuid3`, :func:`uuid4`, :func:`uuid5` for
|
||||||
generating version 1, 3, 4, and 5 UUIDs as specified in :rfc:`4122`.
|
generating version 1, 3, 4, 5, and 8 UUIDs as specified in :rfc:`9562` (which
|
||||||
|
supersedes :rfc:`4122`).
|
||||||
|
|
||||||
If all you want is a unique ID, you should probably call :func:`uuid1` or
|
If all you want is a unique ID, you should probably call :func:`uuid1` or
|
||||||
:func:`uuid4`. Note that :func:`uuid1` may compromise privacy since it creates
|
: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:
|
||||||
|
|
||||||
Exactly one of *hex*, *bytes*, *bytes_le*, *fields*, or *int* must be given.
|
Exactly one of *hex*, *bytes*, *bytes_le*, *fields*, or *int* must be given.
|
||||||
The *version* argument is optional; if given, the resulting UUID will have its
|
The *version* argument is optional; if given, the resulting UUID will have its
|
||||||
variant and version number set according to :rfc:`4122`, overriding bits in the
|
variant and version number set according to :rfc:`9562`, overriding bits in the
|
||||||
given *hex*, *bytes*, *bytes_le*, *fields*, or *int*.
|
given *hex*, *bytes*, *bytes_le*, *fields*, or *int*.
|
||||||
|
|
||||||
Comparison of UUID objects are made by way of comparing their
|
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:
|
||||||
|
|
||||||
.. attribute:: UUID.urn
|
.. attribute:: UUID.urn
|
||||||
|
|
||||||
The UUID as a URN as specified in :rfc:`4122`.
|
The UUID as a URN as specified in :rfc:`9562`.
|
||||||
|
|
||||||
|
|
||||||
.. attribute:: UUID.variant
|
.. attribute:: UUID.variant
|
||||||
|
@ -149,9 +150,13 @@ which relays any information about the UUID's safety, using this enumeration:
|
||||||
|
|
||||||
.. attribute:: UUID.version
|
.. attribute:: UUID.version
|
||||||
|
|
||||||
The UUID version number (1 through 5, meaningful only when the variant is
|
The UUID version number (1 through 8, meaningful only when the variant is
|
||||||
:const:`RFC_4122`).
|
:const:`RFC_4122`).
|
||||||
|
|
||||||
|
.. versionchanged:: next
|
||||||
|
Added UUID version 8.
|
||||||
|
|
||||||
|
|
||||||
.. attribute:: UUID.is_safe
|
.. attribute:: UUID.is_safe
|
||||||
|
|
||||||
An enumeration of :class:`SafeUUID` which indicates whether the platform
|
An enumeration of :class:`SafeUUID` which indicates whether the platform
|
||||||
|
@ -216,6 +221,23 @@ The :mod:`uuid` module defines the following functions:
|
||||||
|
|
||||||
.. index:: single: uuid5
|
.. index:: single: uuid5
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: uuid8(a=None, b=None, c=None)
|
||||||
|
|
||||||
|
Generate a pseudo-random UUID according to
|
||||||
|
:rfc:`RFC 9562, §5.8 <9562#section-5.8>`.
|
||||||
|
|
||||||
|
When specified, the parameters *a*, *b* and *c* are expected to be
|
||||||
|
positive integers of 48, 12 and 62 bits respectively. If they exceed
|
||||||
|
their expected bit count, only their least significant bits are kept;
|
||||||
|
non-specified arguments are substituted for a pseudo-random integer of
|
||||||
|
appropriate size.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
.. index:: single: uuid8
|
||||||
|
|
||||||
|
|
||||||
The :mod:`uuid` module defines the following namespace identifiers for use with
|
The :mod:`uuid` module defines the following namespace identifiers for use with
|
||||||
:func:`uuid3` or :func:`uuid5`.
|
:func:`uuid3` or :func:`uuid5`.
|
||||||
|
|
||||||
|
@ -252,7 +274,9 @@ of the :attr:`~UUID.variant` attribute:
|
||||||
|
|
||||||
.. data:: RFC_4122
|
.. data:: RFC_4122
|
||||||
|
|
||||||
Specifies the UUID layout given in :rfc:`4122`.
|
Specifies the UUID layout given in :rfc:`4122`. This constant is kept
|
||||||
|
for backward compatibility even though :rfc:`4122` has been superseded
|
||||||
|
by :rfc:`9562`.
|
||||||
|
|
||||||
|
|
||||||
.. data:: RESERVED_MICROSOFT
|
.. data:: RESERVED_MICROSOFT
|
||||||
|
@ -267,7 +291,7 @@ of the :attr:`~UUID.variant` attribute:
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
:rfc:`4122` - A Universally Unique IDentifier (UUID) URN Namespace
|
:rfc:`9562` - A Universally Unique IDentifier (UUID) URN Namespace
|
||||||
This specification defines a Uniform Resource Name namespace for UUIDs, the
|
This specification defines a Uniform Resource Name namespace for UUIDs, the
|
||||||
internal format of UUIDs, and methods of generating UUIDs.
|
internal format of UUIDs, and methods of generating UUIDs.
|
||||||
|
|
||||||
|
@ -283,7 +307,7 @@ The :mod:`uuid` module can be executed as a script from the command line.
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5}] [-n NAMESPACE] [-N NAME]
|
python -m uuid [-h] [-u {uuid1,uuid3,uuid4,uuid5,uuid8}] [-n NAMESPACE] [-N NAME]
|
||||||
|
|
||||||
The following options are accepted:
|
The following options are accepted:
|
||||||
|
|
||||||
|
|
|
@ -517,6 +517,14 @@ unittest
|
||||||
(Contributed by Jacob Walls in :gh:`80958`.)
|
(Contributed by Jacob Walls in :gh:`80958`.)
|
||||||
|
|
||||||
|
|
||||||
|
uuid
|
||||||
|
----
|
||||||
|
|
||||||
|
* Add support for UUID version 8 via :func:`uuid.uuid8` as specified
|
||||||
|
in :rfc:`9562`.
|
||||||
|
(Contributed by Bénédikt Tran in :gh:`89083`.)
|
||||||
|
|
||||||
|
|
||||||
.. Add improved modules above alphabetically, not here at the end.
|
.. Add improved modules above alphabetically, not here at the end.
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
|
|
|
@ -8,8 +8,10 @@ import enum
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
|
import random
|
||||||
import sys
|
import sys
|
||||||
import weakref
|
import weakref
|
||||||
|
from itertools import product
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid'])
|
py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid'])
|
||||||
|
@ -267,7 +269,7 @@ class BaseTestUUID:
|
||||||
|
|
||||||
# Version number out of range.
|
# Version number out of range.
|
||||||
badvalue(lambda: self.uuid.UUID('00'*16, version=0))
|
badvalue(lambda: self.uuid.UUID('00'*16, version=0))
|
||||||
badvalue(lambda: self.uuid.UUID('00'*16, version=6))
|
badvalue(lambda: self.uuid.UUID('00'*16, version=42))
|
||||||
|
|
||||||
# Integer value out of range.
|
# Integer value out of range.
|
||||||
badvalue(lambda: self.uuid.UUID(int=-1))
|
badvalue(lambda: self.uuid.UUID(int=-1))
|
||||||
|
@ -681,6 +683,37 @@ class BaseTestUUID:
|
||||||
equal(u, self.uuid.UUID(v))
|
equal(u, self.uuid.UUID(v))
|
||||||
equal(str(u), v)
|
equal(str(u), v)
|
||||||
|
|
||||||
|
def test_uuid8(self):
|
||||||
|
equal = self.assertEqual
|
||||||
|
u = self.uuid.uuid8()
|
||||||
|
|
||||||
|
equal(u.variant, self.uuid.RFC_4122)
|
||||||
|
equal(u.version, 8)
|
||||||
|
|
||||||
|
for (_, hi, mid, lo) in product(
|
||||||
|
range(10), # repeat 10 times
|
||||||
|
[None, 0, random.getrandbits(48)],
|
||||||
|
[None, 0, random.getrandbits(12)],
|
||||||
|
[None, 0, random.getrandbits(62)],
|
||||||
|
):
|
||||||
|
u = self.uuid.uuid8(hi, mid, lo)
|
||||||
|
equal(u.variant, self.uuid.RFC_4122)
|
||||||
|
equal(u.version, 8)
|
||||||
|
if hi is not None:
|
||||||
|
equal((u.int >> 80) & 0xffffffffffff, hi)
|
||||||
|
if mid is not None:
|
||||||
|
equal((u.int >> 64) & 0xfff, mid)
|
||||||
|
if lo is not None:
|
||||||
|
equal(u.int & 0x3fffffffffffffff, lo)
|
||||||
|
|
||||||
|
def test_uuid8_uniqueness(self):
|
||||||
|
# Test that UUIDv8-generated values are unique
|
||||||
|
# (up to a negligible probability of failure).
|
||||||
|
u1 = self.uuid.uuid8()
|
||||||
|
u2 = self.uuid.uuid8()
|
||||||
|
self.assertNotEqual(u1.int, u2.int)
|
||||||
|
self.assertEqual(u1.version, u2.version)
|
||||||
|
|
||||||
@support.requires_fork()
|
@support.requires_fork()
|
||||||
def testIssue8621(self):
|
def testIssue8621(self):
|
||||||
# On at least some versions of OSX self.uuid.uuid4 generates
|
# On at least some versions of OSX self.uuid.uuid4 generates
|
||||||
|
|
41
Lib/uuid.py
41
Lib/uuid.py
|
@ -1,8 +1,8 @@
|
||||||
r"""UUID objects (universally unique identifiers) according to RFC 4122.
|
r"""UUID objects (universally unique identifiers) according to RFC 4122/9562.
|
||||||
|
|
||||||
This module provides immutable UUID objects (class UUID) and the functions
|
This module provides immutable UUID objects (class UUID) and the functions
|
||||||
uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
|
uuid1(), uuid3(), uuid4(), uuid5(), and uuid8() for generating version 1, 3,
|
||||||
UUIDs as specified in RFC 4122.
|
4, 5, and 8 UUIDs as specified in RFC 4122/9562.
|
||||||
|
|
||||||
If all you want is a unique ID, you should probably call uuid1() or uuid4().
|
If all you want is a unique ID, you should probably call uuid1() or uuid4().
|
||||||
Note that uuid1() may compromise privacy since it creates a UUID containing
|
Note that uuid1() may compromise privacy since it creates a UUID containing
|
||||||
|
@ -124,12 +124,12 @@ class UUID:
|
||||||
|
|
||||||
int the UUID as a 128-bit integer
|
int the UUID as a 128-bit integer
|
||||||
|
|
||||||
urn the UUID as a URN as specified in RFC 4122
|
urn the UUID as a URN as specified in RFC 4122/9562
|
||||||
|
|
||||||
variant the UUID variant (one of the constants RESERVED_NCS,
|
variant the UUID variant (one of the constants RESERVED_NCS,
|
||||||
RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
|
RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
|
||||||
|
|
||||||
version the UUID version number (1 through 5, meaningful only
|
version the UUID version number (1 through 8, meaningful only
|
||||||
when the variant is RFC_4122)
|
when the variant is RFC_4122)
|
||||||
|
|
||||||
is_safe An enum indicating whether the UUID has been generated in
|
is_safe An enum indicating whether the UUID has been generated in
|
||||||
|
@ -214,9 +214,9 @@ class UUID:
|
||||||
if not 0 <= int < 1<<128:
|
if not 0 <= int < 1<<128:
|
||||||
raise ValueError('int is out of range (need a 128-bit value)')
|
raise ValueError('int is out of range (need a 128-bit value)')
|
||||||
if version is not None:
|
if version is not None:
|
||||||
if not 1 <= version <= 5:
|
if not 1 <= version <= 8:
|
||||||
raise ValueError('illegal version number')
|
raise ValueError('illegal version number')
|
||||||
# Set the variant to RFC 4122.
|
# Set the variant to RFC 4122/9562.
|
||||||
int &= ~(0xc000 << 48)
|
int &= ~(0xc000 << 48)
|
||||||
int |= 0x8000 << 48
|
int |= 0x8000 << 48
|
||||||
# Set the version number.
|
# Set the version number.
|
||||||
|
@ -355,7 +355,7 @@ class UUID:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
# The version bits are only meaningful for RFC 4122 UUIDs.
|
# The version bits are only meaningful for RFC 4122/9562 UUIDs.
|
||||||
if self.variant == RFC_4122:
|
if self.variant == RFC_4122:
|
||||||
return int((self.int >> 76) & 0xf)
|
return int((self.int >> 76) & 0xf)
|
||||||
|
|
||||||
|
@ -719,6 +719,28 @@ def uuid5(namespace, name):
|
||||||
hash = sha1(namespace.bytes + name).digest()
|
hash = sha1(namespace.bytes + name).digest()
|
||||||
return UUID(bytes=hash[:16], version=5)
|
return UUID(bytes=hash[:16], version=5)
|
||||||
|
|
||||||
|
def uuid8(a=None, b=None, c=None):
|
||||||
|
"""Generate a UUID from three custom blocks.
|
||||||
|
|
||||||
|
* 'a' is the first 48-bit chunk of the UUID (octets 0-5);
|
||||||
|
* 'b' is the mid 12-bit chunk (octets 6-7);
|
||||||
|
* 'c' is the last 62-bit chunk (octets 8-15).
|
||||||
|
|
||||||
|
When a value is not specified, a pseudo-random value is generated.
|
||||||
|
"""
|
||||||
|
if a is None:
|
||||||
|
import random
|
||||||
|
a = random.getrandbits(48)
|
||||||
|
if b is None:
|
||||||
|
import random
|
||||||
|
b = random.getrandbits(12)
|
||||||
|
if c is None:
|
||||||
|
import random
|
||||||
|
c = random.getrandbits(62)
|
||||||
|
int_uuid_8 = (a & 0xffff_ffff_ffff) << 80
|
||||||
|
int_uuid_8 |= (b & 0xfff) << 64
|
||||||
|
int_uuid_8 |= c & 0x3fff_ffff_ffff_ffff
|
||||||
|
return UUID(int=int_uuid_8, version=8)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run the uuid command line interface."""
|
"""Run the uuid command line interface."""
|
||||||
|
@ -726,7 +748,8 @@ def main():
|
||||||
"uuid1": uuid1,
|
"uuid1": uuid1,
|
||||||
"uuid3": uuid3,
|
"uuid3": uuid3,
|
||||||
"uuid4": uuid4,
|
"uuid4": uuid4,
|
||||||
"uuid5": uuid5
|
"uuid5": uuid5,
|
||||||
|
"uuid8": uuid8,
|
||||||
}
|
}
|
||||||
uuid_namespace_funcs = ("uuid3", "uuid5")
|
uuid_namespace_funcs = ("uuid3", "uuid5")
|
||||||
namespaces = {
|
namespaces = {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add :func:`uuid.uuid8` for generating UUIDv8 objects as specified in
|
||||||
|
:rfc:`9562`. Patch by Bénédikt Tran
|
Loading…
Reference in New Issue