234 lines
9 KiB
Python
234 lines
9 KiB
Python
"""UUID (universally unique identifiers) as specified in RFC 4122.
|
|
|
|
This module provides the UUID class and the functions uuid1(), uuid3(),
|
|
uuid4(), uuid5() for generating version 1, 3, 4, and 5 UUIDs respectively.
|
|
|
|
This module works with Python 2.3 or higher."""
|
|
|
|
__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
|
|
__date__ = '$Date: 2005/11/30 11:51:58 $'.split()[1].replace('/', '-')
|
|
__version__ = '$Revision: 1.10 $'
|
|
|
|
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
|
|
'reserved for NCS compatibility', 'specified in RFC 4122',
|
|
'reserved for Microsoft compatibility', 'reserved for future definition']
|
|
|
|
class UUID(object):
|
|
"""Instances of the UUID class represent UUIDs as specified in RFC 4122.
|
|
Converting a UUID to a string using str() produces a string in the form
|
|
"{12345678-1234-1234-1234-123456789abc}". The UUID constructor accepts
|
|
a similar string (braces and hyphens optional), or six integer arguments
|
|
(with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and 48-bit values
|
|
respectively). UUID objects have the following attributes:
|
|
|
|
bytes gets or sets the UUID as a 16-byte string
|
|
|
|
urn gets the UUID as a URN as specified in RFC 4122
|
|
|
|
variant gets or sets the UUID variant as one of the constants
|
|
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE
|
|
|
|
version gets or sets the UUID version number (1 through 5)
|
|
"""
|
|
|
|
def __init__(self, *args):
|
|
"""Create a UUID either from a string representation in hexadecimal
|
|
or from six integers (32-bit time_low, 16-bit time_mid, 16-bit
|
|
time_hi_ver, 8-bit clock_hi_res, 8-bit clock_low, 48-bit node)."""
|
|
if len(args) == 1:
|
|
digits = args[0].replace('urn:', '').replace('uuid:', '')
|
|
digits = digits.replace('{', '').replace('}', '').replace('-', '')
|
|
assert len(digits) == 32, ValueError('badly formed UUID string')
|
|
time_low = int(digits[:8], 16)
|
|
time_mid = int(digits[8:12], 16)
|
|
time_hi_ver = int(digits[12:16], 16)
|
|
clock_hi_res = int(digits[16:18], 16)
|
|
clock_low = int(digits[18:20], 16)
|
|
node = int(digits[20:32], 16)
|
|
else:
|
|
(time_low, time_mid, time_hi_ver,
|
|
clock_hi_res, clock_low, node) = args
|
|
assert 0 <= time_low < 0x100000000, ValueError('time_low out of range')
|
|
assert 0 <= time_mid < 1<<16, ValueError('time_mid out of range')
|
|
assert 0 <= time_hi_ver < 1<<16, ValueError('time_hi_ver out of range')
|
|
assert 0 <= clock_hi_res < 1<<8, ValueError('clock_hi_res out of range')
|
|
assert 0 <= clock_low < 1<<8, ValueError('clock_low out of range')
|
|
assert 0 <= node < 0x1000000000000, ValueError('node out of range')
|
|
self.time_low = time_low
|
|
self.time_mid = time_mid
|
|
self.time_hi_ver = time_hi_ver
|
|
self.clock_hi_res = clock_hi_res
|
|
self.clock_low = clock_low
|
|
self.node = node
|
|
|
|
def __cmp__(self, other):
|
|
return cmp(self.bytes, getattr(other, 'bytes', other))
|
|
|
|
def __str__(self):
|
|
return '{%08x-%04x-%04x-%02x%02x-%012x}' % (
|
|
self.time_low, self.time_mid, self.time_hi_ver,
|
|
self.clock_hi_res, self.clock_low, self.node)
|
|
|
|
def __repr__(self):
|
|
return 'UUID(%r)' % str(self)
|
|
|
|
def get_bytes(self):
|
|
def byte(n):
|
|
return chr(n & 0xff)
|
|
|
|
return (byte(self.time_low >> 24) + byte(self.time_low >> 16) +
|
|
byte(self.time_low >> 8) + byte(self.time_low) +
|
|
byte(self.time_mid >> 8) + byte(self.time_mid) +
|
|
byte(self.time_hi_ver >> 8) + byte(self.time_hi_ver) +
|
|
byte(self.clock_hi_res) + byte(self.clock_low) +
|
|
byte(self.node >> 40) + byte(self.node >> 32) +
|
|
byte(self.node >> 24) + byte(self.node >> 16) +
|
|
byte(self.node >> 8) + byte(self.node))
|
|
|
|
def set_bytes(self, bytes):
|
|
values = map(ord, bytes)
|
|
self.time_low = ((values[0] << 24) + (values[1] << 16) +
|
|
(values[2] << 8) + values[3])
|
|
self.time_mid = (values[4] << 8) + values[5]
|
|
self.time_hi_ver = (values[6] << 8) + values[7]
|
|
self.clock_hi_res = values[8]
|
|
self.clock_low = values[9]
|
|
self.node = ((values[10] << 40) + (values[11] << 32) +
|
|
(values[12] << 24) + (values[13] << 16) +
|
|
(values[14] << 8) + values[15])
|
|
|
|
bytes = property(get_bytes, set_bytes)
|
|
|
|
def get_urn(self):
|
|
return 'urn:uuid:%08x-%04x-%04x-%02x%02x-%012x' % (
|
|
self.time_low, self.time_mid, self.time_hi_ver,
|
|
self.clock_hi_res, self.clock_low, self.node)
|
|
|
|
urn = property(get_urn)
|
|
|
|
def get_variant(self):
|
|
if not self.clock_hi_res & 0x80:
|
|
return RESERVED_NCS
|
|
elif not self.clock_hi_res & 0x40:
|
|
return RFC_4122
|
|
elif not self.clock_hi_res & 0x20:
|
|
return RESERVED_MICROSOFT
|
|
else:
|
|
return RESERVED_FUTURE
|
|
|
|
def set_variant(self, variant):
|
|
if variant == RESERVED_NCS:
|
|
self.clock_hi_res &= 0x7f
|
|
elif variant == RFC_4122:
|
|
self.clock_hi_res &= 0x3f
|
|
self.clock_hi_res |= 0x80
|
|
elif variant == RESERVED_MICROSOFT:
|
|
self.clock_hi_res &= 0x1f
|
|
self.clock_hi_res |= 0xc0
|
|
elif variant == RESERVED_FUTURE:
|
|
self.clock_hi_res &= 0x1f
|
|
self.clock_hi_res |= 0xe0
|
|
else:
|
|
raise ValueError('illegal variant identifier')
|
|
|
|
variant = property(get_variant, set_variant)
|
|
|
|
def get_version(self):
|
|
return self.time_hi_ver >> 12
|
|
|
|
def set_version(self, version):
|
|
assert 1 <= version <= 5, ValueError('illegal version number')
|
|
self.time_hi_ver &= 0x0fff
|
|
self.time_hi_ver |= (version << 12)
|
|
|
|
version = property(get_version, set_version)
|
|
|
|
def unixgetaddr(program):
|
|
"""Get the hardware address on a Unix machine."""
|
|
from os import popen
|
|
for line in popen(program):
|
|
words = line.lower().split()
|
|
if 'hwaddr' in words:
|
|
addr = words[words.index('hwaddr') + 1]
|
|
return int(addr.replace(':', ''), 16)
|
|
if 'ether' in words:
|
|
addr = words[words.index('ether') + 1]
|
|
return int(addr.replace(':', ''), 16)
|
|
|
|
def wingetaddr(program):
|
|
"""Get the hardware address on a Windows machine."""
|
|
from os import popen
|
|
for line in popen(program + ' /all'):
|
|
if line.strip().lower().startswith('physical address'):
|
|
addr = line.split(':')[-1].strip()
|
|
return int(addr.replace('-', ''), 16)
|
|
|
|
def getaddr():
|
|
"""Get the hardware address as a 48-bit integer."""
|
|
from os.path import join, isfile
|
|
for dir in ['/sbin', '/usr/sbin', r'c:\windows',
|
|
r'c:\windows\system', r'c:\windows\system32']:
|
|
if isfile(join(dir, 'ifconfig')):
|
|
return unixgetaddr(join(dir, 'ifconfig'))
|
|
if isfile(join(dir, 'ipconfig.exe')):
|
|
return wingetaddr(join(dir, 'ipconfig.exe'))
|
|
|
|
def uuid1():
|
|
"""Generate a UUID based on the time and hardware address."""
|
|
from time import time
|
|
from random import randrange
|
|
nanoseconds = int(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) + 0x01b21dd213814000
|
|
clock = randrange(1<<16) # don't use stable storage
|
|
time_low = timestamp & (0x100000000 - 1)
|
|
time_mid = (timestamp >> 32) & 0xffff
|
|
time_hi_ver = (timestamp >> 48) & 0x0fff
|
|
clock_low = clock & 0xff
|
|
clock_hi_res = (clock >> 8) & 0x3f
|
|
node = getaddr()
|
|
uuid = UUID(time_low, time_mid, time_hi_ver, clock_low, clock_hi_res, node)
|
|
uuid.variant = RFC_4122
|
|
uuid.version = 1
|
|
return uuid
|
|
|
|
def uuid3(namespace, name):
|
|
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
|
|
from md5 import md5
|
|
uuid = UUID(0, 0, 0, 0, 0, 0)
|
|
uuid.bytes = md5(namespace.bytes + name).digest()[:16]
|
|
uuid.variant = RFC_4122
|
|
uuid.version = 3
|
|
return uuid
|
|
|
|
def uuid4():
|
|
"""Generate a random UUID."""
|
|
try:
|
|
from os import urandom
|
|
except:
|
|
from random import randrange
|
|
uuid = UUID(randrange(1<<32), randrange(1<<16), randrange(1<<16),
|
|
randrange(1<<8), randrange(1<<8), randrange(1<<48))
|
|
else:
|
|
uuid = UUID(0, 0, 0, 0, 0, 0)
|
|
uuid.bytes = urandom(16)
|
|
uuid.variant = RFC_4122
|
|
uuid.version = 4
|
|
return uuid
|
|
|
|
def uuid5(namespace, name):
|
|
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
|
|
from sha import sha
|
|
uuid = UUID(0, 0, 0, 0, 0, 0)
|
|
uuid.bytes = sha(namespace.bytes + name).digest()[:16]
|
|
uuid.variant = RFC_4122
|
|
uuid.version = 5
|
|
return uuid
|
|
|
|
NAMESPACE_DNS = UUID('{6ba7b810-9dad-11d1-80b4-00c04fd430c8}')
|
|
NAMESPACE_URL = UUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}')
|
|
NAMESPACE_OID = UUID('{6ba7b812-9dad-11d1-80b4-00c04fd430c8}')
|
|
NAMESPACE_X500 = UUID('{6ba7b814-9dad-11d1-80b4-00c04fd430c8}')
|
|
|
|
|