diff options
author | Antoine Pitrou <pitrou@free.fr> | 2017-09-28 21:03:06 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-28 21:03:06 (GMT) |
commit | a106aec2ed6ba171838ca7e6ba43c4e722bbecd1 (patch) | |
tree | 2a242b9061198d40f40d2921818e6da8089a9b50 /Lib/uuid.py | |
parent | 8d59aca4a953b097a9b02b0ecafef840e4ac5855 (diff) | |
download | cpython-a106aec2ed6ba171838ca7e6ba43c4e722bbecd1.zip cpython-a106aec2ed6ba171838ca7e6ba43c4e722bbecd1.tar.gz cpython-a106aec2ed6ba171838ca7e6ba43c4e722bbecd1.tar.bz2 |
bpo-11063, bpo-20519: avoid ctypes and improve import time for uuid (#3796)
bpo-11063, bpo-20519: avoid ctypes and improve import time for uuid.
Diffstat (limited to 'Lib/uuid.py')
-rw-r--r-- | Lib/uuid.py | 176 |
1 files changed, 108 insertions, 68 deletions
diff --git a/Lib/uuid.py b/Lib/uuid.py index 15a81f5..b2fbd38 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -45,6 +45,7 @@ Typical usage: """ import os +import sys from enum import Enum @@ -475,73 +476,112 @@ def _netbios_getnode(): continue return int.from_bytes(bytes, 'big') -# Thanks to Thomas Heller for ctypes and for his help with its use here. -# If ctypes is available, use it to find system routines for UUID generation. -# XXX This makes the module non-thread-safe! -_uuid_generate_time = _UuidCreate = None +_generate_time_safe = _UuidCreate = None +_has_uuid_generate_time_safe = None + +# Import optional C extension at toplevel, to help disabling it when testing try: - import ctypes, ctypes.util - import sys - - # The uuid_generate_* routines are provided by libuuid on at least - # Linux and FreeBSD, and provided by libc on Mac OS X. - _libnames = ['uuid'] - if not sys.platform.startswith('win'): - _libnames.append('c') - for libname in _libnames: - try: - lib = ctypes.CDLL(ctypes.util.find_library(libname)) - except Exception: # pragma: nocover - continue - # Try to find the safe variety first. - if hasattr(lib, 'uuid_generate_time_safe'): - _uuid_generate_time = lib.uuid_generate_time_safe - # int uuid_generate_time_safe(uuid_t out); - break - elif hasattr(lib, 'uuid_generate_time'): # pragma: nocover - _uuid_generate_time = lib.uuid_generate_time - # void uuid_generate_time(uuid_t out); - _uuid_generate_time.restype = None - break - del _libnames - - # 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_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. + import _uuid +except ImportError: + _uuid = None + + +def _load_system_functions(): + """ + Try to load platform-specific functions for generating uuids. + """ + global _generate_time_safe, _UuidCreate, _has_uuid_generate_time_safe + + if _has_uuid_generate_time_safe is not None: + return + + _has_uuid_generate_time_safe = False + + 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 + # 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. + pass + elif _uuid is not None: + _generate_time_safe = _uuid.generate_time_safe + _has_uuid_generate_time_safe = True + return + try: - lib = ctypes.windll.rpcrt4 - except: - lib = None - _UuidCreate = getattr(lib, 'UuidCreateSequential', - getattr(lib, 'UuidCreate', None)) -except: - pass - -def _unixdll_getnode(): - """Get the hardware address on Unix using ctypes.""" - _buffer = ctypes.create_string_buffer(16) - _uuid_generate_time(_buffer) - return UUID(bytes=bytes_(_buffer.raw)).node + # If we couldn't find an extension module, try ctypes to find + # system routines for UUID generation. + # Thanks to Thomas Heller for ctypes and for his help with its use here. + 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. + _libnames = ['uuid'] + if not sys.platform.startswith('win'): + _libnames.append('c') + for libname in _libnames: + try: + lib = ctypes.CDLL(ctypes.util.find_library(libname)) + except Exception: # pragma: nocover + continue + # Try to find the safe variety first. + if hasattr(lib, 'uuid_generate_time_safe'): + _uuid_generate_time_safe = lib.uuid_generate_time_safe + # int uuid_generate_time_safe(uuid_t out); + def _generate_time_safe(): + _buffer = ctypes.create_string_buffer(16) + res = _uuid_generate_time_safe(_buffer) + return bytes(_buffer.raw), res + _has_uuid_generate_time_safe = True + break + + elif hasattr(lib, 'uuid_generate_time'): # pragma: nocover + _uuid_generate_time = lib.uuid_generate_time + # void uuid_generate_time(uuid_t out); + _uuid_generate_time.restype = None + def _generate_time_safe(): + _buffer = ctypes.create_string_buffer(16) + _uuid_generate_time(_buffer) + return bytes(_buffer.raw), None + break + + # 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 exc: + import warnings + warnings.warn(f"Could not find fallback ctypes uuid functions: {exc}", + ImportWarning) + + +def _unix_getnode(): + """Get the hardware address on Unix using the _uuid extension module + or ctypes.""" + _load_system_functions() + uuid_time, _ = _generate_time_safe() + return UUID(bytes=uuid_time).node def _windll_getnode(): """Get the hardware address on Windows using ctypes.""" + import ctypes + _load_system_functions() _buffer = ctypes.create_string_buffer(16) if _UuidCreate(_buffer) == 0: return UUID(bytes=bytes_(_buffer.raw)).node @@ -551,6 +591,7 @@ def _random_getnode(): import random return random.getrandbits(48) | 0x010000000000 + _node = None def getnode(): @@ -561,16 +602,14 @@ def getnode(): choose a random 48-bit number with its eighth bit set to 1 as recommended in RFC 4122. """ - global _node if _node is not None: return _node - import sys if sys.platform == 'win32': getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] else: - getters = [_unixdll_getnode, _ifconfig_getnode, _ip_getnode, + getters = [_unix_getnode, _ifconfig_getnode, _ip_getnode, _arp_getnode, _lanscan_getnode, _netstat_getnode] for getter in getters + [_random_getnode]: @@ -581,6 +620,7 @@ def getnode(): if _node is not None: return _node + _last_timestamp = None def uuid1(node=None, clock_seq=None): @@ -591,14 +631,14 @@ def uuid1(node=None, clock_seq=None): # 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_generate_time and node is clock_seq is None: - _buffer = ctypes.create_string_buffer(16) - safely_generated = _uuid_generate_time(_buffer) + _load_system_functions() + if _generate_time_safe is not None and node is clock_seq is None: + uuid_time, safely_generated = _generate_time_safe() try: is_safe = SafeUUID(safely_generated) except ValueError: is_safe = SafeUUID.unknown - return UUID(bytes=bytes_(_buffer.raw), is_safe=is_safe) + return UUID(bytes=uuid_time, is_safe=is_safe) global _last_timestamp import time |