~ubuntu-branches/ubuntu/hardy/emesene/hardy-proposed

« back to all changes in this revision

Viewing changes to emesenelib/uuid.py

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2008-02-06 21:57:05 UTC
  • Revision ID: james.westby@ubuntu.com-20080206215705-d1abf07rdwcaju3p
Tags: upstream-1.0~r1013
ImportĀ upstreamĀ versionĀ 1.0~r1013

Show diffs side-by-side

added added

removed removed

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