Update code and tests to support the 'bytes_le' attribute (for

little-endian byte order on Windows), and to work around clocks
with low resolution yielding duplicate UUIDs.

Anthony Baxter has approved this change.
This commit is contained in:
Ka-Ping Yee 2006-08-16 07:02:50 +00:00
parent d112bc7958
commit b1cb56ad17
2 changed files with 103 additions and 52 deletions

View file

@ -16,12 +16,13 @@ class TestUUID(TestCase):
def test_UUID(self):
equal = self.assertEqual
ascending = []
for (string, curly, hex, bytes, fields, integer, urn,
for (string, curly, hex, bytes, bytes_le, fields, integer, urn,
time, clock_seq, variant, version) in [
('00000000-0000-0000-0000-000000000000',
'{00000000-0000-0000-0000-000000000000}',
'00000000000000000000000000000000',
'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
(0, 0, 0, 0, 0, 0),
0,
'urn:uuid:00000000-0000-0000-0000-000000000000',
@ -30,6 +31,7 @@ class TestUUID(TestCase):
'{00010203-0405-0607-0809-0a0b0c0d0e0f}',
'000102030405060708090a0b0c0d0e0f',
'\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\x0d\x0e\x0f',
'\x03\x02\x01\0\x05\x04\x07\x06\x08\t\n\x0b\x0c\x0d\x0e\x0f',
(0x00010203L, 0x0405, 0x0607, 8, 9, 0x0a0b0c0d0e0fL),
0x000102030405060708090a0b0c0d0e0fL,
'urn:uuid:00010203-0405-0607-0809-0a0b0c0d0e0f',
@ -38,6 +40,7 @@ class TestUUID(TestCase):
'{02d9e6d5-9467-382e-8f9b-9300a64ac3cd}',
'02d9e6d59467382e8f9b9300a64ac3cd',
'\x02\xd9\xe6\xd5\x94\x67\x38\x2e\x8f\x9b\x93\x00\xa6\x4a\xc3\xcd',
'\xd5\xe6\xd9\x02\x67\x94\x2e\x38\x8f\x9b\x93\x00\xa6\x4a\xc3\xcd',
(0x02d9e6d5L, 0x9467, 0x382e, 0x8f, 0x9b, 0x9300a64ac3cdL),
0x02d9e6d59467382e8f9b9300a64ac3cdL,
'urn:uuid:02d9e6d5-9467-382e-8f9b-9300a64ac3cd',
@ -46,6 +49,7 @@ class TestUUID(TestCase):
'{12345678-1234-5678-1234-567812345678}',
'12345678123456781234567812345678',
'\x12\x34\x56\x78'*4,
'\x78\x56\x34\x12\x34\x12\x78\x56\x12\x34\x56\x78\x12\x34\x56\x78',
(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678),
0x12345678123456781234567812345678,
'urn:uuid:12345678-1234-5678-1234-567812345678',
@ -54,6 +58,7 @@ class TestUUID(TestCase):
'{6ba7b810-9dad-11d1-80b4-00c04fd430c8}',
'6ba7b8109dad11d180b400c04fd430c8',
'\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
'\x10\xb8\xa7\x6b\xad\x9d\xd1\x11\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
(0x6ba7b810L, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c04fd430c8L),
0x6ba7b8109dad11d180b400c04fd430c8L,
'urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8',
@ -62,6 +67,7 @@ class TestUUID(TestCase):
'{6ba7b811-9dad-11d1-80b4-00c04fd430c8}',
'6ba7b8119dad11d180b400c04fd430c8',
'\x6b\xa7\xb8\x11\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
'\x11\xb8\xa7\x6b\xad\x9d\xd1\x11\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
(0x6ba7b811L, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c04fd430c8L),
0x6ba7b8119dad11d180b400c04fd430c8L,
'urn:uuid:6ba7b811-9dad-11d1-80b4-00c04fd430c8',
@ -70,6 +76,7 @@ class TestUUID(TestCase):
'{6ba7b812-9dad-11d1-80b4-00c04fd430c8}',
'6ba7b8129dad11d180b400c04fd430c8',
'\x6b\xa7\xb8\x12\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
'\x12\xb8\xa7\x6b\xad\x9d\xd1\x11\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
(0x6ba7b812L, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c04fd430c8L),
0x6ba7b8129dad11d180b400c04fd430c8L,
'urn:uuid:6ba7b812-9dad-11d1-80b4-00c04fd430c8',
@ -78,6 +85,7 @@ class TestUUID(TestCase):
'{6ba7b814-9dad-11d1-80b4-00c04fd430c8}',
'6ba7b8149dad11d180b400c04fd430c8',
'\x6b\xa7\xb8\x14\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
'\x14\xb8\xa7\x6b\xad\x9d\xd1\x11\x80\xb4\x00\xc0\x4f\xd4\x30\xc8',
(0x6ba7b814L, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00c04fd430c8L),
0x6ba7b8149dad11d180b400c04fd430c8L,
'urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8',
@ -86,6 +94,7 @@ class TestUUID(TestCase):
'{7d444840-9dc0-11d1-b245-5ffdce74fad2}',
'7d4448409dc011d1b2455ffdce74fad2',
'\x7d\x44\x48\x40\x9d\xc0\x11\xd1\xb2\x45\x5f\xfd\xce\x74\xfa\xd2',
'\x40\x48\x44\x7d\xc0\x9d\xd1\x11\xb2\x45\x5f\xfd\xce\x74\xfa\xd2',
(0x7d444840L, 0x9dc0, 0x11d1, 0xb2, 0x45, 0x5ffdce74fad2L),
0x7d4448409dc011d1b2455ffdce74fad2L,
'urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2',
@ -94,6 +103,7 @@ class TestUUID(TestCase):
'{e902893a-9d22-3c7e-a7b8-d6e313b71d9f}',
'e902893a9d223c7ea7b8d6e313b71d9f',
'\xe9\x02\x89\x3a\x9d\x22\x3c\x7e\xa7\xb8\xd6\xe3\x13\xb7\x1d\x9f',
'\x3a\x89\x02\xe9\x22\x9d\x7e\x3c\xa7\xb8\xd6\xe3\x13\xb7\x1d\x9f',
(0xe902893aL, 0x9d22, 0x3c7e, 0xa7, 0xb8, 0xd6e313b71d9fL),
0xe902893a9d223c7ea7b8d6e313b71d9fL,
'urn:uuid:e902893a-9d22-3c7e-a7b8-d6e313b71d9f',
@ -102,6 +112,7 @@ class TestUUID(TestCase):
'{eb424026-6f54-4ef8-a4d0-bb658a1fc6cf}',
'eb4240266f544ef8a4d0bb658a1fc6cf',
'\xeb\x42\x40\x26\x6f\x54\x4e\xf8\xa4\xd0\xbb\x65\x8a\x1f\xc6\xcf',
'\x26\x40\x42\xeb\x54\x6f\xf8\x4e\xa4\xd0\xbb\x65\x8a\x1f\xc6\xcf',
(0xeb424026L, 0x6f54, 0x4ef8, 0xa4, 0xd0, 0xbb658a1fc6cfL),
0xeb4240266f544ef8a4d0bb658a1fc6cfL,
'urn:uuid:eb424026-6f54-4ef8-a4d0-bb658a1fc6cf',
@ -110,6 +121,7 @@ class TestUUID(TestCase):
'{f81d4fae-7dec-11d0-a765-00a0c91e6bf6}',
'f81d4fae7dec11d0a76500a0c91e6bf6',
'\xf8\x1d\x4f\xae\x7d\xec\x11\xd0\xa7\x65\x00\xa0\xc9\x1e\x6b\xf6',
'\xae\x4f\x1d\xf8\xec\x7d\xd0\x11\xa7\x65\x00\xa0\xc9\x1e\x6b\xf6',
(0xf81d4faeL, 0x7dec, 0x11d0, 0xa7, 0x65, 0x00a0c91e6bf6L),
0xf81d4fae7dec11d0a76500a0c91e6bf6L,
'urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
@ -118,6 +130,7 @@ class TestUUID(TestCase):
'{fffefdfc-fffe-fffe-fffe-fffefdfcfbfa}',
'fffefdfcfffefffefffefffefdfcfbfa',
'\xff\xfe\xfd\xfc\xff\xfe\xff\xfe\xff\xfe\xff\xfe\xfd\xfc\xfb\xfa',
'\xfc\xfd\xfe\xff\xfe\xff\xfe\xff\xff\xfe\xff\xfe\xfd\xfc\xfb\xfa',
(0xfffefdfcL, 0xfffe, 0xfffe, 0xff, 0xfe, 0xfffefdfcfbfaL),
0xfffefdfcfffefffefffefffefdfcfbfaL,
'urn:uuid:fffefdfc-fffe-fffe-fffe-fffefdfcfbfa',
@ -126,6 +139,7 @@ class TestUUID(TestCase):
'{ffffffff-ffff-ffff-ffff-ffffffffffff}',
'ffffffffffffffffffffffffffffffff',
'\xff'*16,
'\xff'*16,
(0xffffffffL, 0xffffL, 0xffffL, 0xff, 0xff, 0xffffffffffffL),
0xffffffffffffffffffffffffffffffffL,
'urn:uuid:ffffffff-ffff-ffff-ffff-ffffffffffff',
@ -134,12 +148,14 @@ class TestUUID(TestCase):
equivalents = []
# Construct each UUID in several different ways.
for u in [uuid.UUID(string), uuid.UUID(curly), uuid.UUID(hex),
uuid.UUID(bytes=bytes), uuid.UUID(fields=fields),
uuid.UUID(int=integer), uuid.UUID(urn)]:
uuid.UUID(bytes=bytes), uuid.UUID(bytes_le=bytes_le),
uuid.UUID(fields=fields), uuid.UUID(int=integer),
uuid.UUID(urn)]:
# Test all conversions and properties of the UUID object.
equal(str(u), string)
equal(int(u), integer)
equal(u.bytes, bytes)
equal(u.bytes_le, bytes_le)
equal(u.fields, fields)
equal(u.time_low, fields[0])
equal(u.time_mid, fields[1])
@ -189,6 +205,11 @@ class TestUUID(TestCase):
badvalue(lambda: uuid.UUID(bytes='\0'*15))
badvalue(lambda: uuid.UUID(bytes='\0'*17))
# Badly formed bytes_le.
badvalue(lambda: uuid.UUID(bytes_le='abc'))
badvalue(lambda: uuid.UUID(bytes_le='\0'*15))
badvalue(lambda: uuid.UUID(bytes_le='\0'*17))
# Badly formed fields.
badvalue(lambda: uuid.UUID(fields=(1,)))
badvalue(lambda: uuid.UUID(fields=(1, 2, 3, 4, 5)))
@ -221,51 +242,43 @@ class TestUUID(TestCase):
uuid.UUID(h)
uuid.UUID(hex=h)
uuid.UUID(bytes=b)
uuid.UUID(bytes_le=b)
uuid.UUID(fields=f)
uuid.UUID(int=i)
# Wrong number of arguments (positional).
badtype(lambda: uuid.UUID())
badtype(lambda: uuid.UUID(h, b))
badtype(lambda: uuid.UUID(h, b, f))
badtype(lambda: uuid.UUID(h, b, f, i))
badtype(lambda: uuid.UUID(h, b, b))
badtype(lambda: uuid.UUID(h, b, b, f))
badtype(lambda: uuid.UUID(h, b, b, f, i))
# Duplicate arguments (named).
badtype(lambda: uuid.UUID(hex=h, bytes=b))
badtype(lambda: uuid.UUID(hex=h, fields=f))
badtype(lambda: uuid.UUID(hex=h, int=i))
badtype(lambda: uuid.UUID(bytes=b, fields=f))
badtype(lambda: uuid.UUID(bytes=b, int=i))
badtype(lambda: uuid.UUID(fields=f, int=i))
badtype(lambda: uuid.UUID(hex=h, bytes=b, fields=f))
badtype(lambda: uuid.UUID(hex=h, bytes=b, int=i))
badtype(lambda: uuid.UUID(hex=h, fields=f, int=i))
badtype(lambda: uuid.UUID(bytes=b, int=i, fields=f))
badtype(lambda: uuid.UUID(hex=h, bytes=b, int=i, fields=f))
# Duplicate arguments (positional and named).
badtype(lambda: uuid.UUID(h, hex=h))
badtype(lambda: uuid.UUID(h, bytes=b))
badtype(lambda: uuid.UUID(h, fields=f))
badtype(lambda: uuid.UUID(h, int=i))
badtype(lambda: uuid.UUID(h, hex=h, bytes=b))
badtype(lambda: uuid.UUID(h, hex=h, fields=f))
badtype(lambda: uuid.UUID(h, hex=h, int=i))
badtype(lambda: uuid.UUID(h, bytes=b, fields=f))
badtype(lambda: uuid.UUID(h, bytes=b, int=i))
badtype(lambda: uuid.UUID(h, fields=f, int=i))
badtype(lambda: uuid.UUID(h, hex=h, bytes=b, fields=f))
badtype(lambda: uuid.UUID(h, hex=h, bytes=b, int=i))
badtype(lambda: uuid.UUID(h, hex=h, fields=f, int=i))
badtype(lambda: uuid.UUID(h, bytes=b, int=i, fields=f))
badtype(lambda: uuid.UUID(h, hex=h, bytes=b, int=i, fields=f))
# Duplicate arguments.
for hh in [[], [('hex', h)]]:
for bb in [[], [('bytes', b)]]:
for bble in [[], [('bytes_le', b)]]:
for ii in [[], [('int', i)]]:
for ff in [[], [('fields', f)]]:
args = dict(hh + bb + bble + ii + ff)
if len(args) != 0:
badtype(lambda: uuid.UUID(h, **args))
if len(args) != 1:
badtype(lambda: uuid.UUID(**args))
# Immutability.
u = uuid.UUID(h)
badtype(lambda: setattr(u, 'hex', h))
badtype(lambda: setattr(u, 'bytes', b))
badtype(lambda: setattr(u, 'bytes_le', b))
badtype(lambda: setattr(u, 'fields', f))
badtype(lambda: setattr(u, 'int', i))
badtype(lambda: setattr(u, 'time_low', 0))
badtype(lambda: setattr(u, 'time_mid', 0))
badtype(lambda: setattr(u, 'time_hi_version', 0))
badtype(lambda: setattr(u, 'time_hi_version', 0))
badtype(lambda: setattr(u, 'clock_seq_hi_variant', 0))
badtype(lambda: setattr(u, 'clock_seq_low', 0))
badtype(lambda: setattr(u, 'node', 0))
def check_node(self, node, source):
individual_group_bit = (node >> 40L) & 1
@ -356,11 +369,17 @@ class TestUUID(TestCase):
def test_uuid1(self):
equal = self.assertEqual
# Make sure uuid4() generates UUIDs that are actually version 1.
# Make sure uuid1() generates UUIDs that are actually version 1.
for u in [uuid.uuid1() for i in range(10)]:
equal(u.variant, uuid.RFC_4122)
equal(u.version, 1)
# Make sure the generated UUIDs are actually unique.
uuids = {}
for u in [uuid.uuid1() for i in range(1000)]:
uuids[u] = 1
equal(len(uuids.keys()), 1000)
# Make sure the supplied node ID appears in the UUID.
u = uuid.uuid1(0)
equal(u.node, 0)
@ -408,6 +427,12 @@ class TestUUID(TestCase):
equal(u.variant, uuid.RFC_4122)
equal(u.version, 4)
# Make sure the generated UUIDs are actually unique.
uuids = {}
for u in [uuid.uuid1() for i in range(1000)]:
uuids[u] = 1
equal(len(uuids.keys()), 1000)
def test_uuid5(self):
equal = self.assertEqual

View file

@ -45,8 +45,6 @@ Typical usage:
"""
__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-')
__version__ = '$Revision: 1.30 $'.split()[1]
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
'reserved for NCS compatibility', 'specified in RFC 4122',
@ -57,15 +55,21 @@ class UUID(object):
UUID objects are immutable, hashable, and usable as dictionary keys.
Converting a UUID to a string with str() yields something in the form
'12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts
four possible forms: a similar string of hexadecimal digits, or a
string of 16 raw bytes as an argument named 'bytes', or a tuple of
six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
48-bit values respectively) as an argument named 'fields', or a single
128-bit integer as an argument named 'int'.
five possible forms: a similar string of hexadecimal digits, or a tuple
of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
48-bit values respectively) as an argument named 'fields', or a string
of 16 bytes (with all the integer fields in big-endian order) as an
argument named 'bytes', or a string of 16 bytes (with the first three
fields in little-endian order) as an argument named 'bytes_le', or a
single 128-bit integer as an argument named 'int'.
UUIDs have these read-only attributes:
bytes the UUID as a 16-byte string
bytes the UUID as a 16-byte string (containing the six
integer fields in big-endian byte order)
bytes_le the UUID as a 16-byte string (with time_low, time_mid,
and time_hi_version in little-endian byte order)
fields a tuple of the six integer fields of the UUID,
which are also available as six individual attributes
@ -94,10 +98,11 @@ class UUID(object):
when the variant is RFC_4122)
"""
def __init__(self, hex=None, bytes=None, fields=None, int=None,
version=None):
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
int=None, version=None):
r"""Create a UUID from either a string of 32 hexadecimal digits,
a string of 16 bytes as the 'bytes' argument, a tuple of six
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
integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
the 'fields' argument, or a single 128-bit integer as the 'int'
@ -109,23 +114,31 @@ class UUID(object):
UUID('12345678123456781234567812345678')
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
UUID(bytes='\x12\x34\x56\x78'*4)
UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
'\x12\x34\x56\x78\x12\x34\x56\x78')
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
UUID(int=0x12345678123456781234567812345678)
Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given.
The 'version' argument is optional; if given, the resulting UUID
will have its variant and version number set according to RFC 4122,
overriding bits in the given 'hex', 'bytes', 'fields', or 'int'.
Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must
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'.
"""
if [hex, bytes, fields, int].count(None) != 3:
raise TypeError('need just one of hex, bytes, fields, or int')
if [hex, bytes, bytes_le, fields, int].count(None) != 4:
raise TypeError('need one of hex, bytes, bytes_le, fields, or int')
if hex is not None:
hex = hex.replace('urn:', '').replace('uuid:', '')
hex = hex.strip('{}').replace('-', '')
if len(hex) != 32:
raise ValueError('badly formed hexadecimal UUID string')
int = long(hex, 16)
if bytes_le is not None:
if len(bytes_le) != 16:
raise ValueError('bytes_le is not a 16-char string')
bytes = (bytes_le[3] + bytes_le[2] + bytes_le[1] + bytes_le[0] +
bytes_le[5] + bytes_le[4] + bytes_le[7] + bytes_le[6] +
bytes_le[8:])
if bytes is not None:
if len(bytes) != 16:
raise ValueError('bytes is not a 16-char string')
@ -194,6 +207,13 @@ class UUID(object):
bytes = property(get_bytes)
def get_bytes_le(self):
bytes = self.bytes
return (bytes[3] + bytes[2] + bytes[1] + bytes[0] +
bytes[5] + bytes[4] + bytes[7] + bytes[6] + bytes[8:])
bytes_le = property(get_bytes_le)
def get_fields(self):
return (self.time_low, self.time_mid, self.time_hi_version,
self.clock_seq_hi_variant, self.clock_seq_low, self.node)
@ -448,6 +468,8 @@ def getnode():
if _node is not None:
return _node
_last_timestamp = None
def uuid1(node=None, clock_seq=None):
"""Generate a UUID from a host ID, sequence number, and the current time.
If 'node' is not given, getnode() is used to obtain the hardware
@ -460,11 +482,15 @@ def uuid1(node=None, clock_seq=None):
_uuid_generate_time(_buffer)
return UUID(bytes=_buffer.raw)
global _last_timestamp
import time
nanoseconds = int(time.time() * 1e9)
# 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 = int(nanoseconds/100) + 0x01b21dd213814000L
if timestamp == _last_timestamp:
timestamp += 1
_last_timestamp = timestamp
if clock_seq is None:
import random
clock_seq = random.randrange(1<<14L) # instead of stable storage