← 返回首页
bpo-40501: Replace ctypes code in uuid with native module by zooba · Pull Request #19948 · python/cpython · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .c  (1) .filters  (1) .proj  (1) .py  (2) .rst  (1) .sln  (1) .vcxproj  (1) .wxs  (1) All 8 file types selected Only manifest files Viewed files
Conversations
Failed to load comments. Retry
Loading
Jump to
Jump to file
Failed to load files. Retry
Loading
Diff view
Unified
Split
Hide whitespace
Apply and reload
Show whitespace
Diff view
Unified
Split
Hide whitespace
Apply and reload
27 changes: 7 additions & 20 deletions Lib/test/test_uuid.py
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -852,17 +852,6 @@ def test_netstat_getnode(self):
node = self.uuid._netstat_getnode()
self.check_node(node, 'netstat')

@unittest.skipUnless(os.name == 'nt', 'requires Windows')
def test_ipconfig_getnode(self):
node = self.uuid._ipconfig_getnode()
self.check_node(node, 'ipconfig')

@unittest.skipUnless(importable('win32wnet'), 'requires win32wnet')
@unittest.skipUnless(importable('netbios'), 'requires netbios')
def test_netbios_getnode(self):
node = self.uuid._netbios_getnode()
self.check_node(node)

def test_random_getnode(self):
node = self.uuid._random_getnode()
# The multicast bit, i.e. the least significant bit of first octet,
Expand All @@ -874,6 +863,13 @@ def test_random_getnode(self):
node2 = self.uuid._random_getnode()
self.assertNotEqual(node2, node, '%012x' % node)

class TestInternalsWithoutExtModule(BaseTestInternals, unittest.TestCase):
uuid = py_uuid

@unittest.skipUnless(c_uuid, 'requires the C _uuid module')
class TestInternalsWithExtModule(BaseTestInternals, unittest.TestCase):
uuid = c_uuid

@unittest.skipUnless(os.name == 'posix', 'requires Posix')
def test_unix_getnode(self):
if not importable('_uuid') and not importable('ctypes'):
Expand All @@ -885,19 +881,10 @@ def test_unix_getnode(self):
self.check_node(node, 'unix')

@unittest.skipUnless(os.name == 'nt', 'requires Windows')
@unittest.skipUnless(importable('ctypes'), 'requires ctypes')
def test_windll_getnode(self):
node = self.uuid._windll_getnode()
self.check_node(node)


class TestInternalsWithoutExtModule(BaseTestInternals, unittest.TestCase):
uuid = py_uuid

@unittest.skipUnless(c_uuid, 'requires the C _uuid module')
class TestInternalsWithExtModule(BaseTestInternals, unittest.TestCase):
uuid = c_uuid


if __name__ == '__main__':
unittest.main()
180 changes: 23 additions & 157 deletions Lib/uuid.py
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -555,178 +555,44 @@ def _netstat_getnode():
return _find_mac_under_heading('netstat', '-ian', b'Address')

def _ipconfig_getnode():
"""Get the hardware address on Windows by running ipconfig.exe."""
import os, re, subprocess
first_local_mac = None
dirs = ['', r'c:\windows\system32', r'c:\winnt\system32']
try:
import ctypes
buffer = ctypes.create_string_buffer(300)
ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300)
dirs.insert(0, buffer.value.decode('mbcs'))
except:
pass
for dir in dirs:
try:
proc = subprocess.Popen([os.path.join(dir, 'ipconfig'), '/all'],
stdout=subprocess.PIPE,
encoding="oem")
except OSError:
continue
with proc:
for line in proc.stdout:
value = line.split(':')[-1].strip().lower()
if re.fullmatch('(?:[0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
mac = int(value.replace('-', ''), 16)
if _is_universal(mac):
return mac
first_local_mac = first_local_mac or mac
return first_local_mac or None
"""[DEPRECATED] Get the hardware address on Windows."""
# bpo-40501: UuidCreateSequential() is now the only supported approach
return _windll_getnode()

def _netbios_getnode():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Why not simply removing _ipconfig_getnode and _netbios_getnode? They are private functions, there is no backward compatibility warranty.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality Hide comment

Last time I was involved in one of those people still broke. I'd rather leave them there as long as they aren't hurting anyone.

"""Get the hardware address on Windows using NetBIOS calls.
See http://support.microsoft.com/kb/118623 for details."""
import win32wnet, netbios
first_local_mac = None
ncb = netbios.NCB()
ncb.Command = netbios.NCBENUM
ncb.Buffer = adapters = netbios.LANA_ENUM()
adapters._pack()
if win32wnet.Netbios(ncb) != 0:
return None
adapters._unpack()
for i in range(adapters.length):
ncb.Reset()
ncb.Command = netbios.NCBRESET
ncb.Lana_num = ord(adapters.lana[i])
if win32wnet.Netbios(ncb) != 0:
continue
ncb.Reset()
ncb.Command = netbios.NCBASTAT
ncb.Lana_num = ord(adapters.lana[i])
ncb.Callname = '*'.ljust(16)
ncb.Buffer = status = netbios.ADAPTER_STATUS()
if win32wnet.Netbios(ncb) != 0:
continue
status._unpack()
bytes = status.adapter_address[:6]
if len(bytes) != 6:
continue
mac = int.from_bytes(bytes, 'big')
if _is_universal(mac):
return mac
first_local_mac = first_local_mac or mac
return first_local_mac or None
"""[DEPRECATED] Get the hardware address on Windows."""
# bpo-40501: UuidCreateSequential() is now the only supported approach
return _windll_getnode()


_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 _uuid
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
_UuidCreate = getattr(_uuid, "UuidCreate", None)
_has_uuid_generate_time_safe = _uuid.has_uuid_generate_time_safe
except ImportError:
_uuid = None
_generate_time_safe = None
_UuidCreate = None
_has_uuid_generate_time_safe = 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 = _uuid.has_uuid_generate_time_safe
return

try:
# 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)
"""[DEPRECATED] Platform-specific functions loaded at import time"""


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
"""Get the hardware address on Unix using the _uuid extension module."""
if _generate_time_safe:
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
"""Get the hardware address on Windows using the _uuid extension module."""
if _UuidCreate:
uuid_bytes = _UuidCreate()
return UUID(bytes_le=uuid_bytes).node

def _random_getnode():
"""Get a random node ID."""
Expand Down Expand Up @@ -755,7 +621,8 @@ def _random_getnode():
elif _DARWIN:
_OS_GETTERS = [_ifconfig_getnode, _arp_getnode, _netstat_getnode]
elif _WINDOWS:
_OS_GETTERS = [_netbios_getnode, _ipconfig_getnode]
# bpo-40201: _windll_getnode will always succeed, so these are not needed
_OS_GETTERS = []
elif _AIX:
_OS_GETTERS = [_netstat_getnode]
else:
Expand Down Expand Up @@ -802,7 +669,6 @@ 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).
_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:
Expand Down
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:mod:`uuid` no longer uses :mod:`ctypes` to load :file:`libuuid` or
:file:`rpcrt4.dll` at runtime.
54 changes: 49 additions & 5 deletions Modules/_uuidmodule.c
Show comments View file Edit file Delete file Open in desktop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Python UUID module that wraps libuuid -
* Python UUID module that wraps libuuid or Windows rpcrt4.dll.
* DCE compatible Universally Unique Identifier library.
*/

