blob: b0afd945c1047d6b462008e4fe2420720ac77364 [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']:
277 try:
Georg Brandl75a832d2006-07-27 16:08:15 +0000278 # LC_ALL to get English output, 2>/dev/null to
279 # prevent output on stderr
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000280 executable = os.path.join(dir, command)
281 cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args)
Georg Brandl75a832d2006-07-27 16:08:15 +0000282 pipe = os.popen(cmd)
Tim Peters2adc6262006-06-13 00:30:50 +0000283 except IOError:
284 continue
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000285
Tim Peters2adc6262006-06-13 00:30:50 +0000286 for line in pipe:
287 words = line.lower().split()
288 for i in range(len(words)):
Neal Norwitz46fc6a02006-07-28 07:21:27 +0000289 if words[i] in hw_identifiers:
290 return int(words[get_index(i)].replace(':', ''), 16)
291 return None
292
293def _ifconfig_getnode():
294 """Get the hardware address on Unix by running ifconfig."""
295
296 # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
297 for args in ('', '-a', '-av'):
298 mac = _find_mac('ifconfig', args, ['hwaddr', 'ether'], lambda i: i+1)
299 if mac:
300 return mac
301
302 import socket
303 ip_addr = socket.gethostbyname(socket.gethostname())
304
305 # Try getting the MAC addr from arp based on our IP address (Solaris).
306 mac = _find_mac('arp', '-an', [ip_addr], lambda i: -1)
307 if mac:
308 return mac
309
310 # This might work on HP-UX.
311 mac = _find_mac('lanscan', '-ai', ['lan0'], lambda i: 0)
312 if mac:
313 return mac
314
315 return None
Tim Peters2adc6262006-06-13 00:30:50 +0000316
317def _ipconfig_getnode():
318 """Get the hardware address on Windows by running ipconfig.exe."""
319 import os, re
320 dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
321 try:
322 import ctypes
323 buffer = ctypes.create_string_buffer(300)
324 ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
325 dirs.insert(0, buffer.value.decode('mbcs'))
326 except:
327 pass
328 for dir in dirs:
329 try:
330 pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all')
331 except IOError:
332 continue
333 for line in pipe:
334 value = line.split(':')[-1].strip().lower()
335 if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
336 return int(value.replace('-', ''), 16)
337
338def _netbios_getnode():
339 """Get the hardware address on Windows using NetBIOS calls.
340 See http://support.microsoft.com/kb/118623 for details."""
341 import win32wnet, netbios
342 ncb = netbios.NCB()
343 ncb.Command = netbios.NCBENUM
344 ncb.Buffer = adapters = netbios.LANA_ENUM()
345 adapters._pack()
346 if win32wnet.Netbios(ncb) != 0:
347 return
348 adapters._unpack()
349 for i in range(adapters.length):
350 ncb.Reset()
351 ncb.Command = netbios.NCBRESET
352 ncb.Lana_num = ord(adapters.lana[i])
353 if win32wnet.Netbios(ncb) != 0:
354 continue
355 ncb.Reset()
356 ncb.Command = netbios.NCBASTAT
357 ncb.Lana_num = ord(adapters.lana[i])
358 ncb.Callname = '*'.ljust(16)
359 ncb.Buffer = status = netbios.ADAPTER_STATUS()
360 if win32wnet.Netbios(ncb) != 0:
361 continue
362 status._unpack()
363 bytes = map(ord, status.adapter_address)
364 return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) +
365 (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5])
366
367# Thanks to Thomas Heller for ctypes and for his help with its use here.
368
369# If ctypes is available, use it to find system routines for UUID generation.
370_uuid_generate_random = _uuid_generate_time = _UuidCreate = None
371try:
372 import ctypes, ctypes.util
373 _buffer = ctypes.create_string_buffer(16)
374
375 # The uuid_generate_* routines are provided by libuuid on at least
376 # Linux and FreeBSD, and provided by libc on Mac OS X.
377 for libname in ['uuid', 'c']:
378 try:
379 lib = ctypes.CDLL(ctypes.util.find_library(libname))
380 except:
381 continue
382 if hasattr(lib, 'uuid_generate_random'):
383 _uuid_generate_random = lib.uuid_generate_random
384 if hasattr(lib, 'uuid_generate_time'):
385 _uuid_generate_time = lib.uuid_generate_time
386
387 # On Windows prior to 2000, UuidCreate gives a UUID containing the
388 # hardware address. On Windows 2000 and later, UuidCreate makes a
389 # random UUID and UuidCreateSequential gives a UUID containing the
390 # hardware address. These routines are provided by the RPC runtime.
Tim Peters750c4422006-07-28 04:51:59 +0000391 # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last
392 # 6 bytes returned by UuidCreateSequential are fixed, they don't appear
393 # to bear any relationship to the MAC address of any network device
394 # on the box.
Tim Peters2adc6262006-06-13 00:30:50 +0000395 try:
396 lib = ctypes.windll.rpcrt4
397 except:
398 lib = None
399 _UuidCreate = getattr(lib, 'UuidCreateSequential',
400 getattr(lib, 'UuidCreate', None))
401except:
402 pass
403
404def _unixdll_getnode():
405 """Get the hardware address on Unix using ctypes."""
406 _uuid_generate_time(_buffer)
407 return UUID(bytes=_buffer.raw).node
408
409def _windll_getnode():
410 """Get the hardware address on Windows using ctypes."""
411 if _UuidCreate(_buffer) == 0:
412 return UUID(bytes=_buffer.raw).node
413
414def _random_getnode():
415 """Get a random node ID, with eighth bit set as suggested by RFC 4122."""
416 import random
417 return random.randrange(0, 1<<48L) | 0x010000000000L
418
419_node = None
420
421def getnode():
Tim Peters750c4422006-07-28 04:51:59 +0000422 """Get the hardware address as a 48-bit positive integer.
423
424 The first time this runs, it may launch a separate program, which could
425 be quite slow. If all attempts to obtain the hardware address fail, we
426 choose a random 48-bit number with its eighth bit set to 1 as recommended
427 in RFC 4122.
428 """
Tim Peters2adc6262006-06-13 00:30:50 +0000429
430 global _node
431 if _node is not None:
432 return _node
433
434 import sys
435 if sys.platform == 'win32':
436 getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode]
437 else:
438 getters = [_unixdll_getnode, _ifconfig_getnode]
439
440 for getter in getters + [_random_getnode]:
441 try:
442 _node = getter()
443 except:
444 continue
445 if _node is not None:
446 return _node
447
448def uuid1(node=None, clock_seq=None):
449 """Generate a UUID from a host ID, sequence number, and the current time.
450 If 'node' is not given, getnode() is used to obtain the hardware
451 address. If 'clock_seq' is given, it is used as the sequence number;
452 otherwise a random 14-bit sequence number is chosen."""
453
454 # When the system provides a version-1 UUID generator, use it (but don't
455 # use UuidCreate here because its UUIDs don't conform to RFC 4122).
456 if _uuid_generate_time and node is clock_seq is None:
457 _uuid_generate_time(_buffer)
458 return UUID(bytes=_buffer.raw)
459
460 import time
461 nanoseconds = int(time.time() * 1e9)
462 # 0x01b21dd213814000 is the number of 100-ns intervals between the
463 # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
464 timestamp = int(nanoseconds/100) + 0x01b21dd213814000L
465 if clock_seq is None:
466 import random
467 clock_seq = random.randrange(1<<14L) # instead of stable storage
468 time_low = timestamp & 0xffffffffL
469 time_mid = (timestamp >> 32L) & 0xffffL
470 time_hi_version = (timestamp >> 48L) & 0x0fffL
471 clock_seq_low = clock_seq & 0xffL
472 clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL
473 if node is None:
474 node = getnode()
475 return UUID(fields=(time_low, time_mid, time_hi_version,
476 clock_seq_hi_variant, clock_seq_low, node), version=1)
477
478def uuid3(namespace, name):
479 """Generate a UUID from the MD5 hash of a namespace UUID and a name."""
480 import md5
481 hash = md5.md5(namespace.bytes + name).digest()
482 return UUID(bytes=hash[:16], version=3)
483
484def uuid4():
485 """Generate a random UUID."""
486
487 # When the system provides a version-4 UUID generator, use it.
488 if _uuid_generate_random:
489 _uuid_generate_random(_buffer)
490 return UUID(bytes=_buffer.raw)
491
492 # Otherwise, get randomness from urandom or the 'random' module.
493 try:
494 import os
495 return UUID(bytes=os.urandom(16), version=4)
496 except:
497 import random
498 bytes = [chr(random.randrange(256)) for i in range(16)]
499 return UUID(bytes=bytes, version=4)
500
501def uuid5(namespace, name):
502 """Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
503 import sha
504 hash = sha.sha(namespace.bytes + name).digest()
505 return UUID(bytes=hash[:16], version=5)
506
507# The following standard UUIDs are for use with uuid3() or uuid5().
508
509NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
510NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
511NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
512NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')