11r"""UUID objects (universally unique identifiers) according to RFC 4122.
22
33This module provides immutable UUID objects (class UUID) and the functions
4- uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
5- UUIDs as specified in RFC 4122.
4+ uuid1(), uuid3(), uuid4(), uuid5() for generating version 1 to 8 UUIDs as
5+ specified in RFC 4122 (superseeded by RFC 9562 but will still be referred
6+ to as RFC 4122 in the future for compatibility purposes).
67
78If all you want is a unique ID, you should probably call uuid1() or uuid4().
89Note that uuid1() may compromise privacy since it creates a UUID containing
@@ -129,7 +130,7 @@ class UUID:
129130 variant the UUID variant (one of the constants RESERVED_NCS,
130131 RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
131132
132- version the UUID version number (1 through 5 , meaningful only
133+ version the UUID version number (1 through 8 , meaningful only
133134 when the variant is RFC_4122)
134135
135136 is_safe An enum indicating whether the UUID has been generated in
@@ -214,9 +215,9 @@ def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
214215 if not 0 <= int < 1 << 128 :
215216 raise ValueError ('int is out of range (need a 128-bit value)' )
216217 if version is not None :
217- if not 1 <= version <= 5 :
218+ if not 1 <= version <= 8 :
218219 raise ValueError ('illegal version number' )
219- # Set the variant to RFC 4122 .
220+ # Set the variant to RFC 9562 .
220221 int &= ~ (0xc000 << 48 )
221222 int |= 0x8000 << 48
222223 # Set the version number.
@@ -297,17 +298,29 @@ def bytes_le(self):
297298
298299 @property
299300 def fields (self ):
301+ if self .version == 6 :
302+ # the first field should be a 32-bit integer
303+ return (self .time_hi , self .time_mid , self .time_hi_version ,
304+ self .clock_seq_hi_variant , self .clock_seq_low , self .node )
300305 return (self .time_low , self .time_mid , self .time_hi_version ,
301306 self .clock_seq_hi_variant , self .clock_seq_low , self .node )
302307
303308 @property
304309 def time_low (self ):
310+ if self .version == 6 :
311+ return (self .int >> 64 ) & 0x0fff
305312 return self .int >> 96
306313
307314 @property
308315 def time_mid (self ):
309316 return (self .int >> 80 ) & 0xffff
310317
318+ @property
319+ def time_hi (self ):
320+ if self .version == 6 :
321+ return self .int >> 96
322+ return (self .int >> 64 ) & 0x0fff
323+
311324 @property
312325 def time_hi_version (self ):
313326 return (self .int >> 64 ) & 0xffff
@@ -322,8 +335,9 @@ def clock_seq_low(self):
322335
323336 @property
324337 def time (self ):
325- return (((self .time_hi_version & 0x0fff ) << 48 ) |
326- (self .time_mid << 32 ) | self .time_low )
338+ if self .version == 6 :
339+ return (self .time_hi << 28 ) | (self .time_mid << 12 ) | self .time_low
340+ return (self .time_hi << 48 ) | (self .time_mid << 32 ) | self .time_low
327341
328342 @property
329343 def clock_seq (self ):
@@ -656,7 +670,7 @@ def getnode():
656670 assert False , '_random_getnode() returned invalid value: {}' .format (_node )
657671
658672
659- _last_timestamp = None
673+ _last_timestamp_v1 = None
660674
661675def uuid1 (node = None , clock_seq = None ):
662676 """Generate a UUID from a host ID, sequence number, and the current time.
@@ -674,15 +688,15 @@ def uuid1(node=None, clock_seq=None):
674688 is_safe = SafeUUID .unknown
675689 return UUID (bytes = uuid_time , is_safe = is_safe )
676690
677- global _last_timestamp
691+ global _last_timestamp_v1
678692 import time
679693 nanoseconds = time .time_ns ()
680694 # 0x01b21dd213814000 is the number of 100-ns intervals between the
681695 # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
682696 timestamp = nanoseconds // 100 + 0x01b21dd213814000
683- if _last_timestamp is not None and timestamp <= _last_timestamp :
684- timestamp = _last_timestamp + 1
685- _last_timestamp = timestamp
697+ if _last_timestamp_v1 is not None and timestamp <= _last_timestamp_v1 :
698+ timestamp = _last_timestamp_v1 + 1
699+ _last_timestamp_v1 = timestamp
686700 if clock_seq is None :
687701 import random
688702 clock_seq = random .getrandbits (14 ) # instead of stable storage
@@ -719,14 +733,97 @@ def uuid5(namespace, name):
719733 hash = sha1 (namespace .bytes + name ).digest ()
720734 return UUID (bytes = hash [:16 ], version = 5 )
721735
736+ _last_timestamp_v6 = None
737+
738+ def uuid6 (node = None , clock_seq = None ):
739+ """Similar to :func:`uuid1` but where fields are ordered differently
740+ for improved DB locality.
741+
742+ More precisely, given a 60-bit timestamp value as specified for UUIDv1,
743+ for UUIDv6 the first 48 most significant bits are stored first, followed
744+ by the 4-bit version (same position), followed by the remaining 12 bits
745+ of the original 60-bit timestamp.
746+ """
747+ global _last_timestamp_v6
748+ import time
749+ nanoseconds = time .time_ns ()
750+ # 0x01b21dd213814000 is the number of 100-ns intervals between the
751+ # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
752+ timestamp = nanoseconds // 100 + 0x01b21dd213814000
753+ if _last_timestamp_v6 is not None and timestamp <= _last_timestamp_v6 :
754+ timestamp = _last_timestamp_v6 + 1
755+ _last_timestamp_v6 = timestamp
756+ if clock_seq is None :
757+ import random
758+ clock_seq = random .getrandbits (14 ) # instead of stable storage
759+ time_hi_and_mid = (timestamp >> 12 ) & 0xffffffffffff
760+ time_ver_and_lo = timestamp & 0xffff
761+ var_and_clock_s = clock_seq & 0x3fff
762+ if node is None :
763+ node = getnode ()
764+ int_uuid_6 = time_hi_and_mid << 80
765+ int_uuid_6 |= time_ver_and_lo << 64
766+ int_uuid_6 |= var_and_clock_s << 48
767+ int_uuid_6 |= node & 0xffffffffffff
768+ return UUID (int = int_uuid_6 , version = 6 )
769+
770+ _last_timestamp_v7 = None
771+
772+ def uuid7 ():
773+ """Generate a UUID from a Unix timestamp in milliseconds and random bits."""
774+ global _last_timestamp_v7
775+ import time
776+ nanoseconds = time .time_ns ()
777+ timestamp_ms = nanoseconds // 10 ** 6 # may be improved
778+ if _last_timestamp_v7 is not None and timestamp_ms <= _last_timestamp_v7 :
779+ timestamp_ms = _last_timestamp_v7 + 1
780+ _last_timestamp_v7 = timestamp_ms
781+ int_uuid_7 = (timestamp_ms & 0xffffffffffff ) << 80
782+ # Ideally, we would have 'rand_a' = first 12 bits of 'rand'
783+ # and 'rand_b' = lowest 62 bits, but it is easier to test
784+ # when we pick 'rand_a' from the lowest bits of 'rand' and
785+ # 'rand_b' from the next 62 bits, ignoring the first bits
786+ # of 'rand'.
787+ rand = int .from_bytes (os .urandom (19 )) # 76 random bits (ignore 2 first)
788+ int_uuid_7 |= (rand & 0x0fff ) << 64 # rand_a
789+ int_uuid_7 |= (rand >> 12 ) & 0x3fffffffffffffff # rand_b
790+ return UUID (int = int_uuid_7 , version = 7 )
791+
792+ def uuid8 (a = None , b = None , c = None ):
793+ """Generate a UUID from three custom blocks.
794+
795+ 'a' is the first 48-bit chunk of the UUID (octets 0-5);
796+ 'b' is the mid 12-bit chunk (octets 6-7);
797+ 'c' is the last 62-bit chunk (octets 8-15).
798+
799+ When a value is not specified, a random value is generated.
800+ """
801+ if a is None :
802+ import random
803+ a = random .getrandbits (48 )
804+ if b is None :
805+ import random
806+ b = random .getrandbits (12 )
807+ if c is None :
808+ import random
809+ c = random .getrandbits (62 )
810+
811+ int_uuid_8 = (a & 0xffffffffffff ) << 80
812+ int_uuid_8 |= (b & 0xfff ) << 64
813+ int_uuid_8 |= c & 0x3fffffffffffffff
814+ return UUID (int = int_uuid_8 , version = 8 )
815+
722816
723817def main ():
724818 """Run the uuid command line interface."""
725819 uuid_funcs = {
726820 "uuid1" : uuid1 ,
727821 "uuid3" : uuid3 ,
728822 "uuid4" : uuid4 ,
729- "uuid5" : uuid5
823+ "uuid5" : uuid5 ,
824+ "uuid6" : uuid6 ,
825+ "uuid7" : uuid7 ,
826+ "uuid8" : uuid8 ,
730827 }
731828 uuid_namespace_funcs = ("uuid3" , "uuid5" )
732829 namespaces = {
0 commit comments