-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
gh-89083: add support for UUID version 7 (RFC 9562) #121119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
42d55b4
6826fa1
edc2cab
c6d26b6
2ddb4b8
bcd1417
4630c8f
cd80afb
c3d4745
392d289
26889ea
44b66e6
7be6dc4
8ba3d8b
a14ae9b
7a169c9
b082c90
94c70e9
05b7a2b
275deb7
5e97cc3
051f34e
bdf9a77
00661fc
0474de4
a446d53
2e39072
ebc1a07
694e07f
965dbc8
7ff4368
7c3cab6
e758741
c18d0c4
2df6f41
6fcb6a1
f6048c9
be3f024
99c6761
06befca
2aacadf
f7f536e
aee2898
1a5ac19
8764b28
af0baef
939b5a8
ef85b20
2d08821
eaa9ad4
571d2fe
f9ac658
a756b9d
4406796
d4eeded
0e54a72
40ab2fa
5ee85ad
3ce8943
59e6d7e
437d8cf
2d917b0
73ab656
54d07ae
6d76389
bd4ab55
e9ddb74
8755de0
12d7ad4
560d87c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -919,8 +919,9 @@ urllib | |
| uuid | ||
| ---- | ||
|
|
||
| * Add support for UUID versions 7 and 8 via :func:`uuid.uuid7` and | ||
| :func:`uuid.uuid8` respectively, as specified in :rfc:`9562`. | ||
| * Add support for UUID versions 6, 7, and 8 via :func:`uuid.uuid6`, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not to be pedantic, but is via correct? It seems to suggest that the functions are the only thing that support these versions, but the support is added in the UUID class and the functions are there too as a convenience.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but in general, we don't really want people to directly use the UUID class. Strictly speaking, we're only adding the support for the version value but we don't check how it's been generated. I prefer users to actually use the factories. Otherwise, I can say that the UUID class now accepts
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I slightly disagree but won’t argue 🙂 |
||
| :func:`uuid.uuid7`, and :func:`uuid.uuid8` respectively, as specified | ||
| in :rfc:`9562`. | ||
| (Contributed by Bénédikt Tran in :gh:`89083`.) | ||
|
|
||
| * :const:`uuid.NIL` and :const:`uuid.MAX` are now available to represent the | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,8 @@ | ||||||||
| r"""UUID objects (universally unique identifiers) according to RFC 4122/9562. | ||||||||
|
|
||||||||
| This module provides immutable UUID objects (class UUID) and the functions | ||||||||
| uuid1(), uuid3(), uuid4(), uuid5(), uuid7(), and uuid8() for generating | ||||||||
| version 1, 3, 4, 5, 7, and 8 UUIDs as specified in RFC 4122/9562. | ||||||||
| uuid{N}() for generating UUIDs version N as specified in RFC 4122/9562 for | ||||||||
| N = 1, 3, 4, 5, 6, 7, and 8. | ||||||||
|
picnixz marked this conversation as resolved.
Outdated
picnixz marked this conversation as resolved.
Outdated
|
||||||||
|
|
||||||||
| 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 | ||||||||
|
|
@@ -102,6 +102,7 @@ class SafeUUID: | |||||||
| _RFC_4122_VERSION_3_FLAGS = ((3 << 76) | (0x8000 << 48)) | ||||||||
| _RFC_4122_VERSION_4_FLAGS = ((4 << 76) | (0x8000 << 48)) | ||||||||
| _RFC_4122_VERSION_5_FLAGS = ((5 << 76) | (0x8000 << 48)) | ||||||||
| _RFC_4122_VERSION_6_FLAGS = ((6 << 76) | (0x8000 << 48)) | ||||||||
| _RFC_4122_VERSION_7_FLAGS = ((7 << 76) | (0x8000 << 48)) | ||||||||
| _RFC_4122_VERSION_8_FLAGS = ((8 << 76) | (0x8000 << 48)) | ||||||||
|
|
||||||||
|
|
@@ -770,6 +771,44 @@ def uuid5(namespace, name): | |||||||
| int_uuid_5 |= _RFC_4122_VERSION_5_FLAGS | ||||||||
| return UUID._from_int(int_uuid_5) | ||||||||
|
|
||||||||
| _last_timestamp_v6 = None | ||||||||
|
|
||||||||
| def uuid6(node=None, clock_seq=None): | ||||||||
| """Similar to :func:`uuid1` but where fields are ordered differently | ||||||||
| for improved DB locality. | ||||||||
|
|
||||||||
| More precisely, given a 60-bit timestamp value as specified for UUIDv1, | ||||||||
| for UUIDv6 the first 48 most significant bits are stored first, followed | ||||||||
| by the 4-bit version (same position), followed by the remaining 12 bits | ||||||||
| of the original 60-bit timestamp. | ||||||||
| """ | ||||||||
| global _last_timestamp_v6 | ||||||||
| import time | ||||||||
| nanoseconds = time.time_ns() | ||||||||
| # 0x01b21dd213814000 is the number of 100-ns intervals between the | ||||||||
| # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. | ||||||||
| timestamp = nanoseconds // 100 + 0x01b21dd213814000 | ||||||||
| if _last_timestamp_v6 is not None and timestamp <= _last_timestamp_v6: | ||||||||
| timestamp = _last_timestamp_v6 + 1 | ||||||||
| _last_timestamp_v6 = timestamp | ||||||||
| if clock_seq is None: | ||||||||
| import random | ||||||||
| clock_seq = random.getrandbits(14) # instead of stable storage | ||||||||
| time_hi_and_mid = (timestamp >> 12) & 0xffff_ffff_ffff | ||||||||
| time_lo = timestamp & 0x0fff # keep 12 bits and clear version bits | ||||||||
| clock_s = clock_seq & 0x3fff # keep 14 bits and clear variant bits | ||||||||
| if node is None: | ||||||||
| node = getnode() | ||||||||
| # --- 32 + 16 --- -- 4 -- -- 12 -- -- 2 -- -- 14 --- 48 | ||||||||
| # time_hi_and_mid | version | time_lo | variant | clock_seq | node | ||||||||
| int_uuid_6 = time_hi_and_mid << 80 | ||||||||
| int_uuid_6 |= time_lo << 64 | ||||||||
| int_uuid_6 |= clock_s << 48 | ||||||||
| int_uuid_6 |= node & 0xffff_ffff_ffff | ||||||||
| # by construction, the variant and version bits are already cleared | ||||||||
| int_uuid_6 |= _RFC_4122_VERSION_6_FLAGS | ||||||||
| return UUID._from_int(int_uuid_6) | ||||||||
|
|
||||||||
| _last_timestamp_v7 = None | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to apply a PEP-8 change in a separate PR because the module has inconsistencies. It seems a bit weird to only PEP-8ify this part of the code while the rest is not really PEP-8ified. See #121119 (comment).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. python-dev doesn’t have a practice of doing reformatting-only PRs. Instead, follow good conventions in code that is added or already changed.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well... if a core dev endorses the change, I think it's fine. I don't mind endorsing it. I didn't do it for uuid6() nor for uuid8() when I wrote the function as there were more 1-blank lines separations rather than 2 blank lines separations. But if you insist on adding 2 blank lines, I'll also add them around the other functions because I prefer being consistent in this case (honestly, having 2 blank lines around only UUIDv7 makes it harder to read IMO).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say PEP-8 tells me that we can also ignore the PEP if the surrounding code already breaks it. But I will make a commit to just add blank lines around the functions I've added (uuid6 to uuid8).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that it's worth it to reformat the whole uuid.py file to PEP 8, but respecting PEP 8 for new code (or code near changed code) is a good practice.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, adding a few blank lines is innocuous (it does not change git blame, or risk changing the meaning of code), so it’s fine to do in existing code in this PR. Generally people saying they want to «apply PEP 8» think of more bigger changes. [note: marking this convo as unresolved just to help Victor or Hugo see it, not because there’s something left to do for the PR author]
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is about for example methods using camelCase in unittest or logging, not spaces! |
||||||||
| _last_counter_v7 = 0 # 42-bit counter | ||||||||
|
|
||||||||
|
|
@@ -876,6 +915,7 @@ def main(): | |||||||
| "uuid3": uuid3, | ||||||||
| "uuid4": uuid4, | ||||||||
| "uuid5": uuid5, | ||||||||
| "uuid6": uuid6, | ||||||||
| "uuid7": uuid7, | ||||||||
| "uuid8": uuid8, | ||||||||
| } | ||||||||
|
|
||||||||
Uh oh!
There was an error while loading. Please reload this page.