diff -r 3dcbf8f928ec Lib/test/test_uuid.py --- a/Lib/test/test_uuid.py Wed Feb 05 10:33:14 2014 -0500 +++ b/Lib/test/test_uuid.py Wed Oct 28 13:29:38 2015 +0000 @@ -339,11 +339,19 @@ @unittest.skipUnless(os.name == 'posix', 'requires Posix') @unittest.skipUnless(importable('ctypes'), 'requires ctypes') def test_unixdll_getnode(self): + if uuid._uuid is not None: + self.skipTest("_uuid extension was imported, ctypes not used") try: # Issues 1481, 3581: _uuid_generate_time() might be None. self.check_node(uuid._unixdll_getnode(), 'unixdll') except TypeError: pass + @unittest.skipUnless(os.name == 'posix', 'requires Posix') + def test_unix_extmod_getnode(self): + if uuid._uuid is None: + self.skipTest("_uuid extension was not imported") + self.check_node(uuid._unix_extmod_getnode(), 'unix_extmod') + @unittest.skipUnless(os.name == 'nt', 'requires Windows') @unittest.skipUnless(importable('ctypes'), 'requires ctypes') def test_windll_getnode(self): diff -r 3dcbf8f928ec Lib/uuid.py --- a/Lib/uuid.py Wed Feb 05 10:33:14 2014 -0500 +++ b/Lib/uuid.py Wed Oct 28 13:29:38 2015 +0000 @@ -425,23 +425,12 @@ # If ctypes is available, use it to find system routines for UUID generation. # XXX This makes the module non-thread-safe! _uuid_generate_random = _uuid_generate_time = _UuidCreate = None -try: - import ctypes, ctypes.util - # The uuid_generate_* routines are provided by libuuid on at least - # Linux and FreeBSD, and provided by libc on Mac OS X. - for libname in ['uuid', 'c']: - try: - lib = ctypes.CDLL(ctypes.util.find_library(libname)) - except: - continue - if hasattr(lib, 'uuid_generate_random'): - _uuid_generate_random = lib.uuid_generate_random - if hasattr(lib, 'uuid_generate_time'): - _uuid_generate_time = lib.uuid_generate_time - if _uuid_generate_random is not None: - break # found everything we were looking for +import sys +import os + +if sys.platform == "darwin" and int(os.uname().release.split('.')[0]) < 9: # The uuid_generate_* functions are broken on MacOS X 10.5, as noted # in issue #8621 the function generates the same sequence of values # in the parent process and all children created using fork (unless @@ -449,28 +438,64 @@ # # Assume that the uuid_generate functions are broken from 10.5 onward, # the test can be adjusted when a later version is fixed. - import sys - if sys.platform == 'darwin': - import os - if int(os.uname().release.split('.')[0]) >= 9: - _uuid_generate_random = _uuid_generate_time = None + _uuid = None +else: + try: + import _uuid + except ImportError: + _uuid = None - # On Windows prior to 2000, UuidCreate gives a UUID containing the - # hardware address. On Windows 2000 and later, UuidCreate makes a - # random UUID and UuidCreateSequential gives a UUID containing the - # hardware address. These routines are provided by the RPC runtime. - # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last - # 6 bytes returned by UuidCreateSequential are fixed, they don't appear - # to bear any relationship to the MAC address of any network device - # on the box. +if _uuid is None: try: - lib = ctypes.windll.rpcrt4 - except: - lib = None - _UuidCreate = getattr(lib, 'UuidCreateSequential', - getattr(lib, 'UuidCreate', None)) -except: - pass + # If we couldn't find an extension module, try ctypes + import ctypes + import ctypes.util + + # The uuid_generate_* routines are provided by libuuid on at least + # Linux and FreeBSD, and provided by libc on Mac OS X. + for libname in ['uuid', 'c']: + try: + lib = ctypes.CDLL(ctypes.util.find_library(libname)) + except: + continue + if hasattr(lib, 'uuid_generate_random'): + _uuid_generate_random = lib.uuid_generate_random + if hasattr(lib, 'uuid_generate_time'): + _uuid_generate_time = lib.uuid_generate_time + if _uuid_generate_random is not None: + break # found everything we were looking for + + # The uuid_generate_* functions are broken on MacOS X 10.5, as noted + # in issue #8621 the function generates the same sequence of values + # in the parent process and all children created using fork (unless + # those children use exec as well). + # + # Assume that the uuid_generate functions are broken from 10.5 onward, + # the test can be adjusted when a later version is fixed. + if sys.platform == 'darwin': + if int(os.uname().release.split('.')[0]) >= 9: + _uuid_generate_random = _uuid_generate_time = None + + # On Windows prior to 2000, UuidCreate gives a UUID containing the + # hardware address. On Windows 2000 and later, UuidCreate makes a + # random UUID and UuidCreateSequential gives a UUID containing the + # hardware address. These routines are provided by the RPC runtime. + # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last + # 6 bytes returned by UuidCreateSequential are fixed, they don't appear + # to bear any relationship to the MAC address of any network device + # on the box. + try: + lib = ctypes.windll.rpcrt4 + except: + lib = None + _UuidCreate = getattr(lib, 'UuidCreateSequential', + getattr(lib, 'UuidCreate', None)) + except Exception as ex: + import warnings + warnings.warn("Could not find fallback ctypes uuid functions: {}" + .format(ex), + ImportWarning) + def _unixdll_getnode(): """Get the hardware address on Unix using ctypes.""" @@ -478,12 +503,20 @@ _uuid_generate_time(_buffer) return UUID(bytes=bytes_(_buffer.raw)).node + +def _unix_extmod_getnode(): + """Get the hardware address on Unix using the _uuid extension module.""" + uuid_time = _uuid.generate_time() + return UUID(bytes=uuid_time).node + + def _windll_getnode(): """Get the hardware address on Windows using ctypes.""" _buffer = ctypes.create_string_buffer(16) if _UuidCreate(_buffer) == 0: return UUID(bytes=bytes_(_buffer.raw)).node + def _random_getnode(): """Get a random node ID, with eighth bit set as suggested by RFC 4122.""" import random @@ -491,6 +524,7 @@ _node = None + def getnode(): """Get the hardware address as a 48-bit positive integer. @@ -508,7 +542,7 @@ if sys.platform == 'win32': getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] else: - getters = [_unixdll_getnode, _ifconfig_getnode] + getters = [_unix_extmod_getnode, _unixdll_getnode, _ifconfig_getnode] for getter in getters + [_random_getnode]: try: @@ -520,6 +554,7 @@ _last_timestamp = None + def uuid1(node=None, clock_seq=None): """Generate a UUID from a host ID, sequence number, and the current time. If 'node' is not given, getnode() is used to obtain the hardware @@ -528,6 +563,8 @@ # When the system provides a version-1 UUID generator, use it (but don't # use UuidCreate here because its UUIDs don't conform to RFC 4122). + if _uuid is not None and node is clock_seq is None: + return UUID(bytes=_uuid.generate_time()) if _uuid_generate_time and node is clock_seq is None: _buffer = ctypes.create_string_buffer(16) _uuid_generate_time(_buffer) @@ -555,16 +592,20 @@ return UUID(fields=(time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node), version=1) + def uuid3(namespace, name): """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" from hashlib import md5 hash = md5(namespace.bytes + bytes(name, "utf-8")).digest() return UUID(bytes=hash[:16], version=3) + def uuid4(): """Generate a random UUID.""" # When the system provides a version-4 UUID generator, use it. + if _uuid is not None: + return UUID(bytes=_uuid.generate_random()) if _uuid_generate_random: _buffer = ctypes.create_string_buffer(16) _uuid_generate_random(_buffer) @@ -579,6 +620,7 @@ bytes = bytes_(random.randrange(256) for i in range(16)) return UUID(bytes=bytes, version=4) + def uuid5(namespace, name): """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" from hashlib import sha1 diff -r 3dcbf8f928ec Modules/_uuidmodule.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_uuidmodule.c Wed Oct 28 13:29:38 2015 +0000 @@ -0,0 +1,45 @@ +#include "Python.h" +#include + +static PyObject * +_uuid_generate_random(void) +{ + uuid_t out; + uuid_generate_random(out); + return PyBytes_FromStringAndSize((const char *) out, sizeof(out)); +} + +static PyObject * +_uuid_generate_time(void) +{ + uuid_t out; + uuid_generate_time(out); + return PyBytes_FromStringAndSize((const char *) out, sizeof(out)); +} + + +static PyMethodDef uuid_methods[] = { + {"generate_random", (PyCFunction)_uuid_generate_random, METH_NOARGS, NULL}, + {"generate_time", (PyCFunction)_uuid_generate_time, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; + + +static struct PyModuleDef uuidmodule = { + PyModuleDef_HEAD_INIT, + "_uuid", + NULL, + -1, + uuid_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__uuid(void) +{ + assert(sizeof(uuid_t) == 16); + return PyModule_Create(&uuidmodule); +} diff -r 3dcbf8f928ec setup.py --- a/setup.py Wed Feb 05 10:33:14 2014 -0500 +++ b/setup.py Wed Oct 28 13:29:38 2015 +0000 @@ -1542,6 +1542,20 @@ define_macros=[('Py_LIMITED_API', '0x03040000')]) self.extensions.append(ext) + # Build the _uuid module if possible + build_uuid = False + if find_file("uuid.h", inc_dirs, ["/usr/include/uuid"]): + if self.compiler.find_library_file(lib_dirs, 'uuid'): + uuid_libs = ['uuid'] + else: + uuid_libs = [] + build_uuid = True + if build_uuid: + self.extensions.append(Extension('_uuid', ['_uuidmodule.c'], + libraries=uuid_libs)) + else: + missing.append('_uuid') + return missing def detect_tkinter_explicitly(self):