summaryrefslogtreecommitdiffstats
path: root/Lib/uuid.py
diff options
context:
space:
mode:
authorAntoine Pitrou <pitrou@free.fr>2017-09-28 21:03:06 (GMT)
committerGitHub <noreply@github.com>2017-09-28 21:03:06 (GMT)
commita106aec2ed6ba171838ca7e6ba43c4e722bbecd1 (patch)
tree2a242b9061198d40f40d2921818e6da8089a9b50 /Lib/uuid.py
parent8d59aca4a953b097a9b02b0ecafef840e4ac5855 (diff)
downloadcpython-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.py176
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