PageRenderTime 479ms CodeModel.GetById 171ms app.highlight 110ms RepoModel.GetById 192ms app.codeStats 0ms

/mysql_watcher/uuid.py

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