00001
00002
00003 r"""UUID objects (universally unique identifiers) according to RFC 4122.
00004
00005 This module provides immutable UUID objects (class UUID) and the functions
00006 uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
00007 UUIDs as specified in RFC 4122.
00008
00009 If all you want is a unique ID, you should probably call uuid1() or uuid4().
00010 Note that uuid1() may compromise privacy since it creates a UUID containing
00011 the computer's network address. uuid4() creates a random UUID.
00012
00013 Typical usage:
00014
00015 >>> import uuid
00016
00017 # make a UUID based on the host ID and current time
00018 >>> uuid.uuid1()
00019 UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
00020
00021 # make a UUID using an MD5 hash of a namespace UUID and a name
00022 >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
00023 UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
00024
00025 # make a random UUID
00026 >>> uuid.uuid4()
00027 UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
00028
00029 # make a UUID using a SHA-1 hash of a namespace UUID and a name
00030 >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
00031 UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
00032
00033 # make a UUID from a string of hex digits (braces and hyphens ignored)
00034 >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
00035
00036 # convert a UUID to a string of hex digits in standard form
00037 >>> str(x)
00038 '00010203-0405-0607-0809-0a0b0c0d0e0f'
00039
00040 # get the raw 16 bytes of the UUID
00041 >>> x.bytes
00042 '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
00043
00044 # make a UUID from a 16-byte string
00045 >>> uuid.UUID(bytes=x.bytes)
00046 UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
00047
00048 This module works with Python 2.3 or higher."""
00049
00050 __author__ = 'Ka-Ping Yee <ping@zesty.ca>'
00051 __date__ = '$Date: 2007-07-20 15:38:13 $'.split()[1].replace('/', '-')
00052 __version__ = '$Revision: 1.1.2.1 $'.split()[1]
00053
00054 RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
00055 'reserved for NCS compatibility', 'specified in RFC 4122',
00056 'reserved for Microsoft compatibility', 'reserved for future definition']
00057
00058 class UUID(object):
00059 """Instances of the UUID class represent UUIDs as specified in RFC 4122.
00060 UUID objects are immutable, hashable, and usable as dictionary keys.
00061 Converting a UUID to a string with str() yields something in the form
00062 '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts
00063 four possible forms: a similar string of hexadecimal digits, or a
00064 string of 16 raw bytes as an argument named 'bytes', or a tuple of
00065 six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
00066 48-bit values respectively) as an argument named 'fields', or a single
00067 128-bit integer as an argument named 'int'.
00068
00069 UUIDs have these read-only attributes:
00070
00071 bytes the UUID as a 16-byte string
00072
00073 fields a tuple of the six integer fields of the UUID,
00074 which are also available as six individual attributes
00075 and two derived attributes:
00076
00077 time_low the first 32 bits of the UUID
00078 time_mid the next 16 bits of the UUID
00079 time_hi_version the next 16 bits of the UUID
00080 clock_seq_hi_variant the next 8 bits of the UUID
00081 clock_seq_low the next 8 bits of the UUID
00082 node the last 48 bits of the UUID
00083
00084 time the 60-bit timestamp
00085 clock_seq the 14-bit sequence number
00086
00087 hex the UUID as a 32-character hexadecimal string
00088
00089 int the UUID as a 128-bit integer
00090
00091 urn the UUID as a URN as specified in RFC 4122
00092
00093 variant the UUID variant (one of the constants RESERVED_NCS,
00094 RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
00095
00096 version the UUID version number (1 through 5, meaningful only
00097 when the variant is RFC_4122)
00098 """
00099
00100 def __init__(self, hex=None, bytes=None, fields=None, int=None,
00101 version=None):
00102 r"""Create a UUID from either a string of 32 hexadecimal digits,
00103 a string of 16 bytes as the 'bytes' argument, a tuple of six
00104 integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
00105 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
00106 the 'fields' argument, or a single 128-bit integer as the 'int'
00107 argument. When a string of hex digits is given, curly braces,
00108 hyphens, and a URN prefix are all optional. For example, these
00109 expressions all yield the same UUID:
00110
00111 UUID('{12345678-1234-5678-1234-567812345678}')
00112 UUID('12345678123456781234567812345678')
00113 UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
00114 UUID(bytes='\x12\x34\x56\x78'*4)
00115 UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
00116 UUID(int=0x12345678123456781234567812345678)
00117
00118 Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given.
00119 The 'version' argument is optional; if given, the resulting UUID
00120 will have its variant and version number set according to RFC 4122,
00121 overriding bits in the given 'hex', 'bytes', 'fields', or 'int'.
00122 """
00123
00124 if [hex, bytes, fields, int].count(None) != 3:
00125 raise TypeError('need just one of hex, bytes, fields, or int')
00126 if hex is not None:
00127 hex = hex.replace('urn:', '').replace('uuid:', '')
00128 hex = hex.strip('{}').replace('-', '')
00129 if len(hex) != 32:
00130 raise ValueError('badly formed hexadecimal UUID string')
00131 int = long(hex, 16)
00132 if bytes is not None:
00133 if len(bytes) != 16:
00134 raise ValueError('bytes is not a 16-char string')
00135 int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
00136 if fields is not None:
00137 if len(fields) != 6:
00138 raise ValueError('fields is not a 6-tuple')
00139 (time_low, time_mid, time_hi_version,
00140 clock_seq_hi_variant, clock_seq_low, node) = fields
00141 if not 0 <= time_low < 1<<32L:
00142 raise ValueError('field 1 out of range (need a 32-bit value)')
00143 if not 0 <= time_mid < 1<<16L:
00144 raise ValueError('field 2 out of range (need a 16-bit value)')
00145 if not 0 <= time_hi_version < 1<<16L:
00146 raise ValueError('field 3 out of range (need a 16-bit value)')
00147 if not 0 <= clock_seq_hi_variant < 1<<8L:
00148 raise ValueError('field 4 out of range (need an 8-bit value)')
00149 if not 0 <= clock_seq_low < 1<<8L:
00150 raise ValueError('field 5 out of range (need an 8-bit value)')
00151 if not 0 <= node < 1<<48L:
00152 raise ValueError('field 6 out of range (need a 48-bit value)')
00153 clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low
00154 int = ((time_low << 96L) | (time_mid << 80L) |
00155 (time_hi_version << 64L) | (clock_seq << 48L) | node)
00156 if int is not None:
00157 if not 0 <= int < 1<<128L:
00158 raise ValueError('int is out of range (need a 128-bit value)')
00159 if version is not None:
00160 if not 1 <= version <= 5:
00161 raise ValueError('illegal version number')
00162
00163 int &= ~(0xc000 << 48L)
00164 int |= 0x8000 << 48L
00165
00166 int &= ~(0xf000 << 64L)
00167 int |= version << 76L
00168 self.__dict__['int'] = int
00169
00170 def __cmp__(self, other):
00171 if isinstance(other, UUID):
00172 return cmp(self.int, other.int)
00173 return NotImplemented
00174
00175 def __hash__(self):
00176 return hash(self.int)
00177
00178 def __int__(self):
00179 return self.int
00180
00181 def __repr__(self):
00182 return 'UUID(%r)' % str(self)
00183
00184 def __setattr__(self, name, value):
00185 raise TypeError('UUID objects are immutable')
00186
00187 def __str__(self):
00188 hex = '%032x' % self.int
00189 return '%s-%s-%s-%s-%s' % (
00190 hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
00191
00192 def get_bytes(self):
00193 bytes = ''
00194 for shift in range(0, 128, 8):
00195 bytes = chr((self.int >> shift) & 0xff) + bytes
00196 return bytes
00197
00198 bytes = property(get_bytes)
00199
00200 def get_fields(self):
00201 return (self.time_low, self.time_mid, self.time_hi_version,
00202 self.clock_seq_hi_variant, self.clock_seq_low, self.node)
00203
00204 fields = property(get_fields)
00205
00206 def get_time_low(self):
00207 return self.int >> 96L
00208
00209 time_low = property(get_time_low)
00210
00211 def get_time_mid(self):
00212 return (self.int >> 80L) & 0xffff
00213
00214 time_mid = property(get_time_mid)
00215
00216 def get_time_hi_version(self):
00217 return (self.int >> 64L) & 0xffff
00218
00219 time_hi_version = property(get_time_hi_version)
00220
00221 def get_clock_seq_hi_variant(self):
00222 return (self.int >> 56L) & 0xff
00223
00224 clock_seq_hi_variant = property(get_clock_seq_hi_variant)
00225
00226 def get_clock_seq_low(self):
00227 return (self.int >> 48L) & 0xff
00228
00229 clock_seq_low = property(get_clock_seq_low)
00230
00231 def get_time(self):
00232 return (((self.time_hi_version & 0x0fffL) << 48L) |
00233 (self.time_mid << 32L) | self.time_low)
00234
00235 time = property(get_time)
00236
00237 def get_clock_seq(self):
00238 return (((self.clock_seq_hi_variant & 0x3fL) << 8L) |
00239 self.clock_seq_low)
00240
00241 clock_seq = property(get_clock_seq)
00242
00243 def get_node(self):
00244 return self.int & 0xffffffffffff
00245
00246 node = property(get_node)
00247
00248 def get_hex(self):
00249 return '%032x' % self.int
00250
00251 hex = property(get_hex)
00252
00253 def get_urn(self):
00254 return 'urn:uuid:' + str(self)
00255
00256 urn = property(get_urn)
00257
00258 def get_variant(self):
00259 if not self.int & (0x8000 << 48L):
00260 return RESERVED_NCS
00261 elif not self.int & (0x4000 << 48L):
00262 return RFC_4122
00263 elif not self.int & (0x2000 << 48L):
00264 return RESERVED_MICROSOFT
00265 else:
00266 return RESERVED_FUTURE
00267
00268 variant = property(get_variant)
00269
00270 def get_version(self):
00271
00272 if self.variant == RFC_4122:
00273 return int((self.int >> 76L) & 0xf)
00274
00275 version = property(get_version)
00276
00277 def _ifconfig_getnode():
00278 """Get the hardware address on Unix by running ifconfig."""
00279 import os
00280 for dir in ['', '/sbin/', '/usr/sbin']:
00281 try:
00282 pipe = os.popen(os.path.join(dir, 'ifconfig'))
00283 except IOError:
00284 continue
00285 for line in pipe:
00286 words = line.lower().split()
00287 for i in range(len(words)):
00288 if words[i] in ['hwaddr', 'ether']:
00289 return int(words[i + 1].replace(':', ''), 16)
00290
00291 def _ipconfig_getnode():
00292 """Get the hardware address on Windows by running ipconfig.exe."""
00293 import os, re
00294 dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
00295 try:
00296 import ctypes
00297 buffer = ctypes.create_string_buffer(300)
00298 ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
00299 dirs.insert(0, buffer.value.decode('mbcs'))
00300 except:
00301 pass
00302 for dir in dirs:
00303 try:
00304 pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all')
00305 except IOError:
00306 continue
00307 for line in pipe:
00308 value = line.split(':')[-1].strip().lower()
00309 if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
00310 return int(value.replace('-', ''), 16)
00311
00312 def _netbios_getnode():
00313 """Get the hardware address on Windows using NetBIOS calls.
00314 See http://support.microsoft.com/kb/118623 for details."""
00315 import win32wnet, netbios
00316 ncb = netbios.NCB()
00317 ncb.Command = netbios.NCBENUM
00318 ncb.Buffer = adapters = netbios.LANA_ENUM()
00319 adapters._pack()
00320 if win32wnet.Netbios(ncb) != 0:
00321 return
00322 adapters._unpack()
00323 for i in range(adapters.length):
00324 ncb.Reset()
00325 ncb.Command = netbios.NCBRESET
00326 ncb.Lana_num = ord(adapters.lana[i])
00327 if win32wnet.Netbios(ncb) != 0:
00328 continue
00329 ncb.Reset()
00330 ncb.Command = netbios.NCBASTAT
00331 ncb.Lana_num = ord(adapters.lana[i])
00332 ncb.Callname = '*'.ljust(16)
00333 ncb.Buffer = status = netbios.ADAPTER_STATUS()
00334 if win32wnet.Netbios(ncb) != 0:
00335 continue
00336 status._unpack()
00337 bytes = map(ord, status.adapter_address)
00338 return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) +
00339 (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5])
00340
00341
00342
00343
00344 _uuid_generate_random = _uuid_generate_time = _UuidCreate = None
00345 try:
00346 import ctypes, ctypes.util
00347 _buffer = ctypes.create_string_buffer(16)
00348
00349
00350
00351 for libname in ['uuid', 'c']:
00352 try:
00353 lib = ctypes.CDLL(ctypes.util.find_library(libname))
00354 except:
00355 continue
00356 if hasattr(lib, 'uuid_generate_random'):
00357 _uuid_generate_random = lib.uuid_generate_random
00358 if hasattr(lib, 'uuid_generate_time'):
00359 _uuid_generate_time = lib.uuid_generate_time
00360
00361
00362
00363
00364
00365 try:
00366 lib = ctypes.windll.rpcrt4
00367 except:
00368 lib = None
00369 _UuidCreate = getattr(lib, 'UuidCreateSequential',
00370 getattr(lib, 'UuidCreate', None))
00371 except:
00372 pass
00373
00374 def _unixdll_getnode():
00375 """Get the hardware address on Unix using ctypes."""
00376 _uuid_generate_time(_buffer)
00377 return UUID(bytes=_buffer.raw).node
00378
00379 def _windll_getnode():
00380 """Get the hardware address on Windows using ctypes."""
00381 if _UuidCreate(_buffer) == 0:
00382 return UUID(bytes=_buffer.raw).node
00383
00384 def _random_getnode():
00385 """Get a random node ID, with eighth bit set as suggested by RFC 4122."""
00386 import random
00387 return random.randrange(0, 1<<48L) | 0x010000000000L
00388
00389 _node = None
00390
00391 def getnode():
00392 """Get the hardware address as a 48-bit integer. The first time this
00393 runs, it may launch a separate program, which could be quite slow. If
00394 all attempts to obtain the hardware address fail, we choose a random
00395 48-bit number with its eighth bit set to 1 as recommended in RFC 4122."""
00396
00397 global _node
00398 if _node is not None:
00399 return _node
00400
00401 import sys
00402 if sys.platform == 'win32':
00403 getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
00404 else:
00405 getters = [_unixdll_getnode, _ifconfig_getnode]
00406
00407 for getter in getters + [_random_getnode]:
00408 try:
00409 _node = getter()
00410 except:
00411 continue
00412 if _node is not None:
00413 return _node
00414
00415 def uuid1(node=None, clock_seq=None):
00416 """Generate a UUID from a host ID, sequence number, and the current time.
00417 If 'node' is not given, getnode() is used to obtain the hardware
00418 address. If 'clock_seq' is given, it is used as the sequence number;
00419 otherwise a random 14-bit sequence number is chosen."""
00420
00421
00422
00423 if _uuid_generate_time and node is clock_seq is None:
00424 _uuid_generate_time(_buffer)
00425 return UUID(bytes=_buffer.raw)
00426
00427 import time
00428 nanoseconds = int(time.time() * 1e9)
00429
00430
00431 timestamp = int(nanoseconds/100) + 0x01b21dd213814000L
00432 if clock_seq is None:
00433 import random
00434 clock_seq = random.randrange(1<<14L)
00435 time_low = timestamp & 0xffffffffL
00436 time_mid = (timestamp >> 32L) & 0xffffL
00437 time_hi_version = (timestamp >> 48L) & 0x0fffL
00438 clock_seq_low = clock_seq & 0xffL
00439 clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL
00440 if node is None:
00441 node = getnode()
00442 return UUID(fields=(time_low, time_mid, time_hi_version,
00443 clock_seq_hi_variant, clock_seq_low, node), version=1)
00444
00445 def uuid3(namespace, name):
00446 """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
00447 import md5
00448 hash = md5.md5(namespace.bytes + name).digest()
00449 return UUID(bytes=hash[:16], version=3)
00450
00451 def uuid4():
00452 """Generate a random UUID."""
00453
00454
00455 if _uuid_generate_random:
00456 _uuid_generate_random(_buffer)
00457 return UUID(bytes=_buffer.raw)
00458
00459
00460 try:
00461 import os
00462 return UUID(bytes=os.urandom(16), version=4)
00463 except:
00464 import random
00465 bytes = [chr(random.randrange(256)) for i in range(16)]
00466 return UUID(bytes=bytes, version=4)
00467
00468 def uuid5(namespace, name):
00469 """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
00470 import sha
00471 hash = sha.sha(namespace.bytes + name).digest()
00472 return UUID(bytes=hash[:16], version=5)
00473
00474
00475
00476 NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
00477 NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
00478 NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
00479 NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')