From 327ee2c7c9af2f17882e1dc0b41cdbe6be32a7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Wed, 20 Sep 2017 23:13:40 +0200 Subject: [PATCH 1/4] optimize paths for finding uuid_generate_time optimizes uuid ctypes initialization for BSD and Darwin --- Lib/uuid.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/Lib/uuid.py b/Lib/uuid.py index 15a81f5c18b66b..3dda13eacc248e 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -481,14 +481,29 @@ def _netbios_getnode(): # XXX This makes the module non-thread-safe! _uuid_generate_time = _UuidCreate = None try: - import ctypes, ctypes.util import sys + # 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 + raise NotImplementedError("Not supported on OSX >= 10.5") - # The uuid_generate_* routines are provided by libuuid on at least - # Linux and FreeBSD, and provided by libc on Mac OS X. + import ctypes + import ctypes.util + # The uuid_generate_* routines are provided by libuuid - at least + # on Linux. On FreeBSD and OS X they are provided by libc _libnames = ['uuid'] if not sys.platform.startswith('win'): - _libnames.append('c') + if 'bsd' in sys.platform: + _libnames.insert(0, 'c') + else: + _libnames.append('c') for libname in _libnames: try: lib = ctypes.CDLL(ctypes.util.find_library(libname)) @@ -506,17 +521,6 @@ def _netbios_getnode(): 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 From aaf146032f9362e3995c0d4990b21bf66a2f058c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=B6nke?= Date: Sun, 24 Sep 2017 14:03:00 +0200 Subject: [PATCH 2/4] lazy-load uuid_generate_time _uuid_generate_time is lazy-loaded on first use to speed up initialization of applications depending on other uuid features --- Lib/uuid.py | 144 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/Lib/uuid.py b/Lib/uuid.py index 3dda13eacc248e..0ca556a46acf4b 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -45,7 +45,7 @@ """ import os - +import sys from enum import Enum @@ -475,78 +475,102 @@ def _netbios_getnode(): continue return int.from_bytes(bytes, 'big') +def _is_darwin_greater_than_version(major_version_number): + """Checks if the operating system is Darwin newer than + the specified major version number.""" + if sys.platform != "darwin": + return False + return int(os.uname().release.split(".")[0]) >= major_version_number + +# 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. +_no_uuid_generate_time_lookup = _is_darwin_greater_than_version(9) +_uuid_generate_time = _UuidCreate = None + # 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 -try: - import sys - # 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 - raise NotImplementedError("Not supported on OSX >= 10.5") +def _get_uuid_generate_time(): + """Lazy-loading of _uuid_generate_time.""" + global _no_uuid_generate_time_lookup + global _uuid_generate_time + + if _no_uuid_generate_time_lookup or (_uuid_generate_time is not None): + return _uuid_generate_time + + # After looking up _uuid_generate_time once, the result is memoized + _no_uuid_generate_time_lookup = True - import ctypes - import ctypes.util - # The uuid_generate_* routines are provided by libuuid - at least - # on Linux. On FreeBSD and OS X they are provided by libc - _libnames = ['uuid'] - if not sys.platform.startswith('win'): - if 'bsd' in sys.platform: - _libnames.insert(0, 'c') - else: - _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 - - # 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 + import ctypes + import ctypes.util + # The uuid_generate_* routines are provided by libuuid - at least + # on Linux. On FreeBSD and OS X they are provided by libc + _libnames = ['uuid'] + if not sys.platform.startswith('win'): + if 'bsd' in sys.platform: + _libnames.insert(0, 'c') + else: + _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 + + # 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 AttributeError: + lib = None + global _UuidCreate + _UuidCreate = getattr( + lib, + 'UuidCreateSequential', + getattr(lib, 'UuidCreate', None) + ) except: - lib = None - _UuidCreate = getattr(lib, 'UuidCreateSequential', - getattr(lib, 'UuidCreate', None)) -except: - pass + pass + + return _uuid_generate_time def _unixdll_getnode(): """Get the hardware address on Unix using ctypes.""" + import ctypes _buffer = ctypes.create_string_buffer(16) - _uuid_generate_time(_buffer) + uuid_generate_time = _get_uuid_generate_time() + uuid_generate_time(_buffer) return UUID(bytes=bytes_(_buffer.raw)).node def _windll_getnode(): """Get the hardware address on Windows using ctypes.""" + import ctypes _buffer = ctypes.create_string_buffer(16) + _get_uuid_generate_time() if _UuidCreate(_buffer) == 0: return UUID(bytes=bytes_(_buffer.raw)).node @@ -595,9 +619,11 @@ 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: + uuid_generate_time = _get_uuid_generate_time() + if uuid_generate_time and node is clock_seq is None: + import ctypes _buffer = ctypes.create_string_buffer(16) - safely_generated = _uuid_generate_time(_buffer) + safely_generated = uuid_generate_time(_buffer) try: is_safe = SafeUUID(safely_generated) except ValueError: From 971ff5ab3ef4e5002958515770ac2694792e2a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Thu, 21 Sep 2017 14:14:36 +0200 Subject: [PATCH 3/4] bpo-5885: add Misc/NEWS entry --- .../next/Library/2017-09-21-14-14-36.bpo-5885.Aquai1.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2017-09-21-14-14-36.bpo-5885.Aquai1.rst diff --git a/Misc/NEWS.d/next/Library/2017-09-21-14-14-36.bpo-5885.Aquai1.rst b/Misc/NEWS.d/next/Library/2017-09-21-14-14-36.bpo-5885.Aquai1.rst new file mode 100644 index 00000000000000..6a34a046811e2d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-21-14-14-36.bpo-5885.Aquai1.rst @@ -0,0 +1,4 @@ +uuid_generate_time is now lazily discovered and loaded. This ensures that +libraries and programs which load uuid as (transient) dependency are not +performance-impacted, unless they actually use the function ``uuid.uuid1()`` +that needs it. Patch by Stefan Grönke & Igor Galić From 673e6c62881cd64882e0302ee1ba2056a3dcb957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=B6nke?= Date: Sun, 24 Sep 2017 14:03:13 +0200 Subject: [PATCH 4/4] import ctypes to uuid on module-level --- Lib/uuid.py | 174 ++++++++++++++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/Lib/uuid.py b/Lib/uuid.py index 0ca556a46acf4b..7c11934c45ca9a 100644 --- a/Lib/uuid.py +++ b/Lib/uuid.py @@ -366,6 +366,94 @@ def _find_mac(command, args, hw_identifiers, get_index): except OSError: pass +def _is_darwin_greater_than_version(major_version_number): + """Checks if the operating system is Darwin newer than + the specified major version number.""" + if sys.platform != "darwin": + return False + return int(os.uname().release.split(".")[0]) >= major_version_number + +# 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. +try: + import ctypes +except BaseException: + _no_uuid_generate_time_lookup = True +else: + _no_uuid_generate_time_lookup = _is_darwin_greater_than_version(9) + +_uuid_generate_time = _UuidCreate = None + +# 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! +def _get_uuid_generate_time(): + """Lazy-loading of _uuid_generate_time.""" + global _no_uuid_generate_time_lookup + global _uuid_generate_time + + if _no_uuid_generate_time_lookup or (_uuid_generate_time is not None): + return _uuid_generate_time + + # After looking up _uuid_generate_time once, the result is memoized + _no_uuid_generate_time_lookup = True + + try: + import ctypes.util + # The uuid_generate_* routines are provided by libuuid - at least + # on Linux. On FreeBSD and OS X they are provided by libc + _libnames = ['uuid'] + if not sys.platform.startswith('win'): + if 'bsd' in sys.platform: + _libnames.insert(0, 'c') + else: + _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 + + # 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 AttributeError: + lib = None + global _UuidCreate + _UuidCreate = getattr( + lib, + 'UuidCreateSequential', + getattr(lib, 'UuidCreate', None) + ) + except: + pass + + return _uuid_generate_time + def _ifconfig_getnode(): """Get the hardware address on Unix by running ifconfig.""" # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes. @@ -475,92 +563,8 @@ def _netbios_getnode(): continue return int.from_bytes(bytes, 'big') -def _is_darwin_greater_than_version(major_version_number): - """Checks if the operating system is Darwin newer than - the specified major version number.""" - if sys.platform != "darwin": - return False - return int(os.uname().release.split(".")[0]) >= major_version_number - -# 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. -_no_uuid_generate_time_lookup = _is_darwin_greater_than_version(9) -_uuid_generate_time = _UuidCreate = None - -# 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! -def _get_uuid_generate_time(): - """Lazy-loading of _uuid_generate_time.""" - global _no_uuid_generate_time_lookup - global _uuid_generate_time - - if _no_uuid_generate_time_lookup or (_uuid_generate_time is not None): - return _uuid_generate_time - - # After looking up _uuid_generate_time once, the result is memoized - _no_uuid_generate_time_lookup = True - - try: - import ctypes - import ctypes.util - # The uuid_generate_* routines are provided by libuuid - at least - # on Linux. On FreeBSD and OS X they are provided by libc - _libnames = ['uuid'] - if not sys.platform.startswith('win'): - if 'bsd' in sys.platform: - _libnames.insert(0, 'c') - else: - _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 - - # 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 AttributeError: - lib = None - global _UuidCreate - _UuidCreate = getattr( - lib, - 'UuidCreateSequential', - getattr(lib, 'UuidCreate', None) - ) - except: - pass - - return _uuid_generate_time - def _unixdll_getnode(): """Get the hardware address on Unix using ctypes.""" - import ctypes _buffer = ctypes.create_string_buffer(16) uuid_generate_time = _get_uuid_generate_time() uuid_generate_time(_buffer) @@ -568,7 +572,6 @@ def _unixdll_getnode(): def _windll_getnode(): """Get the hardware address on Windows using ctypes.""" - import ctypes _buffer = ctypes.create_string_buffer(16) _get_uuid_generate_time() if _UuidCreate(_buffer) == 0: @@ -621,7 +624,6 @@ def uuid1(node=None, clock_seq=None): # use UuidCreate here because its UUIDs don't conform to RFC 4122). uuid_generate_time = _get_uuid_generate_time() if uuid_generate_time and node is clock_seq is None: - import ctypes _buffer = ctypes.create_string_buffer(16) safely_generated = uuid_generate_time(_buffer) try: