0001"""UUID (universally unique identifiers) as specified in RFC 4122.
0002
0003This module provides the UUID class and the functions uuid1(), uuid3(),
0004uuid4(), uuid5() for generating version 1, 3, 4, and 5 UUIDs respectively.
0005
0006This module works with Python 2.3 or higher."""
0007
0008__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
0009__date__ = '$Date: 2005/11/30 11:51:58 $'.split()[1].replace('/', '-')
0010__version__ = '$Revision: 1.10 $'
0011
0012RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
0013 'reserved for NCS compatibility', 'specified in RFC 4122',
0014 'reserved for Microsoft compatibility', 'reserved for future definition']
0015
0016class UUID(object):
0017 """Instances of the UUID class represent UUIDs as specified in RFC 4122.
0018 Converting a UUID to a string using str() produces a string in the form
0019 "{12345678-1234-1234-1234-123456789abc}". The UUID constructor accepts
0020 a similar string (braces and hyphens optional), or six integer arguments
0021 (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and 48-bit values
0022 respectively). UUID objects have the following attributes:
0023
0024 bytes gets or sets the UUID as a 16-byte string
0025
0026 urn gets the UUID as a URN as specified in RFC 4122
0027
0028 variant gets or sets the UUID variant as one of the constants
0029 RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE
0030
0031 version gets or sets the UUID version number (1 through 5)
0032 """
0033
0034 def __init__(self, *args):
0035 """Create a UUID either from a string representation in hexadecimal
0036 or from six integers (32-bit time_low, 16-bit time_mid, 16-bit
0037 time_hi_ver, 8-bit clock_hi_res, 8-bit clock_low, 48-bit node)."""
0038 if len(args) == 1:
0039 digits = args[0].replace('urn:', '').replace('uuid:', '')
0040 digits = digits.replace('{', '').replace('}', '').replace('-', '')
0041 assert len(digits) == 32, ValueError('badly formed UUID string')
0042 time_low = int(digits[:8], 16)
0043 time_mid = int(digits[8:12], 16)
0044 time_hi_ver = int(digits[12:16], 16)
0045 clock_hi_res = int(digits[16:18], 16)
0046 clock_low = int(digits[18:20], 16)
0047 node = int(digits[20:32], 16)
0048 else:
0049 (time_low, time_mid, time_hi_ver,
0050 clock_hi_res, clock_low, node) = args
0051 assert 0 <= time_low < 0x100000000, ValueError('time_low out of range')
0052 assert 0 <= time_mid < 1<<16, ValueError('time_mid out of range')
0053 assert 0 <= time_hi_ver < 1<<16, ValueError('time_hi_ver out of range')
0054 assert 0 <= clock_hi_res < 1<<8, ValueError('clock_hi_res out of range')
0055 assert 0 <= clock_low < 1<<8, ValueError('clock_low out of range')
0056 assert 0 <= node < 0x1000000000000, ValueError('node out of range')
0057 self.time_low = time_low
0058 self.time_mid = time_mid
0059 self.time_hi_ver = time_hi_ver
0060 self.clock_hi_res = clock_hi_res
0061 self.clock_low = clock_low
0062 self.node = node
0063
0064 def __cmp__(self, other):
0065 return cmp(self.bytes, getattr(other, 'bytes', other))
0066
0067 def __str__(self):
0068 return '{%08x-%04x-%04x-%02x%02x-%012x}' % (
0069 self.time_low, self.time_mid, self.time_hi_ver,
0070 self.clock_hi_res, self.clock_low, self.node)
0071
0072 def __repr__(self):
0073 return 'UUID(%r)' % str(self)
0074
0075 def get_bytes(self):
0076 def byte(n):
0077 return chr(n & 0xff)
0078
0079 return (byte(self.time_low >> 24) + byte(self.time_low >> 16) +
0080 byte(self.time_low >> 8) + byte(self.time_low) +
0081 byte(self.time_mid >> 8) + byte(self.time_mid) +
0082 byte(self.time_hi_ver >> 8) + byte(self.time_hi_ver) +
0083 byte(self.clock_hi_res) + byte(self.clock_low) +
0084 byte(self.node >> 40) + byte(self.node >> 32) +
0085 byte(self.node >> 24) + byte(self.node >> 16) +
0086 byte(self.node >> 8) + byte(self.node))
0087
0088 def set_bytes(self, bytes):
0089 values = map(ord, bytes)
0090 self.time_low = ((values[0] << 24) + (values[1] << 16) +
0091 (values[2] << 8) + values[3])
0092 self.time_mid = (values[4] << 8) + values[5]
0093 self.time_hi_ver = (values[6] << 8) + values[7]
0094 self.clock_hi_res = values[8]
0095 self.clock_low = values[9]
0096 self.node = ((values[10] << 40) + (values[11] << 32) +
0097 (values[12] << 24) + (values[13] << 16) +
0098 (values[14] << 8) + values[15])
0099
0100 bytes = property(get_bytes, set_bytes)
0101
0102 def get_urn(self):
0103 return 'urn:uuid:%08x-%04x-%04x-%02x%02x-%012x' % (
0104 self.time_low, self.time_mid, self.time_hi_ver,
0105 self.clock_hi_res, self.clock_low, self.node)
0106
0107 urn = property(get_urn)
0108
0109 def get_variant(self):
0110 if not self.clock_hi_res & 0x80:
0111 return RESERVED_NCS
0112 elif not self.clock_hi_res & 0x40:
0113 return RFC_4122
0114 elif not self.clock_hi_res & 0x20:
0115 return RESERVED_MICROSOFT
0116 else:
0117 return RESERVED_FUTURE
0118
0119 def set_variant(self, variant):
0120 if variant == RESERVED_NCS:
0121 self.clock_hi_res &= 0x7f
0122 elif variant == RFC_4122:
0123 self.clock_hi_res &= 0x3f
0124 self.clock_hi_res |= 0x80
0125 elif variant == RESERVED_MICROSOFT:
0126 self.clock_hi_res &= 0x1f
0127 self.clock_hi_res |= 0xc0
0128 elif variant == RESERVED_FUTURE:
0129 self.clock_hi_res &= 0x1f
0130 self.clock_hi_res |= 0xe0
0131 else:
0132 raise ValueError('illegal variant identifier')
0133
0134 variant = property(get_variant, set_variant)
0135
0136 def get_version(self):
0137 return self.time_hi_ver >> 12
0138
0139 def set_version(self, version):
0140 assert 1 <= version <= 5, ValueError('illegal version number')
0141 self.time_hi_ver &= 0x0fff
0142 self.time_hi_ver |= (version << 12)
0143
0144 version = property(get_version, set_version)
0145
0146def unixgetaddr(program):
0147 """Get the hardware address on a Unix machine."""
0148 from os import popen
0149 for line in popen(program):
0150 words = line.lower().split()
0151 if 'hwaddr' in words:
0152 addr = words[words.index('hwaddr') + 1]
0153 return int(addr.replace(':', ''), 16)
0154 if 'ether' in words:
0155 addr = words[words.index('ether') + 1]
0156 return int(addr.replace(':', ''), 16)
0157
0158def wingetaddr(program):
0159 """Get the hardware address on a Windows machine."""
0160 from os import popen
0161 for line in popen(program + ' /all'):
0162 if line.strip().lower().startswith('physical address'):
0163 addr = line.split(':')[-1].strip()
0164 return int(addr.replace('-', ''), 16)
0165
0166def getaddr():
0167 """Get the hardware address as a 48-bit integer."""
0168 from os.path import join, isfile
0169 for dir in ['/sbin', '/usr/sbin', r'c:\windows',
0170 r'c:\windows\system', r'c:\windows\system32']:
0171 if isfile(join(dir, 'ifconfig')):
0172 return unixgetaddr(join(dir, 'ifconfig'))
0173 if isfile(join(dir, 'ipconfig.exe')):
0174 return wingetaddr(join(dir, 'ipconfig.exe'))
0175
0176def uuid1():
0177 """Generate a UUID based on the time and hardware address."""
0178 from time import time
0179 from random import randrange
0180 nanoseconds = int(time() * 1e9)
0181
0182
0183 timestamp = int(nanoseconds/100) + 0x01b21dd213814000
0184 clock = randrange(1<<16)
0185 time_low = timestamp & (0x100000000 - 1)
0186 time_mid = (timestamp >> 32) & 0xffff
0187 time_hi_ver = (timestamp >> 48) & 0x0fff
0188 clock_low = clock & 0xff
0189 clock_hi_res = (clock >> 8) & 0x3f
0190 node = getaddr()
0191 uuid = UUID(time_low, time_mid, time_hi_ver, clock_low, clock_hi_res, node)
0192 uuid.variant = RFC_4122
0193 uuid.version = 1
0194 return uuid
0195
0196def uuid3(namespace, name):
0197 """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
0198 from md5 import md5
0199 uuid = UUID(0, 0, 0, 0, 0, 0)
0200 uuid.bytes = md5(namespace.bytes + name).digest()[:16]
0201 uuid.variant = RFC_4122
0202 uuid.version = 3
0203 return uuid
0204
0205def uuid4():
0206 """Generate a random UUID."""
0207 try:
0208 from os import urandom
0209 except:
0210 from random import randrange
0211 uuid = UUID(randrange(1<<32), randrange(1<<16), randrange(1<<16),
0212 randrange(1<<8), randrange(1<<8), randrange(1<<48))
0213 else:
0214 uuid = UUID(0, 0, 0, 0, 0, 0)
0215 uuid.bytes = urandom(16)
0216 uuid.variant = RFC_4122
0217 uuid.version = 4
0218 return uuid
0219
0220def uuid5(namespace, name):
0221 """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
0222 from sha import sha
0223 uuid = UUID(0, 0, 0, 0, 0, 0)
0224 uuid.bytes = sha(namespace.bytes + name).digest()[:16]
0225 uuid.variant = RFC_4122
0226 uuid.version = 5
0227 return uuid
0228
0229NAMESPACE_DNS = UUID('{6ba7b810-9dad-11d1-80b4-00c04fd430c8}')
0230NAMESPACE_URL = UUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}')
0231NAMESPACE_OID = UUID('{6ba7b812-9dad-11d1-80b4-00c04fd430c8}')
0232NAMESPACE_X500 = UUID('{6ba7b814-9dad-11d1-80b4-00c04fd430c8}')