diff --git a/Doc/library/uuid.rst b/Doc/library/uuid.rst index 4b505c81c06f0f9..6f3c3cf090ed67f 100644 --- a/Doc/library/uuid.rst +++ b/Doc/library/uuid.rst @@ -201,6 +201,11 @@ The :mod:`!uuid` module defines the following functions: If *node* or *clock_seq* exceed their expected bit count, only their least significant bits are kept. + .. versionchanged:: next + On Windows, when neither *node* nor *clock_seq* is given, the UUID is + now generated by the system ``UuidCreateSequential()`` function and + reported as safe by :attr:`UUID.is_safe`. + .. function:: uuid3(namespace, name) diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 055be2994bf435e..134a020e86d3956 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -636,8 +636,17 @@ def test_uuid1_bogus_return_value(self): self.assertEqual(u.is_safe, self.uuid.SafeUUID.unknown) self.assertEqual(u.version, 1) + def test_uuid1_windows_safe(self): + if self.uuid._UuidCreate is None: + self.skipTest('requires _uuid.UuidCreate') + u = self.uuid.uuid1() + self.assertEqual(u.is_safe, self.uuid.SafeUUID.safe) + self.assertEqual(u.version, 1) + self.assertEqual(u.variant, self.uuid.RFC_4122) + def test_uuid1_time(self): with mock.patch.object(self.uuid, '_generate_time_safe', None), \ + mock.patch.object(self.uuid, '_UuidCreate', None), \ mock.patch.object(self.uuid, '_last_timestamp', None), \ mock.patch.object(self.uuid, 'getnode', return_value=93328246233727), \ mock.patch('time.time_ns', return_value=1545052026752910643), \ diff --git a/Lib/uuid.py b/Lib/uuid.py index 4bdcb67775a2ea1..e0dd821458cfd66 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -729,16 +729,20 @@ def uuid1(node=None, clock_seq=None): address. If 'clock_seq' is given, it is used as the sequence number; otherwise a random 14-bit sequence number is chosen.""" - # When the system provides a version-1 UUID generator, use it (but don't - # use UuidCreate here because its UUIDs don't conform to RFC 4122). - if _generate_time_safe is not None and node is clock_seq is None: - uuid_time, safely_generated = _generate_time_safe() - try: - is_safe = SafeUUID(safely_generated) - except ValueError: - is_safe = SafeUUID.unknown - # The version field is assumed to be handled by _generate_time_safe(). - return UUID(bytes=uuid_time, is_safe=is_safe) + # When the system provides a version-1 UUID generator, use it. + if node is clock_seq is None: + if _generate_time_safe is not None: + uuid_time, safely_generated = _generate_time_safe() + try: + is_safe = SafeUUID(safely_generated) + except ValueError: + is_safe = SafeUUID.unknown + # The version field is assumed to be handled by + # _generate_time_safe(). + return UUID(bytes=uuid_time, is_safe=is_safe) + if _UuidCreate is not None: + uuid_bytes = _UuidCreate() + return UUID(bytes_le=uuid_bytes, is_safe=SafeUUID.safe) global _last_timestamp nanoseconds = time.time_ns() diff --git a/Misc/NEWS.d/next/Library/2026-07-26-22-22-51.gh-issue-118993.U1adlE.rst b/Misc/NEWS.d/next/Library/2026-07-26-22-22-51.gh-issue-118993.U1adlE.rst new file mode 100644 index 000000000000000..0343d39247e4800 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-26-22-22-51.gh-issue-118993.U1adlE.rst @@ -0,0 +1,4 @@ +On Windows, :func:`uuid.uuid1` now uses the system +``UuidCreateSequential()`` function when neither *node* nor *clock_seq* is +given, so concurrent processes no longer produce colliding UUIDs and the +result is reported as safe by :attr:`uuid.UUID.is_safe`.