Skip to content

Commit 249d5e0

Browse files
authored
gh-153903: Use @ctypes.util.wrap_dll_function() (#154122)
1 parent 8d11eb0 commit 249d5e0

7 files changed

Lines changed: 95 additions & 57 deletions

File tree

Lib/platform.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,21 +543,25 @@ def android_ver(release="", api_level=0, manufacturer="", model="", device="",
543543
is_emulator=False):
544544
if sys.platform == "android":
545545
try:
546-
from ctypes import CDLL, c_char_p, create_string_buffer
546+
from ctypes import CDLL, c_int, c_char_p, create_string_buffer
547+
from ctypes.util import wrap_dll_function
547548
except ImportError:
548549
pass
549550
else:
550551
# An NDK developer confirmed that this is an officially-supported
551552
# API (https://stackoverflow.com/a/28416743). Use `getattr` to avoid
552553
# private name mangling.
553-
system_property_get = getattr(CDLL("libc.so"), "__system_property_get")
554-
system_property_get.argtypes = (c_char_p, c_char_p)
554+
libc = CDLL("libc.so")
555+
556+
@wrap_dll_function(libc)
557+
def __system_property_get(name: c_char_p, value: c_char_p) -> c_int:
558+
pass
555559

556560
def getprop(name, default):
557561
# https://android.googlesource.com/platform/bionic/+/refs/tags/android-5.0.0_r1/libc/include/sys/system_properties.h#39
558562
PROP_VALUE_MAX = 92
559563
buffer = create_string_buffer(PROP_VALUE_MAX)
560-
length = system_property_get(name.encode("UTF-8"), buffer)
564+
length = __system_property_get(name.encode("UTF-8"), buffer)
561565
if length == 0:
562566
# This API doesn’t distinguish between an empty property and
563567
# a missing one.

Lib/test/pythoninfo.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ def collect_windows(info_add):
995995
# windows.RtlAreLongPathsEnabled: RtlAreLongPathsEnabled()
996996
# windows.is_admin: IsUserAnAdmin()
997997
try:
998-
import ctypes
998+
import ctypes.util
999999
if not hasattr(ctypes, 'WinDLL'):
10001000
raise ImportError
10011001
except ImportError:
@@ -1004,20 +1004,19 @@ def collect_windows(info_add):
10041004
ntdll = ctypes.WinDLL('ntdll')
10051005
BOOLEAN = ctypes.c_ubyte
10061006
try:
1007-
RtlAreLongPathsEnabled = ntdll.RtlAreLongPathsEnabled
1007+
@ctypes.util.wrap_dll_function(ntdll)
1008+
def RtlAreLongPathsEnabled() -> BOOLEAN:
1009+
pass
10081010
except AttributeError:
10091011
res = '<function not available>'
10101012
else:
1011-
RtlAreLongPathsEnabled.restype = BOOLEAN
1012-
RtlAreLongPathsEnabled.argtypes = ()
10131013
res = bool(RtlAreLongPathsEnabled())
10141014
info_add('windows.RtlAreLongPathsEnabled', res)
10151015

1016-
shell32 = ctypes.windll.shell32
1017-
IsUserAnAdmin = shell32.IsUserAnAdmin
1018-
IsUserAnAdmin.restype = BOOLEAN
1019-
IsUserAnAdmin.argtypes = ()
1020-
info_add('windows.is_admin', IsUserAnAdmin())
1016+
@ctypes.util.wrap_dll_function(ctypes.windll.shell32)
1017+
def IsUserAnAdmin() -> BOOLEAN:
1018+
pass
1019+
info_add('windows.is_admin', bool(IsUserAnAdmin()))
10211020

10221021
try:
10231022
import _winapi

Lib/test/test_android.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,18 @@ def logcat_thread():
4141

4242
try:
4343
from ctypes import CDLL, c_char_p, c_int
44-
android_log_write = getattr(CDLL("liblog.so"), "__android_log_write")
45-
android_log_write.argtypes = (c_int, c_char_p, c_char_p)
46-
ANDROID_LOG_INFO = 4
44+
from ctypes.util import wrap_dll_function
45+
liblog = CDLL("liblog.so")
46+
47+
@wrap_dll_function(liblog)
48+
def __android_log_write(prio: c_int, tag: c_char_p,
49+
text: c_char_p) -> c_int:
50+
pass
4751

4852
# Separate tests using a marker line with a different tag.
53+
ANDROID_LOG_INFO = 4
4954
tag, message = "python.test", f"{self.id()} {time()}"
50-
android_log_write(
55+
__android_log_write(
5156
ANDROID_LOG_INFO, tag.encode("UTF-8"), message.encode("UTF-8"))
5257
self.assert_log("I", tag, message, skip=True)
5358
except:

Lib/test/test_embed.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,19 +1884,31 @@ def test_global_pathconfig(self):
18841884
# The global path configuration (_Py_path_config) must be a copy
18851885
# of the path configuration of PyInterpreter.config (PyConfig).
18861886
ctypes = import_helper.import_module('ctypes')
1887+
import ctypes.util # noqa: F811
18871888

1888-
def get_func(name):
1889-
func = getattr(ctypes.pythonapi, name)
1890-
func.argtypes = ()
1891-
func.restype = ctypes.c_wchar_p
1892-
return func
1893-
1894-
Py_GetPath = get_func('Py_GetPath')
1895-
Py_GetPrefix = get_func('Py_GetPrefix')
1896-
Py_GetExecPrefix = get_func('Py_GetExecPrefix')
1897-
Py_GetProgramName = get_func('Py_GetProgramName')
1898-
Py_GetProgramFullPath = get_func('Py_GetProgramFullPath')
1899-
Py_GetPythonHome = get_func('Py_GetPythonHome')
1889+
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
1890+
def Py_GetPath() -> ctypes.c_wchar_p:
1891+
pass
1892+
1893+
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
1894+
def Py_GetPrefix() -> ctypes.c_wchar_p:
1895+
pass
1896+
1897+
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
1898+
def Py_GetExecPrefix() -> ctypes.c_wchar_p:
1899+
pass
1900+
1901+
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
1902+
def Py_GetProgramName() -> ctypes.c_wchar_p:
1903+
pass
1904+
1905+
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
1906+
def Py_GetProgramFullPath() -> ctypes.c_wchar_p:
1907+
pass
1908+
1909+
@ctypes.util.wrap_dll_function(ctypes.pythonapi)
1910+
def Py_GetPythonHome() -> ctypes.c_wchar_p:
1911+
pass
19001912

19011913
config = _testinternalcapi.get_configs()['config']
19021914

Lib/test/test_ntpath.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,29 @@
3131
HAVE_GETFINALPATHNAME = True
3232

3333
try:
34-
import ctypes
34+
import ctypes.util
35+
import ctypes.wintypes
3536
except ImportError:
3637
HAVE_GETSHORTPATHNAME = False
3738
else:
3839
HAVE_GETSHORTPATHNAME = True
3940
def _getshortpathname(path):
40-
GSPN = ctypes.WinDLL("kernel32", use_last_error=True).GetShortPathNameW
41-
GSPN.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32]
42-
GSPN.restype = ctypes.c_uint32
41+
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
42+
43+
@ctypes.util.wrap_dll_function(kernel32)
44+
def GetShortPathNameW(
45+
lpszLongPath: ctypes.c_wchar_p,
46+
lpszShortPath: ctypes.c_wchar_p,
47+
cchBuffer: ctypes.wintypes.DWORD,
48+
) -> ctypes.wintypes.DWORD:
49+
pass
50+
GSPN = GetShortPathNameW
51+
4352
result_len = GSPN(path, None, 0)
4453
if not result_len:
4554
raise OSError("failed to get short path name 0x{:08X}"
4655
.format(ctypes.get_last_error()))
56+
4757
result = ctypes.create_unicode_buffer(result_len)
4858
result_len = GSPN(path, result, result_len)
4959
return result[:result_len]

Lib/test/test_os/test_windows.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,25 @@ def _kill(self, sig):
2727
# subprocess to the parent that the interpreter is ready. When it
2828
# becomes ready, send *sig* via os.kill to the subprocess and check
2929
# that the return code is equal to *sig*.
30-
import ctypes
30+
import ctypes.util
3131
from ctypes import wintypes
3232
import msvcrt
3333

3434
# Since we can't access the contents of the process' stdout until the
3535
# process has exited, use PeekNamedPipe to see what's inside stdout
3636
# without waiting. This is done so we can tell that the interpreter
3737
# is started and running at a point where it could handle a signal.
38-
PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
39-
PeekNamedPipe.restype = wintypes.BOOL
40-
PeekNamedPipe.argtypes = (wintypes.HANDLE, # Pipe handle
41-
ctypes.POINTER(ctypes.c_char), # stdout buf
42-
wintypes.DWORD, # Buffer size
43-
ctypes.POINTER(wintypes.DWORD), # bytes read
44-
ctypes.POINTER(wintypes.DWORD), # bytes avail
45-
ctypes.POINTER(wintypes.DWORD)) # bytes left
38+
@ctypes.util.wrap_dll_function(ctypes.windll.kernel32)
39+
def PeekNamedPipe(
40+
hNamedPipe: wintypes.HANDLE,
41+
lpBuffer: ctypes.POINTER(ctypes.c_char),
42+
nBufferSize: wintypes.DWORD,
43+
lpBytesRead: ctypes.POINTER(wintypes.DWORD),
44+
lpTotalBytesAvail: ctypes.POINTER(wintypes.DWORD),
45+
lpBytesLeftThisMessage: ctypes.POINTER(wintypes.DWORD),
46+
) -> wintypes.BOOL:
47+
pass
48+
4649
msg = "running"
4750
proc = subprocess.Popen([sys.executable, "-c",
4851
"import sys;"
@@ -126,10 +129,11 @@ def test_CTRL_C_EVENT(self):
126129

127130
# Make a NULL value by creating a pointer with no argument.
128131
NULL = ctypes.POINTER(ctypes.c_int)()
129-
SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler
130-
SetConsoleCtrlHandler.argtypes = (ctypes.POINTER(ctypes.c_int),
131-
wintypes.BOOL)
132-
SetConsoleCtrlHandler.restype = wintypes.BOOL
132+
133+
@ctypes.util.wrap_dll_function(ctypes.windll.kernel32)
134+
def SetConsoleCtrlHandler(HandlerRoutine: ctypes.POINTER(ctypes.c_int),
135+
Add: wintypes.BOOL) -> wintypes.BOOL:
136+
pass
133137

134138
# Calling this with NULL and FALSE causes the calling process to
135139
# handle Ctrl+C, rather than ignore it. This property is inherited
@@ -458,17 +462,20 @@ def test_getfinalpathname_handles(self):
458462
import ctypes.wintypes # noqa: F811
459463

460464
kernel = ctypes.WinDLL('Kernel32.dll', use_last_error=True)
461-
kernel.GetCurrentProcess.restype = ctypes.wintypes.HANDLE
465+
@ctypes.util.wrap_dll_function(kernel)
466+
def GetCurrentProcess() -> ctypes.wintypes.HANDLE:
467+
pass
462468

463-
kernel.GetProcessHandleCount.restype = ctypes.wintypes.BOOL
464-
kernel.GetProcessHandleCount.argtypes = (ctypes.wintypes.HANDLE,
465-
ctypes.wintypes.LPDWORD)
469+
@ctypes.util.wrap_dll_function(kernel)
470+
def GetProcessHandleCount(khProcess: ctypes.wintypes.HANDLE,
471+
pdwHandleCount: ctypes.wintypes.LPDWORD) -> ctypes.wintypes.BOOL:
472+
pass
466473

467474
# This is a pseudo-handle that doesn't need to be closed
468-
hproc = kernel.GetCurrentProcess()
475+
hproc = GetCurrentProcess()
469476

470477
handle_count = ctypes.wintypes.DWORD()
471-
ok = kernel.GetProcessHandleCount(hproc, ctypes.byref(handle_count))
478+
ok = GetProcessHandleCount(hproc, ctypes.byref(handle_count))
472479
self.assertEqual(1, ok)
473480

474481
before_count = handle_count.value

Lib/test/win_console_handler.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import sys
1616

1717
# Function prototype for the handler function. Returns BOOL, takes a DWORD.
18-
HandlerRoutine = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)
18+
HANDLER_ROUTINE = WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)
1919

2020
def _ctrl_handler(sig):
2121
"""Handle a sig event and return 0 to terminate the process"""
@@ -27,12 +27,13 @@ def _ctrl_handler(sig):
2727
print("UNKNOWN EVENT")
2828
return 0
2929

30-
ctrl_handler = HandlerRoutine(_ctrl_handler)
30+
ctrl_handler = HANDLER_ROUTINE(_ctrl_handler)
3131

3232

33-
SetConsoleCtrlHandler = ctypes.windll.kernel32.SetConsoleCtrlHandler
34-
SetConsoleCtrlHandler.argtypes = (HandlerRoutine, wintypes.BOOL)
35-
SetConsoleCtrlHandler.restype = wintypes.BOOL
33+
@ctypes.util.wrap_dll_function(ctypes.windll.kernel32)
34+
def SetConsoleCtrlHandler(HandlerRoutine: HANDLER_ROUTINE,
35+
Add: wintypes.BOOL) -> wintypes.BOOL:
36+
pass
3637

3738
if __name__ == "__main__":
3839
# Add our console control handling function with value 1

0 commit comments

Comments
 (0)