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