bpo-22807: Expose platform UUID generation safety information.
a UUID containing the computer's network address. :func:`uuid4` creates a
random UUID.
+Depending on support from the underlying platform, :func:`uuid1` may or may
+not return a "safe" UUID. A safe UUID is one which is generated using
+synchronization methods that ensure no two processes can obtain the same
+UUID. All instances of :class:`UUID` have an :attr:`is_safe` attribute
+which relays any information about the UUID's safety, using this enumeration:
-.. class:: UUID(hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None)
+.. class:: SafeUUID
+
+ .. versionadded:: 3.7
+
+ .. attribute:: SafeUUID.safe
+
+ The UUID was generated by the platform in a multiprocessing-safe way.
+
+ .. attribute:: SafeUUID.unsafe
+
+ The UUID was not generated in a multiprocessing-safe way.
+
+ .. attribute:: SafeUUID.unknown
+
+ The platform does not provide information on whether the UUID was
+ generated safely or not.
+
+.. class:: UUID(hex=None, bytes=None, bytes_le=None, fields=None, int=None, version=None, *, is_safe=SafeUUID.unknown)
Create a UUID from either a string of 32 hexadecimal digits, a string of 16
bytes as the *bytes* argument, a string of 16 bytes in little-endian order as
The UUID version number (1 through 5, meaningful only when the variant is
:const:`RFC_4122`).
+.. attribute:: UUID.is_safe
+
+ An enumeration of :class:`SafeUUID` which indicates whether the platform
+ generated the UUID in a multiprocessing-safe way.
+
+ .. versionadded:: 3.7
+
The :mod:`uuid` module defines the following functions:
equal(((u.clock_seq_hi_variant & 0x3f) << 8) |
u.clock_seq_low, 0x3fff)
+ @unittest.skipUnless(uuid._uuid_generate_time.restype is not None,
+ 'requires uuid_generate_time_safe(3)')
+ @unittest.skipUnless(importable('ctypes'), 'requires ctypes')
+ def test_uuid1_safe(self):
+ u = uuid.uuid1()
+ # uuid_generate_time_safe() may return 0 or -1 but what it returns is
+ # dependent on the underlying platform support. At least it cannot be
+ # unknown (unless I suppose the platform is buggy).
+ self.assertNotEqual(u.is_safe, uuid.SafeUUID.unknown)
+
+ @unittest.skipUnless(importable('ctypes'), 'requires ctypes')
+ def test_uuid1_unknown(self):
+ # Even if the platform has uuid_generate_time_safe(), let's mock it to
+ # be uuid_generate_time() and ensure the safety is unknown.
+ with unittest.mock.patch.object(uuid._uuid_generate_time,
+ 'restype', None):
+ u = uuid.uuid1()
+ self.assertEqual(u.is_safe, uuid.SafeUUID.unknown)
+
+ @unittest.skipUnless(importable('ctypes'), 'requires ctypes')
+ def test_uuid1_is_safe(self):
+ with unittest.mock.patch.object(uuid._uuid_generate_time,
+ 'restype', lambda x: 0):
+ u = uuid.uuid1()
+ self.assertEqual(u.is_safe, uuid.SafeUUID.safe)
+
+ @unittest.skipUnless(importable('ctypes'), 'requires ctypes')
+ def test_uuid1_is_unsafe(self):
+ with unittest.mock.patch.object(uuid._uuid_generate_time,
+ 'restype', lambda x: -1):
+ u = uuid.uuid1()
+ self.assertEqual(u.is_safe, uuid.SafeUUID.unsafe)
+
+ @unittest.skipUnless(importable('ctypes'), 'requires ctypes')
+ def test_uuid1_bogus_return_value(self):
+ with unittest.mock.patch.object(uuid._uuid_generate_time,
+ 'restype', lambda x: 3):
+ u = uuid.uuid1()
+ self.assertEqual(u.is_safe, uuid.SafeUUID.unknown)
+
def test_uuid3(self):
equal = self.assertEqual
import os
+from enum import Enum
+
+
__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
int_ = int # The built-in int type
bytes_ = bytes # The built-in bytes type
-class UUID(object):
+
+class SafeUUID(Enum):
+ safe = 0
+ unsafe = -1
+ unknown = None
+
+
+class UUID:
"""Instances of the UUID class represent UUIDs as specified in RFC 4122.
UUID objects are immutable, hashable, and usable as dictionary keys.
Converting a UUID to a string with str() yields something in the form
version the UUID version number (1 through 5, meaningful only
when the variant is RFC_4122)
+
+ is_safe An enum indicating whether the UUID has been generated in
+ a way that is safe for multiprocessing applications, via
+ uuid_generate_time_safe(3).
"""
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
- int=None, version=None):
+ int=None, version=None,
+ *, is_safe=SafeUUID.unknown):
r"""Create a UUID from either a string of 32 hexadecimal digits,
a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
in little-endian order as the 'bytes_le' argument, a tuple of six
be given. The 'version' argument is optional; if given, the resulting
UUID will have its variant and version set according to RFC 4122,
overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.
+
+ is_safe is an enum exposed as an attribute on the instance. It
+ indicates whether the UUID has been generated in a way that is safe
+ for multiprocessing applications, via uuid_generate_time_safe(3).
"""
if [hex, bytes, bytes_le, fields, int].count(None) != 4:
int &= ~(0xf000 << 64)
int |= version << 76
self.__dict__['int'] = int
+ self.__dict__['is_safe'] = is_safe
def __eq__(self, other):
if isinstance(other, UUID):
for libname in _libnames:
try:
lib = ctypes.CDLL(ctypes.util.find_library(libname))
- except Exception:
+ except Exception: # pragma: nocover
continue
- if hasattr(lib, 'uuid_generate_time'):
+ # Try to find the safe variety first.
+ if hasattr(lib, 'uuid_generate_time_safe'):
+ _uuid_generate_time = lib.uuid_generate_time_safe
+ # int uuid_generate_time_safe(uuid_t out);
+ break
+ elif hasattr(lib, 'uuid_generate_time'): # pragma: nocover
_uuid_generate_time = lib.uuid_generate_time
+ # void uuid_generate_time(uuid_t out);
+ _uuid_generate_time.restype = None
break
del _libnames
# use UuidCreate here because its UUIDs don't conform to RFC 4122).
if _uuid_generate_time and node is clock_seq is None:
_buffer = ctypes.create_string_buffer(16)
- _uuid_generate_time(_buffer)
- return UUID(bytes=bytes_(_buffer.raw))
+ safely_generated = _uuid_generate_time(_buffer)
+ try:
+ is_safe = SafeUUID(safely_generated)
+ except ValueError:
+ is_safe = SafeUUID.unknown
+ return UUID(bytes=bytes_(_buffer.raw), is_safe=is_safe)
global _last_timestamp
import time
Library
-------
+- bpo-22807: Add uuid.SafeUUID and uuid.UUID.is_safe to relay information from
+ the platform about whether generated UUIDs are generated with a
+ multiprocessing safe method.
+
- bpo-29576: Improve some deprecations in importlib. Some deprecated methods
now emit DeprecationWarnings and have better descriptive messages.