blob: a6446a16f9fc76b492484d6aa9ec3d90f9b1c550 [file] [log] [blame]
Tim Peters2adc6262006-06-13 00:30:50 +00001r"""UUID objects (universally unique identifiers) according to RFC 4122.
2
3This module provides immutable UUID objects (class UUID) and the functions
4uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
5UUIDs as specified in RFC 4122.
6
7If all you want is a unique ID, you should probably call uuid1() or uuid4().
8Note that uuid1() may compromise privacy since it creates a UUID containing
9the computer's network address. uuid4() creates a random UUID.
10
11Typical usage:
12
13 >>> import uuid
14
15 # make a UUID based on the host ID and current time
16 >>> uuid.uuid1()
17 UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
18
19 # make a UUID using an MD5 hash of a namespace UUID and a name
20 >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
21 UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
22
23 # make a random UUID
24 >>> uuid.uuid4()
25 UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
26
27 # make a UUID using a SHA-1 hash of a namespace UUID and a name
28 >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
29 UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
30
31 # make a UUID from a string of hex digits (braces and hyphens ignored)
32 >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
33
34 # convert a UUID to a string of hex digits in standard form
35 >>> str(x)
36 '00010203-0405-0607-0809-0a0b0c0d0e0f'
37
38 # get the raw 16 bytes of the UUID
39 >>> x.bytes
40 '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
41
42 # make a UUID from a 16-byte string
43 >>> uuid.UUID(bytes=x.bytes)
44 UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
Ka-Ping Yee3dbc8912006-06-19 22:49:36 +000045"""
Tim Peters2adc6262006-06-13 00:30:50 +000046
47__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
48__date__ = '$Date: 2006/06/12 23:15:40 $'.split()[1].replace('/', '-')
49__version__ = '$Revision: 1.30 $'.split()[1]
50
51RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
52 'reserved for NCS compatibility', 'specified in RFC 4122',
53 'reserved for Microsoft compatibility', 'reserved for future definition']
54
55class UUID(object):
56 """Instances of the UUID class represent UUIDs as specified in RFC 4122.
57 UUID objects are immutable, hashable, and usable as dictionary keys.
58 Converting a UUID to a string with str() yields something in the form
59 '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts
60 four possible forms: a similar string of hexadecimal digits, or a
61 string of 16 raw bytes as an argument named 'bytes', or a tuple of
62 six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
63 48-bit values respectively) as an argument named 'fields', or a single
64 128-bit integer as an argument named 'int'.
65
66 UUIDs have these read-only attributes:
67
68 bytes the UUID as a 16-byte string
69
70 fields a tuple of the six integer fields of the UUID,
71 which are also available as six individual attributes
72 and two derived attributes:
73
74 time_low the first 32 bits of the UUID
75 time_mid the next 16 bits of the UUID
76 time_hi_version the next 16 bits of the UUID
77 clock_seq_hi_variant the next 8 bits of the UUID
78 clock_seq_low the next 8 bits of the UUID
79 node the last 48 bits of the UUID
80
81 time the 60-bit timestamp
82 clock_seq the 14-bit sequence number
83
84 hex the UUID as a 32-character hexadecimal string
85
86 int the UUID as a 128-bit integer
87
88 urn the UUID as a URN as specified in RFC 4122
89
90 variant the UUID variant (one of the constants RESERVED_NCS,
91 RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
92
93 version the UUID version number (1 through 5, meaningful only
94 when the variant is RFC_4122)
95 """
96
97 def __init__(self, hex=None, bytes=None, fields=None, int=None,
98 version=None):
99 r"""Create a UUID from either a string of 32 hexadecimal digits,
100 a string of 16 bytes as the 'bytes' argument, a tuple of six
101 integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
102 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
103 the 'fields' argument, or a single 128-bit integer as the 'int'
104 argument. When a string of hex digits is given, curly braces,
105 hyphens, and a URN prefix are all optional. For example, these
106 expressions all yield the same UUID:
107
108 UUID('{12345678-1234-5678-1234-567812345678}')
109 UUID('12345678123456781234567812345678')
110 UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
111 UUID(bytes='\x12\x34\x56\x78'*4)
112 UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
113 UUID(int=0x12345678123456781234567812345678)
114
115 Exactly one of 'hex', 'bytes', 'fields', or 'int' must be given.
116 The 'version' argument is optional; if given, the resulting UUID
117 will have its variant and version number set according to RFC 4122,
118 overriding bits in the given 'hex', 'bytes', 'fields', or 'int'.
119 """
120
121 if [hex, bytes, fields, int].count(None) != 3:
122 raise TypeError('need just one of hex, bytes, fields, or int')
123 if hex is not None:
124 hex = hex.replace('urn:', '').replace('uuid:', '')
125 hex = hex.strip('{}').replace('-', '')
126 if len(hex) != 32:
127 raise ValueError('badly formed hexadecimal UUID string')
128 int = long(hex, 16)
129 if bytes is not None:
130 if len(bytes) != 16:
131 raise ValueError('bytes is not a 16-char string')
132 int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
133 if fields is not None:
134 if len(fields) != 6:
135 raise ValueError('fields is not a 6-tuple')
136 (time_low, time_mid, time_hi_version,
137 clock_seq_hi_variant, clock_seq_low, node) = fields
138 if not 0 <= time_low < 1<<32L:
139 raise ValueError('field 1 out of range (need a 32-bit value)')
140 if not 0 <= time_mid < 1<<16L:
141 raise ValueError('field 2 out of range (need a 16-bit value)')
142 if not 0 <= time_hi_version < 1<<16L:
143 raise ValueError('field 3 out of range (need a 16-bit value)')
144 if not 0 <= clock_seq_hi_variant < 1<<8L:
145 raise ValueError('field 4 out of range (need an 8-bit value)')
146 if not 0 <= clock_seq_low < 1<<8L:
147 raise ValueError('field 5 out of range (need an 8-bit value)')
148 if not 0 <= node < 1<<48L:
149 raise ValueError('field 6 out of range (need a 48-bit value)')
150 clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low
151 int = ((time_low << 96L) | (time_mid << 80L) |
152 (time_hi_version << 64L) | (clock_seq << 48L) | node)
153 if int is not None:
154 if not 0 <= int < 1<<128L:
155 raise ValueError('int is out of range (need a 128-bit value)')
156 if version is not None:
157 if not 1 <= version <= 5:
158 raise ValueError('illegal version number')
159 # Set the variant to RFC 4122.
160 int &= ~(0xc000 << 48L)
161 int |= 0x8000 << 48L
162 # Set the version number.
163 int &= ~(0xf000 << 64L)
164 int |= version << 76L
165 self.__dict__['int'] = int
166
167 def __cmp__(self, other):
168 if isinstance(other, UUID):
169 return cmp(self.int, other.int)
170 return NotImplemented
171
172 def __hash__(self):
173 return hash(self.int)
174
175 def __int__(self):
176 return self.int
177
178 def __repr__(self):
179 return 'UUID(%r)' % str(self)
180
181 def __setattr__(self, name, value):
182 raise TypeError('UUID objects are immutable')
183
184 def __str__(self):
185 hex = '%032x' % self.int
186 return '%s-%s-%s-%s-%s' % (
187 hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
188
189 def get_bytes(self):
190 bytes = ''
191 for shift in range(0, 128, 8):
192 bytes = chr((self.int >> shift) & 0xff) + bytes
193 return bytes
194
195 bytes = property(get_bytes)
196
197 def get_fields(self):
198 return (self.time_low, self.time_mid, self.time_hi_version,
199 self.clock_seq_hi_variant, self.clock_seq_low, self.node)
200
201 fields = property(get_fields)
202
203 def get_time_low(self):
204 return self.int >> 96L
205
206 time_low = property(get_time_low)
207
208 def get_time_mid(self):
209 return (self.int >> 80L) & 0xffff
210
211 time_mid = property(get_time_mid)
212
213 def get_time_hi_version(self):
214 return (self.int >> 64L) & 0xffff
215
216 time_hi_version = property(get_time_hi_version)
217
218 def get_clock_seq_hi_variant(self):
219 return (self.int >> 56L) & 0xff
220
221 clock_seq_hi_variant = property(get_clock_seq_hi_variant)
222
223 def get_clock_seq_low(self):
224 return (self.int >> 48L) & 0xff
225
226 clock_seq_low = property(get_clock_seq_low)
227
228 def get_time(self):
229 return (((self.time_hi_version & 0x0fffL) << 48L) |
230 (self.time_mid << 32L) | self.time_low)
231
232 time = property(get_time)
233
234 def get_clock_seq(self):
235 return (((self.clock_seq_hi_variant & 0x3fL) << 8L) |
236 self.clock_seq_low)
237
238 clock_seq = property(get_clock_seq)
239
240 def get_node(self):
241 return self.int & 0xffffffffffff
242
243 node = property(get_node)
244
245 def get_hex(self):
246 return '%032x' % self.int
247
248 hex = property(get_hex)
249
250 def get_urn(self):
251 return 'urn:uuid:' + str(self)
252
253 urn = property(get_urn)
254
255 def get_variant(self):
256 if not self.int & (0x8000 << 48L):
257 return RESERVED_NCS
258 elif not self.int & (0x4000 << 48L):
259 return RFC_4122
260 elif not self.int & (0x2000 << 48L):
261 return RESERVED_MICROSOFT
262 else:
263 return RESERVED_FUTURE
264
265 variant = property(get_variant)
266
267 def get_version(self):
268 # The version bits are only meaningful for RFC 4122 UUIDs.
269 if self.variant == RFC_4122:
270 return int((self.int >> 76L) & 0xf)
271
272 version = property(get_version)
273
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000274def _find_mac(command, args, hw_identifiers, get_index):
Tim Peters2adc6262006-06-13 00:30:50 +0000275 import os
276 for dir in ['', '/sbin/', '/usr/sbin']:
Neal Norwitz175001d2006-07-29 20:20:52 +0000277 executable = os.path.join(dir, command)
278 if not os.path.exists(executable):
279 continue
280
Tim Peters2adc6262006-06-13 00:30:50 +0000281 try:
Georg Brandl75a832d2006-07-27 16:08:15 +0000282 # LC_ALL to get English output, 2>/dev/null to
283 # prevent output on stderr
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000284 cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args)
Georg Brandl75a832d2006-07-27 16:08:15 +0000285 pipe = os.popen(cmd)
Tim Peters2adc6262006-06-13 00:30:50 +0000286 except IOError:
287 continue
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000288
Tim Peters2adc6262006-06-13 00:30:50 +0000289 for line in pipe:
290 words = line.lower().split()
291 for i in range(len(words)):
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000292 if words[i] in hw_identifiers:
293 return int(words[get_index(i)].replace(':', ''), 16)
294 return None
295
296def _ifconfig_getnode():
297 """Get the hardware address on Unix by running ifconfig."""
298
299 # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
300 for args in ('', '-a', '-av'):
301 mac = _find_mac('ifconfig', args, ['hwaddr', 'ether'], lambda i: i+1)
302 if mac:
303 return mac
304
305 import socket
306 ip_addr = socket.gethostbyname(socket.gethostname())
307
308 # Try getting the MAC addr from arp based on our IP address (Solaris).
309 mac = _find_mac('arp', '-an', [ip_addr], lambda i: -1)
310 if mac:
311 return mac
312
313 # This might work on HP-UX.
314 mac = _find_mac('lanscan', '-ai', ['lan0'], lambda i: 0)
315 if mac:
316 return mac
317
318 return None
Tim Peters2adc6262006-06-13 00:30:50 +0000319
320def _ipconfig_getnode():
321 """Get the hardware address on Windows by running ipconfig.exe."""
322 import os, re
323 dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
324 try:
325 import ctypes
326 buffer = ctypes.create_string_buffer(300)
327 ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
328 dirs.insert(0, buffer.value.decode('mbcs'))
329 except:
330 pass
331 for dir in dirs:
332 try:
333 pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all')
334 except IOError:
335 continue
336 for line in pipe:
337 value = line.split(':')[-1].strip().lower()
338 if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
339 return int(value.replace('-', ''), 16)
340
341def _netbios_getnode():
342 """Get the hardware address on Windows using NetBIOS calls.
343 See http://support.microsoft.com/kb/118623 for details."""
344 import win32wnet, netbios
345 ncb = netbios.NCB()
346 ncb.Command = netbios.NCBENUM
347 ncb.Buffer = adapters = netbios.LANA_ENUM()
348 adapters._pack()
349 if win32wnet.Netbios(ncb) != 0:
350 return
351 adapters._unpack()
352 for i in range(adapters.length):
353 ncb.Reset()
354 ncb.Command = netbios.NCBRESET
355 ncb.Lana_num = ord(adapters.lana[i])
356 if win32wnet.Netbios(ncb) != 0:
357 continue
358 ncb.Reset()
359 ncb.Command = netbios.NCBASTAT
360 ncb.Lana_num = ord(adapters.lana[i])
361 ncb.Callname = '*'.ljust(16)
362 ncb.Buffer = status = netbios.ADAPTER_STATUS()
363 if win32wnet.Netbios(ncb) != 0:
364 continue
365 status._unpack()
366 bytes = map(ord, status.adapter_address)
367 return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) +
368 (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5])
369
370# Thanks to Thomas Heller for ctypes and for his help with its use here.
371
372# If ctypes is available, use it to find system routines for UUID generation.
373_uuid_generate_random = _uuid_generate_time = _UuidCreate = None
374try:
375 import ctypes, ctypes.util
376 _buffer = ctypes.create_string_buffer(16)
377
378 # The uuid_generate_* routines are provided by libuuid on at least
379 # Linux and FreeBSD, and provided by libc on Mac OS X.
380 for libname in ['uuid', 'c']:
381 try:
382 lib = ctypes.CDLL(ctypes.util.find_library(libname))
383 except:
384 continue
385 if hasattr(lib, 'uuid_generate_random'):
386 _uuid_generate_random = lib.uuid_generate_random
387 if hasattr(lib, 'uuid_generate_time'):
388 _uuid_generate_time = lib.uuid_generate_time
389
390 # On Windows prior to 2000, UuidCreate gives a UUID containing the
391 # hardware address. On Windows 2000 and later, UuidCreate makes a
392 # random UUID and UuidCreateSequential gives a UUID containing the
393 # hardware address. These routines are provided by the RPC runtime.
Tim Peters750c4422006-07-28 04:51:59 +0000394 # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last
395 # 6 bytes returned by UuidCreateSequential are fixed, they don't appear
396 # to bear any relationship to the MAC address of any network device
397 # on the box.
Tim Peters2adc6262006-06-13 00:30:50 +0000398 try:
399 lib = ctypes.windll.rpcrt4
400 except:
401 lib = None
402 _UuidCreate = getattr(lib, 'UuidCreateSequential',
403 getattr(lib, 'UuidCreate', None))
404except:
405 pass
406
407def _unixdll_getnode():
408 """Get the hardware address on Unix using ctypes."""
409 _uuid_generate_time(_buffer)
410 return UUID(bytes=_buffer.raw).node
411
412def _windll_getnode():
413 """Get the hardware address on Windows using ctypes."""
414 if _UuidCreate(_buffer) == 0:
415 return UUID(bytes=_buffer.raw).node
416
417def _random_getnode():
418 """Get a random node ID, with eighth bit set as suggested by RFC 4122."""
419 import random
420 return random.randrange(0, 1<<48L) | 0x010000000000L
421
422_node = None
423
424def getnode():
Tim Peters750c4422006-07-28 04:51:59 +0000425 """Get the hardware address as a 48-bit positive integer.
426
427 The first time this runs, it may launch a separate program, which could
428 be quite slow. If all attempts to obtain the hardware address fail, we
429 choose a random 48-bit number with its eighth bit set to 1 as recommended
430 in RFC 4122.
431 """
Tim Peters2adc6262006-06-13 00:30:50 +0000432
433 global _node
434 if _node is not None:
435 return _node
436
437 import sys
438 if sys.platform == 'win32':
439 getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
440 else:
441 getters = [_unixdll_getnode, _ifconfig_getnode]
442
443 for getter in getters + [_random_getnode]:
444 try:
445 _node = getter()
446 except:
447 continue
448 if _node is not None:
449 return _node
450
451def uuid1(node=None, clock_seq=None):
452 """Generate a UUID from a host ID, sequence number, and the current time.
453 If 'node' is not given, getnode() is used to obtain the hardware
454 address. If 'clock_seq' is given, it is used as the sequence number;
455 otherwise a random 14-bit sequence number is chosen."""
456
457 # When the system provides a version-1 UUID generator, use it (but don't
458 # use UuidCreate here because its UUIDs don't conform to RFC 4122).
459 if _uuid_generate_time and node is clock_seq is None:
460 _uuid_generate_time(_buffer)
461 return UUID(bytes=_buffer.raw)
462
463 import time
464 nanoseconds = int(time.time() * 1e9)
465 # 0x01b21dd213814000 is the number of 100-ns intervals between the
466 # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
467 timestamp = int(nanoseconds/100) + 0x01b21dd213814000L
468 if clock_seq is None:
469 import random
470 clock_seq = random.randrange(1<<14L) # instead of stable storage
471 time_low = timestamp & 0xffffffffL
472 time_mid = (timestamp >> 32L) & 0xffffL
473 time_hi_version = (timestamp >> 48L) & 0x0fffL
474 clock_seq_low = clock_seq & 0xffL
475 clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL
476 if node is None:
477 node = getnode()
478 return UUID(fields=(time_low, time_mid, time_hi_version,
479 clock_seq_hi_variant, clock_seq_low, node), version=1)
480
481def uuid3(namespace, name):
482 """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
483 import md5
484 hash = md5.md5(namespace.bytes + name).digest()
485 return UUID(bytes=hash[:16], version=3)
486
487def uuid4():
488 """Generate a random UUID."""
489
490 # When the system provides a version-4 UUID generator, use it.
491 if _uuid_generate_random:
492 _uuid_generate_random(_buffer)
493 return UUID(bytes=_buffer.raw)
494
495 # Otherwise, get randomness from urandom or the 'random' module.
496 try:
497 import os
498 return UUID(bytes=os.urandom(16), version=4)
499 except:
500 import random
501 bytes = [chr(random.randrange(256)) for i in range(16)]
502 return UUID(bytes=bytes, version=4)
503
504def uuid5(namespace, name):
505 """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
506 import sha
507 hash = sha.sha(namespace.bytes + name).digest()
508 return UUID(bytes=hash[:16], version=5)
509
510# The following standard UUIDs are for use with uuid3() or uuid5().
511
512NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
513NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
514NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
515NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')