Expand All @@ -12,6 +12,12 @@
#include <uuid.h>
#endif

#ifdef MS_WINDOWS
#include <rpc.h>
#endif

#ifndef MS_WINDOWS

static PyObject *
py_uuid_generate_time_safe(PyObject *Py_UNUSED(context),
PyObject *Py_UNUSED(ignored))
Expand All @@ -31,17 +37,50 @@ py_uuid_generate_time_safe(PyObject *Py_UNUSED(context),
return Py_BuildValue("y#i", buf, sizeof(uuid), (int) status);
# else
return Py_BuildValue("y#i", (const char *) &uuid, sizeof(uuid), (int) status);
# endif
#else
# endif /* HAVE_UUID_CREATE */
#else /* HAVE_UUID_GENERATE_TIME_SAFE */
uuid_generate_time(uuid);
return Py_BuildValue("y#O", (const char *) uuid, sizeof(uuid), Py_None);
#endif
#endif /* HAVE_UUID_GENERATE_TIME_SAFE */
}

#else /* MS_WINDOWS */

static PyObject *
py_UuidCreate(PyObject *Py_UNUSED(context),
PyObject *Py_UNUSED(ignored))
{
UUID uuid;
RPC_STATUS res;

Py_BEGIN_ALLOW_THREADS
res = UuidCreateSequential(&uuid);
Py_END_ALLOW_THREADS

switch (res) {
case RPC_S_OK:
case RPC_S_UUID_LOCAL_ONLY:
case RPC_S_UUID_NO_ADDRESS:
/*
All success codes, but the latter two indicate that the UUID is random
rather than based on the MAC address. If the OS can't figure this out,
neither can we, so we'll take it anyway.
*/
return Py_BuildValue("y#", (const char *)&uuid, sizeof(uuid));
}
PyErr_SetFromWindowsErr(res);
return NULL;
}

#endif /* MS_WINDOWS */


static int
uuid_exec(PyObject *module) {
assert(sizeof(uuid_t) == 16);
#ifdef HAVE_UUID_GENERATE_TIME_SAFE
#if defined(MS_WINDOWS)
int has_uuid_generate_time_safe = 0;
#elif defined(HAVE_UUID_GENERATE_TIME_SAFE)
int has_uuid_generate_time_safe = 1;
#else
int has_uuid_generate_time_safe = 0;
Expand All @@ -54,7 +93,12 @@ uuid_exec(PyObject *module) {
}

static PyMethodDef uuid_methods[] = {
#if defined(HAVE_UUID_UUID_H) || defined(HAVE_UUID_H)
{"generate_time_safe", py_uuid_generate_time_safe, METH_NOARGS, NULL},
#endif
#if defined(MS_WINDOWS)
{"UuidCreate", py_UuidCreate, METH_NOARGS, NULL},
#endif
{NULL, NULL, 0, NULL} /* sentinel */
};

Expand Down
Loading
Toggle all file notes Toggle all file annotations

Footer

© 2026 GitHub, Inc.