1 | # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) |
---|
2 | # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php |
---|
3 | """UUID (universally unique identifiers) as specified in RFC 4122. |
---|
4 | |
---|
5 | This module provides the UUID class and the functions uuid1(), uuid3(), |
---|
6 | uuid4(), uuid5() for generating version 1, 3, 4, and 5 UUIDs respectively. |
---|
7 | |
---|
8 | This module works with Python 2.3 or higher.""" |
---|
9 | |
---|
10 | __author__ = 'Ka-Ping Yee <ping@zesty.ca>' |
---|
11 | __date__ = '$Date: 2005/11/30 11:51:58 $'.split()[1].replace('/', '-') |
---|
12 | __version__ = '$Revision: 1.10 $' |
---|
13 | |
---|
14 | RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [ |
---|
15 | 'reserved for NCS compatibility', 'specified in RFC 4122', |
---|
16 | 'reserved for Microsoft compatibility', 'reserved for future definition'] |
---|
17 | |
---|
18 | class UUID(object): |
---|
19 | """Instances of the UUID class represent UUIDs as specified in RFC 4122. |
---|
20 | Converting a UUID to a string using str() produces a string in the form |
---|
21 | "{12345678-1234-1234-1234-123456789abc}". The UUID constructor accepts |
---|
22 | a similar string (braces and hyphens optional), or six integer arguments |
---|
23 | (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and 48-bit values |
---|
24 | respectively). UUID objects have the following attributes: |
---|
25 | |
---|
26 | bytes gets or sets the UUID as a 16-byte string |
---|
27 | |
---|
28 | urn gets the UUID as a URN as specified in RFC 4122 |
---|
29 | |
---|
30 | variant gets or sets the UUID variant as one of the constants |
---|
31 | RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE |
---|
32 | |
---|
33 | version gets or sets the UUID version number (1 through 5) |
---|
34 | """ |
---|
35 | |
---|
36 | def __init__(self, *args): |
---|
37 | """Create a UUID either from a string representation in hexadecimal |
---|
38 | or from six integers (32-bit time_low, 16-bit time_mid, 16-bit |
---|
39 | time_hi_ver, 8-bit clock_hi_res, 8-bit clock_low, 48-bit node).""" |
---|
40 | if len(args) == 1: |
---|
41 | digits = args[0].replace('urn:', '').replace('uuid:', '') |
---|
42 | digits = digits.replace('{', '').replace('}', '').replace('-', '') |
---|
43 | assert len(digits) == 32, ValueError('badly formed UUID string') |
---|
44 | time_low = int(digits[:8], 16) |
---|
45 | time_mid = int(digits[8:12], 16) |
---|
46 | time_hi_ver = int(digits[12:16], 16) |
---|
47 | clock_hi_res = int(digits[16:18], 16) |
---|
48 | clock_low = int(digits[18:20], 16) |
---|
49 | node = int(digits[20:32], 16) |
---|
50 | else: |
---|
51 | (time_low, time_mid, time_hi_ver, |
---|
52 | clock_hi_res, clock_low, node) = args |
---|
53 | assert 0 <= time_low < 0x100000000, ValueError('time_low out of range') |
---|
54 | assert 0 <= time_mid < 1<<16, ValueError('time_mid out of range') |
---|
55 | assert 0 <= time_hi_ver < 1<<16, ValueError('time_hi_ver out of range') |
---|
56 | assert 0 <= clock_hi_res < 1<<8, ValueError('clock_hi_res out of range') |
---|
57 | assert 0 <= clock_low < 1<<8, ValueError('clock_low out of range') |
---|
58 | assert 0 <= node < 0x1000000000000, ValueError('node out of range') |
---|
59 | self.time_low = time_low |
---|
60 | self.time_mid = time_mid |
---|
61 | self.time_hi_ver = time_hi_ver |
---|
62 | self.clock_hi_res = clock_hi_res |
---|
63 | self.clock_low = clock_low |
---|
64 | self.node = node |
---|
65 | |
---|
66 | def __cmp__(self, other): |
---|
67 | return cmp(self.bytes, getattr(other, 'bytes', other)) |
---|
68 | |
---|
69 | def __str__(self): |
---|
70 | return '{%08x-%04x-%04x-%02x%02x-%012x}' % ( |
---|
71 | self.time_low, self.time_mid, self.time_hi_ver, |
---|
72 | self.clock_hi_res, self.clock_low, self.node) |
---|
73 | |
---|
74 | def __repr__(self): |
---|
75 | return 'UUID(%r)' % str(self) |
---|
76 | |
---|
77 | def get_bytes(self): |
---|
78 | def byte(n): |
---|
79 | return chr(n & 0xff) |
---|
80 | |
---|
81 | return (byte(self.time_low >> 24) + byte(self.time_low >> 16) + |
---|
82 | byte(self.time_low >> 8) + byte(self.time_low) + |
---|
83 | byte(self.time_mid >> 8) + byte(self.time_mid) + |
---|
84 | byte(self.time_hi_ver >> 8) + byte(self.time_hi_ver) + |
---|
85 | byte(self.clock_hi_res) + byte(self.clock_low) + |
---|
86 | byte(self.node >> 40) + byte(self.node >> 32) + |
---|
87 | byte(self.node >> 24) + byte(self.node >> 16) + |
---|
88 | byte(self.node >> 8) + byte(self.node)) |
---|
89 | |
---|
90 | def set_bytes(self, bytes): |
---|
91 | values = map(ord, bytes) |
---|
92 | self.time_low = ((values[0] << 24) + (values[1] << 16) + |
---|
93 | (values[2] << 8) + values[3]) |
---|
94 | self.time_mid = (values[4] << 8) + values[5] |
---|
95 | self.time_hi_ver = (values[6] << 8) + values[7] |
---|
96 | self.clock_hi_res = values[8] |
---|
97 | self.clock_low = values[9] |
---|
98 | self.node = ((values[10] << 40) + (values[11] << 32) + |
---|
99 | (values[12] << 24) + (values[13] << 16) + |
---|
100 | (values[14] << 8) + values[15]) |
---|
101 | |
---|
102 | bytes = property(get_bytes, set_bytes) |
---|
103 | |
---|
104 | def get_urn(self): |
---|
105 | return 'urn:uuid:%08x-%04x-%04x-%02x%02x-%012x' % ( |
---|
106 | self.time_low, self.time_mid, self.time_hi_ver, |
---|
107 | self.clock_hi_res, self.clock_low, self.node) |
---|
108 | |
---|
109 | urn = property(get_urn) |
---|
110 | |
---|
111 | def get_variant(self): |
---|
112 | if not self.clock_hi_res & 0x80: |
---|
113 | return RESERVED_NCS |
---|
114 | elif not self.clock_hi_res & 0x40: |
---|
115 | return RFC_4122 |
---|
116 | elif not self.clock_hi_res & 0x20: |
---|
117 | return RESERVED_MICROSOFT |
---|
118 | else: |
---|
119 | return RESERVED_FUTURE |
---|
120 | |
---|
121 | def set_variant(self, variant): |
---|
122 | if variant == RESERVED_NCS: |
---|
123 | self.clock_hi_res &= 0x7f |
---|
124 | elif variant == RFC_4122: |
---|
125 | self.clock_hi_res &= 0x3f |
---|
126 | self.clock_hi_res |= 0x80 |
---|
127 | elif variant == RESERVED_MICROSOFT: |
---|
128 | self.clock_hi_res &= 0x1f |
---|
129 | self.clock_hi_res |= 0xc0 |
---|
130 | elif variant == RESERVED_FUTURE: |
---|
131 | self.clock_hi_res &= 0x1f |
---|
132 | self.clock_hi_res |= 0xe0 |
---|
133 | else: |
---|
134 | raise ValueError('illegal variant identifier') |
---|
135 | |
---|
136 | variant = property(get_variant, set_variant) |
---|
137 | |
---|
138 | def get_version(self): |
---|
139 | return self.time_hi_ver >> 12 |
---|
140 | |
---|
141 | def set_version(self, version): |
---|
142 | assert 1 <= version <= 5, ValueError('illegal version number') |
---|
143 | self.time_hi_ver &= 0x0fff |
---|
144 | self.time_hi_ver |= (version << 12) |
---|
145 | |
---|
146 | version = property(get_version, set_version) |
---|
147 | |
---|
148 | def unixgetaddr(program): |
---|
149 | """Get the hardware address on a Unix machine.""" |
---|
150 | from os import popen |
---|
151 | for line in popen(program): |
---|
152 | words = line.lower().split() |
---|
153 | if 'hwaddr' in words: |
---|
154 | addr = words[words.index('hwaddr') + 1] |
---|
155 | return int(addr.replace(':', ''), 16) |
---|
156 | if 'ether' in words: |
---|
157 | addr = words[words.index('ether') + 1] |
---|
158 | return int(addr.replace(':', ''), 16) |
---|
159 | |
---|
160 | def wingetaddr(program): |
---|
161 | """Get the hardware address on a Windows machine.""" |
---|
162 | from os import popen |
---|
163 | for line in popen(program + ' /all'): |
---|
164 | if line.strip().lower().startswith('physical address'): |
---|
165 | addr = line.split(':')[-1].strip() |
---|
166 | return int(addr.replace('-', ''), 16) |
---|
167 | |
---|
168 | def getaddr(): |
---|
169 | """Get the hardware address as a 48-bit integer.""" |
---|
170 | from os.path import join, isfile |
---|
171 | for dir in ['/sbin', '/usr/sbin', r'c:\windows', |
---|
172 | r'c:\windows\system', r'c:\windows\system32']: |
---|
173 | if isfile(join(dir, 'ifconfig')): |
---|
174 | return unixgetaddr(join(dir, 'ifconfig')) |
---|
175 | if isfile(join(dir, 'ipconfig.exe')): |
---|
176 | return wingetaddr(join(dir, 'ipconfig.exe')) |
---|
177 | |
---|
178 | def uuid1(): |
---|
179 | """Generate a UUID based on the time and hardware address.""" |
---|
180 | from time import time |
---|
181 | from random import randrange |
---|
182 | nanoseconds = int(time() * 1e9) |
---|
183 | # 0x01b21dd213814000 is the number of 100-ns intervals between the |
---|
184 | # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. |
---|
185 | timestamp = int(nanoseconds/100) + 0x01b21dd213814000 |
---|
186 | clock = randrange(1<<16) # don't use stable storage |
---|
187 | time_low = timestamp & (0x100000000 - 1) |
---|
188 | time_mid = (timestamp >> 32) & 0xffff |
---|
189 | time_hi_ver = (timestamp >> 48) & 0x0fff |
---|
190 | clock_low = clock & 0xff |
---|
191 | clock_hi_res = (clock >> 8) & 0x3f |
---|
192 | node = getaddr() |
---|
193 | uuid = UUID(time_low, time_mid, time_hi_ver, clock_low, clock_hi_res, node) |
---|
194 | uuid.variant = RFC_4122 |
---|
195 | uuid.version = 1 |
---|
196 | return uuid |
---|
197 | |
---|
198 | def uuid3(namespace, name): |
---|
199 | """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" |
---|
200 | try: |
---|
201 | from hashlib import md5 |
---|
202 | except ImportError: |
---|
203 | from md5 import md5 |
---|
204 | uuid = UUID(0, 0, 0, 0, 0, 0) |
---|
205 | uuid.bytes = md5(namespace.bytes + name).digest()[:16] |
---|
206 | uuid.variant = RFC_4122 |
---|
207 | uuid.version = 3 |
---|
208 | return uuid |
---|
209 | |
---|
210 | def uuid4(): |
---|
211 | """Generate a random UUID.""" |
---|
212 | try: |
---|
213 | from os import urandom |
---|
214 | except: |
---|
215 | from random import randrange |
---|
216 | uuid = UUID(randrange(1<<32L), randrange(1<<16), randrange(1<<16), |
---|
217 | randrange(1<<8), randrange(1<<8), randrange(1<<48L)) |
---|
218 | else: |
---|
219 | uuid = UUID(0, 0, 0, 0, 0, 0) |
---|
220 | uuid.bytes = urandom(16) |
---|
221 | uuid.variant = RFC_4122 |
---|
222 | uuid.version = 4 |
---|
223 | return uuid |
---|
224 | |
---|
225 | def uuid5(namespace, name): |
---|
226 | """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" |
---|
227 | try: |
---|
228 | from hashlib import sha1 |
---|
229 | except ImportError: |
---|
230 | from sha import sha as sha1 |
---|
231 | uuid = UUID(0, 0, 0, 0, 0, 0) |
---|
232 | uuid.bytes = sha1(namespace.bytes + name).digest()[:16] |
---|
233 | uuid.variant = RFC_4122 |
---|
234 | uuid.version = 5 |
---|
235 | return uuid |
---|
236 | |
---|
237 | NAMESPACE_DNS = UUID('{6ba7b810-9dad-11d1-80b4-00c04fd430c8}') |
---|
238 | NAMESPACE_URL = UUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}') |
---|
239 | NAMESPACE_OID = UUID('{6ba7b812-9dad-11d1-80b4-00c04fd430c8}') |
---|
240 | NAMESPACE_X500 = UUID('{6ba7b814-9dad-11d1-80b4-00c04fd430c8}') |
---|