Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/uuid6/__init__.py: 26%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1r"""UUID objects (universally unique identifiers) according to RFC 9562.
3This module provides the functions uuid6(), uuid7(), and uuid8() for
4generating version 6, 7, and 8 UUIDs as specified in RFC 9562.
5"""
7import importlib.metadata
8import secrets
9import time
10import uuid
11from typing import Optional, Tuple
13__version__ = importlib.metadata.version(__package__)
16class UUID(uuid.UUID):
17 r"""Instances of the UUID class represent UUIDs as specified in RFC 9562."""
19 __slots__ = ()
21 def __init__(
22 self,
23 hex: Optional[str] = None,
24 bytes: Optional[bytes] = None,
25 bytes_le: Optional[bytes] = None,
26 fields: Optional[Tuple[int, int, int, int, int, int]] = None,
27 int: Optional[int] = None,
28 version: Optional[int] = None,
29 *,
30 is_safe: uuid.SafeUUID = uuid.SafeUUID.unknown,
31 ) -> None:
32 r"""Create a UUID."""
34 if int is None or [hex, bytes, bytes_le, fields].count(None) != 4:
35 return super().__init__(
36 hex=hex,
37 bytes=bytes,
38 bytes_le=bytes_le,
39 fields=fields,
40 int=int,
41 version=version,
42 is_safe=is_safe,
43 )
44 if not 0 <= int < 1 << 128:
45 raise ValueError("int is out of range (need a 128-bit value)")
46 if version is not None:
47 if not 6 <= version <= 8:
48 raise ValueError("illegal version number")
49 # Set the variant to RFC 4122.
50 int &= ~(0xC000 << 48)
51 int |= 0x8000 << 48
52 # Set the version number.
53 int &= ~(0xF000 << 64)
54 int |= version << 76
55 super().__init__(int=int, is_safe=is_safe)
57 @property
58 def subsec(self) -> int:
59 return ((self.int >> 64) & 0x0FFF) << 8 | ((self.int >> 54) & 0xFF)
61 @property
62 def time(self) -> int:
63 if self.version == 6:
64 return (
65 (self.time_low << 28)
66 | (self.time_mid << 12)
67 | (self.time_hi_version & 0x0FFF)
68 )
69 if self.version == 7:
70 return self.int >> 80
71 if self.version == 8:
72 return (self.int >> 80) * 10**6 + _subsec_decode(self.subsec)
73 return super().time
76def _subsec_decode(value: int) -> int:
77 return -(-value * 10**6 // 2**20)
80def _subsec_encode(value: int) -> int:
81 return value * 2**20 // 10**6
84def uuid1_to_uuid6(uuid1: uuid.UUID) -> UUID:
85 r"""Generate a UUID version 6 object from a UUID version 1 object."""
86 if uuid1.version != 1:
87 raise ValueError("given UUID's version number must be 1")
88 h = uuid1.hex
89 h = h[13:16] + h[8:12] + h[0:5] + "6" + h[5:8] + h[16:]
90 return UUID(hex=h, is_safe=uuid1.is_safe)
93_last_v6_timestamp = None
94_last_v7_timestamp = None
95_last_v8_timestamp = None
98def uuid6(node: Optional[int] = None, clock_seq: Optional[int] = None) -> UUID:
99 r"""UUID version 6 is a field-compatible version of UUIDv1, reordered for
100 improved DB locality. It is expected that UUIDv6 will primarily be
101 used in contexts where there are existing v1 UUIDs. Systems that do
102 not involve legacy UUIDv1 SHOULD consider using UUIDv7 instead.
104 If 'node' is not given, a random 48-bit number is chosen.
106 If 'clock_seq' is given, it is used as the sequence number;
107 otherwise a random 14-bit sequence number is chosen."""
109 global _last_v6_timestamp
111 nanoseconds = time.time_ns()
112 # 0x01b21dd213814000 is the number of 100-ns intervals between the
113 # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
114 timestamp = nanoseconds // 100 + 0x01B21DD213814000
115 if _last_v6_timestamp is not None and timestamp <= _last_v6_timestamp:
116 timestamp = _last_v6_timestamp + 1
117 _last_v6_timestamp = timestamp
118 if clock_seq is None:
119 clock_seq = secrets.randbits(14) # instead of stable storage
120 if node is None:
121 node = secrets.randbits(48)
122 time_high_and_time_mid = (timestamp >> 12) & 0xFFFFFFFFFFFF
123 time_low_and_version = timestamp & 0x0FFF
124 uuid_int = time_high_and_time_mid << 80
125 uuid_int |= time_low_and_version << 64
126 uuid_int |= (clock_seq & 0x3FFF) << 48
127 uuid_int |= node & 0xFFFFFFFFFFFF
128 return UUID(int=uuid_int, version=6)
131def uuid7() -> UUID:
132 r"""UUID version 7 features a time-ordered value field derived from the
133 widely implemented and well known Unix Epoch timestamp source, the
134 number of milliseconds since midnight 1 Jan 1970 UTC, leap seconds
135 excluded. As well as improved entropy characteristics over versions
136 1 or 6.
138 Implementations SHOULD utilize UUID version 7 over UUID version 1 and
139 6 if possible."""
141 global _last_v7_timestamp
143 nanoseconds = time.time_ns()
144 timestamp_ms = nanoseconds // 10**6
145 if _last_v7_timestamp is not None and timestamp_ms <= _last_v7_timestamp:
146 timestamp_ms = _last_v7_timestamp + 1
147 _last_v7_timestamp = timestamp_ms
148 uuid_int = (timestamp_ms & 0xFFFFFFFFFFFF) << 80
149 uuid_int |= secrets.randbits(76)
150 return UUID(int=uuid_int, version=7)
153def uuid8() -> UUID:
154 r"""UUID version 8 features a time-ordered value field derived from the
155 widely implemented and well known Unix Epoch timestamp source, the
156 number of nanoseconds since midnight 1 Jan 1970 UTC, leap seconds
157 excluded."""
159 global _last_v8_timestamp
161 nanoseconds = time.time_ns()
162 if _last_v8_timestamp is not None and nanoseconds <= _last_v8_timestamp:
163 nanoseconds = _last_v8_timestamp + 1
164 _last_v8_timestamp = nanoseconds
165 timestamp_ms, timestamp_ns = divmod(nanoseconds, 10**6)
166 subsec = _subsec_encode(timestamp_ns)
167 subsec_a = subsec >> 8
168 subsec_b = subsec & 0xFF
169 uuid_int = (timestamp_ms & 0xFFFFFFFFFFFF) << 80
170 uuid_int |= subsec_a << 64
171 uuid_int |= subsec_b << 54
172 uuid_int |= secrets.randbits(54)
173 return UUID(int=uuid_int, version=8)