https://github.com/python/cpython/commit/249d5e0a10839f9891caa85de35a8a9cf5d7a3a3
commit: 249d5e0a10839f9891caa85de35a8a9cf5d7a3a3
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-07-19T15:12:07Z
summary:
gh-153903: Use `@ctypes.util.wrap_dll_function()` (#154122)
files:
M Lib/platform.py
M Lib/test/pythoninfo.py
M Lib/test/test_android.py
M Lib/test/test_embed.py
M Lib/test/test_ntpath.py
M Lib/test/test_os/test_windows.py
M Lib/test/win_console_handler.py
diff --git a/Lib/platform.py b/Lib/platform.py
index 36489d4fdd98ae2..3141998e02dfe21 100644
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -543,21 +543,25 @@ def android_ver(release="", api_level=0, manufacturer="",
model="", device="",
is_emulator=False):
if sys.platform == "android":
try:
- from ctypes import CDLL, c_char_p, create_string_buffer
+ from ctypes import CDLL, c_int, c_char_p, create_string_buffer
+ from ctypes.util import wrap_dll_function
except ImportError:
pass
else:
# An NDK developer confirmed that this is an officially-supported
# API (https://stackoverflow.com/a/28416743). Use `getattr` to
avoid
# private name mangling.
- system_property_get = getattr(CDLL("libc.so"),
"__system_property_get")
- system_property_get.argtypes = (c_char_p, c_char_p)
+ libc = CDLL("libc.so")
+
+ @wrap_dll_function(libc)
+ def __system_property_get(name: c_char_p, value: c_char_p) ->
c_int:
+ pass
def getprop(name, default):
#
https://android.googlesource.com/platform/bionic/+/refs/tags/android-5.0.0_r1/libc/include/sys/system_properties.h#39
PROP_VALUE_MAX = 92
buffer = create_string_buffer(PROP_VALUE_MAX)
- length = system_property_get(name.encode("UTF-8"), buffer)
+ length = __system_property_get(name.encode("UTF-8"), buffer)
if length == 0:
# This API doesn’t distinguish between an empty property
and
# a missing one.
diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py
index 3227b91bd82a863..13a3199b1f1267b 100644
--- a/Lib/test/pythoninfo.py
+++ b/Lib/test/pythoninfo.py
@@ -995,7 +995,7 @@ def collect_windows(info_add):
# windows.RtlAreLongPathsEnabled: RtlAreLongPathsEnabled()
# windows.is_admin: IsUserAnAdmin()
try:
- import ctypes
+ import ctypes.util
if not hasattr(ctypes, 'WinDLL'):
raise ImportError
except ImportError:
@@ -1004,20 +1004,19 @@ def collect_windows(info_add):
ntdll = ctypes.WinDLL('ntdll')
BOOLEAN = ctypes.c_ubyte
try:
- RtlAreLongPathsEnabled = ntdll.RtlAreLongPathsEnabled
+ @ctypes.util.wrap_dll_function(ntdll)
+ def RtlAreLongPathsEnabled() -> BOOLEAN:
+ pass
except AttributeError:
res = '<function not available>'
else:
- RtlAreLongPathsEnabled.restype = BOOLEAN
- RtlAreLongPathsEnabled.argtypes = ()
res = bool(RtlAreLongPathsEnabled())
info_add('windows.RtlAreLongPathsEnabled', res)
- shell32 = ctypes.windll.shell32
- IsUserAnAdmin = shell32.IsUserAnAdmin
- IsUserAnAdmin.restype = BOOLEAN
- IsUserAnAdmin.argtypes = ()
- info_add('windows.is_admin', IsUserAnAdmin())
+ @ctypes.util.wrap_dll_function(ctypes.windll.shell32)
+ def IsUserAnAdmin() -> BOOLEAN:
+ pass
+ info_add('windows.is_admin', bool(IsUserAnAdmin()))
try:
import _winapi
diff --git a/Lib/test/test_android.py b/Lib/test/test_android.py
index 31daafbc3d63009..201d701aff11eb2 100644
--- a/Lib/test/test_android.py
+++ b/Lib/test/test_android.py
@@ -41,13 +41,18 @@ def logcat_thread():
try:
from ctypes import CDLL, c_char_p, c_int
- android_log_write = getattr(CDLL("liblog.so"),
"__android_log_write")
- android_log_write.argtypes = (c_int, c_char_p, c_char_p)
- ANDROID_LOG_INFO = 4
+ from ctypes.util import wrap_dll_function
+ liblog = CDLL("liblog.so")
+
+ @wrap_dll_function(liblog)
+ def __android_log_write(prio: c_int, tag: c_char_p,
+ text: c_char_p) -> c_int:
+ pass
# Separate tests using a marker line with a different tag.
+ ANDROID_LOG_INFO = 4
tag, message = "python.test", f"{self.id()} {time()}"
- android_log_write(
+ __android_log_write(
ANDROID_LOG_INFO, tag.encode("UTF-8"), message.encode("UTF-8"))
self.assert_log("I", tag, message, skip=True)
except:
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 40036609a190551..1ff600e30bf4cbd 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -1884,19 +1884,31 @@ def test_global_pathconfig(self):
# The global path configuration (_Py_path_config) must be a copy
# of the path configuration of PyInterpreter.config (PyConfig).
ctypes = import_helper.import_module('ctypes')
+ import ctypes.util # noqa: F811
- def get_func(name):
- func = getattr(ctypes.pythonapi, name)
- func.argtypes = ()
- func.restype = ctypes.c_wchar_p
- return func
-
- Py_GetPath = get_func('Py_GetPath')
- Py_GetPrefix = get_func('Py_GetPrefix')
- Py_GetExecPrefix = get_func('Py_GetExecPrefix')
- Py_GetProgramName = get_func('Py_GetProgramName')
- Py_GetProgramFullPath = get_func('Py_GetProgramFullPath')
- Py_GetPythonHome = get_func('Py_GetPythonHome')
+ @ctypes.util.wrap_dll_function(ctypes.pythonapi)
+ def Py_GetPath() -> ctypes.c_wchar_p:
+ pass
+
+ @ctypes.util.wrap_dll_function(ctypes.pythonapi)
+ def Py_GetPrefix() -> ctypes.c_wchar_p:
+ pass
+
+ @ctypes.util.wrap_dll_function(ctypes.pythonapi)
+ def Py_GetExecPrefix() -> ctypes.c_wchar_p:
+ pass
+
+ @ctypes.util.wrap_dll_function(ctypes.pythonapi)
+ def Py_GetProgramName() -> ctypes.c_wchar_p:
+ pass
+
+ @ctypes.util.wrap_dll_function(ctypes.pythonapi)
+ def Py_GetProgramFullPath() -> ctypes.c_wchar_p:
+ pass
+
+ @ctypes.util.wrap_dll_function(ctypes.pythonapi)
+ def Py_GetPythonHome() -> ctypes.c_wchar_p:
+ pass
config = _testinternalcapi.get_configs()['config']
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
index a3728b58335e63b..936332bf94ffe77 100644
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -31,19 +31,29 @@
HAVE_GETFINALPATHNAME = True
try:
- import ctypes
+ import ctypes.util
+ import ctypes.wintypes
except ImportError:
HAVE_GETSHORTPATHNAME = False
else:
HAVE_GETSHORTPATHNAME = True
def _getshortpathname(path):
- GSPN = ctypes.WinDLL("kernel32", use_last_error=True).GetShortPathNameW
- GSPN.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32]
- GSPN.restype = ctypes.c_uint32
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
+
+ @ctypes.util.wrap_dll_function(kernel32)
+ def GetShortPathNameW(
+ lpszLongPath: ctypes.c_wchar_p,
+ lpszShortPath: ctypes.c_wchar_p,
+ cchBuffer: ctypes.wintypes.DWORD,
+ ) -> ctypes.wintypes.DWORD:
+ pass
+ GSPN = GetShortPathNameW
+
result_len = GSPN(path, None, 0)
if not result_len:
raise OSError("failed to get short path name 0x{:08X}"
.format(ctypes.get_last_error()))
+
result = ctypes.create_unicode_buffer(result_len)
result_len = GSPN(path, result, result_len)
return result[:result_len]
diff --git a/Lib/test/test_os/test_windows.py b/Lib/test/test_os/test_windows.py
index f1c6283f60d35ed..b21dd8a4dca6609 100644
--- a/Lib/test/test_os/test_windows.py
+++ b/Lib/test/test_os/test_windows.py
@@ -27,7 +27,7 @@ def _kill(self, sig):
# subprocess to the parent that the interpreter is ready. When it
# becomes ready, send *sig* via os.kill to the subprocess and check
# that the return code is equal to *sig*.
- import ctypes
+ import ctypes.util
from ctypes import wintypes
import msvcrt
@@ -35,14 +35,17 @@ def _kill(self, sig):
# process has exited, use PeekNamedPipe to see what's inside stdout
# without waiting. This is done so we can tell that the interpreter
# is started and running at a point where it could handle a signal.
- PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
- PeekNamedPipe.restype = wintypes.BOOL
- PeekNamedPipe.argtypes = (wintypes.HANDLE, # Pipe handle
- ctypes.POINTER(ctypes.c_char), # stdout buf
- wintypes.DWORD, # Buffer size
- ctypes.POINTER(wintypes.DWORD), # bytes read
- ctypes.POINTER(wintypes.DWORD), # bytes avail
- ctypes.POINTER(wintypes.DWORD)) # bytes left
+ @ctypes.util.wrap_dll_function(ctypes.windll.kernel32)
+ def PeekNamedPipe(
+ hNamedPipe: wintypes.HANDLE,
+ lpBuffer: ctypes.POINTER(ctypes.c_char),
+ nBufferSize: wintypes.DWORD,
+ lpBytesRead: ctypes.POINTER(wintypes.DWORD),
+ lpTotalBytesAvail: ctypes.POINTER(wintypes.DWORD),
+ lpBytesLeftThisMessage: ctypes.POINTER(wintypes.DWORD),
+ ) -> wintypes.BOOL:
+ pass
+
msg = "running"
proc = subprocess.Popen([sys.executable, "-c",
"import sys;"
@@ -126,10 +129,11 @@ def test_CTRL_C_EVENT(self):
# Make a NULL value by creating a pointer with no argument.
NULL = ctypes.POINTER(ctypes.c_int)()
- SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler
- SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_int),
- wintypes.BOOL)
- SetConsoleCtrlHandler.restype = wintypes.BOOL
+
+ @ctypes.util.wrap_dll_function(ctypes.windll.kernel32)
+ def SetConsoleCtrlHandler(HandlerRoutine: ctypes.POINTER(ctypes.c_int),
+ Add: wintypes.BOOL) -> wintypes.BOOL:
+ pass
# Calling this with NULL and FALSE causes the calling process to
# handle Ctrl+C, rather than ignore it. This property is inherited
@@ -458,17 +462,20 @@ def test_getfinalpathname_handles(self):
import ctypes.wintypes # noqa: F811
kernel = ctypes.WinDLL('Kernel32.dll', use_last_error=True)
- kernel.GetCurrentProcess.restype = ctypes.wintypes.HANDLE
+ @ctypes.util.wrap_dll_function(kernel)
+ def GetCurrentProcess() -> ctypes.wintypes.HANDLE:
+ pass
- kernel.GetProcessHandleCount.restype = ctypes.wintypes.BOOL
- kernel.GetProcessHandleCount.argtypes = (ctypes.wintypes.HANDLE,
- ctypes.wintypes.LPDWORD)
+ @ctypes.util.wrap_dll_function(kernel)
+ def GetProcessHandleCount(khProcess: ctypes.wintypes.HANDLE,
+ pdwHandleCount: ctypes.wintypes.LPDWORD) ->
ctypes.wintypes.BOOL:
+ pass
# This is a pseudo-handle that doesn't need to be closed
- hproc = kernel.GetCurrentProcess()
+ hproc = GetCurrentProcess()
handle_count = ctypes.wintypes.DWORD()
- ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count))
+ ok = GetProcessHandleCount(hproc, ctypes.byref(handle_count))
self.assertEqual(1, ok)
before_count = handle_count.value
diff --git a/Lib/test/win_console_handler.py b/Lib/test/win_console_handler.py
index e7779b9363503b4..a1c7c42c109330b 100644
--- a/Lib/test/win_console_handler.py
+++ b/Lib/test/win_console_handler.py
@@ -15,7 +15,7 @@
import sys
# Function prototype for the handler function. Returns BOOL, takes a DWORD.
-HandlerRoutine = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)
+HANDLER_ROUTINE = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)
def _ctrl_handler(sig):
"""Handle a sig event and return 0 to terminate the process"""
@@ -27,12 +27,13 @@ def _ctrl_handler(sig):
print("UNKNOWN EVENT")
return 0
-ctrl_handler = HandlerRoutine(_ctrl_handler)
+ctrl_handler = HANDLER_ROUTINE(_ctrl_handler)
-SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler
-SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL)
-SetConsoleCtrlHandler.restype = wintypes.BOOL
[email protected]_dll_function(ctypes.windll.kernel32)
+def SetConsoleCtrlHandler(HandlerRoutine: HANDLER_ROUTINE,
+ Add: wintypes.BOOL) -> wintypes.BOOL:
+ pass
if __name__ == "__main__":
# Add our console control handling function with value 1
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]