https://github.com/python/cpython/commit/ee50ed3ed010e26495300778b30b57da5cadc933
commit: ee50ed3ed010e26495300778b30b57da5cadc933
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-07-19T12:08:56Z
summary:
gh-104533: Use `@ctypes.util.struct` decorator (#154031)
Use also `@ctypes.util.wrap_dll_function()` decorator.
files:
M Lib/test/_test_multiprocessing.py
M Lib/test/support/__init__.py
M Lib/test/test_buffer.py
M Lib/test/test_codecs.py
M Lib/test/test_ctypes/test_refcounts.py
M Lib/test/test_ctypes/test_unicode.py
M Lib/test/test_ctypes/test_win32_com_foreign_func.py
M Lib/test/test_io/utils.py
diff --git a/Lib/test/_test_multiprocessing.py
b/Lib/test/_test_multiprocessing.py
index 115a187a8a85882..8f665b3a98a0371 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -161,9 +161,10 @@ def wait_for_handle(handle, timeout):
#
try:
- from ctypes import Structure, c_int, c_double, c_longlong
+ from ctypes.util import struct as ctypes_struct
+ from ctypes import c_int, c_double, c_longlong
except ImportError:
- Structure = object
+ def ctypes_struct(cls): return cls
c_int = c_double = c_longlong = None
@@ -4390,12 +4391,11 @@ def test_free_from_gc(self):
#
#
-class _Foo(Structure):
- _fields_ = [
- ('x', c_int),
- ('y', c_double),
- ('z', c_longlong,)
- ]
+@ctypes_struct
+class _Foo:
+ x: c_int
+ y: c_double
+ z: c_longlong
class _TestSharedCTypes(BaseTestCase):
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index d17d9a2ecf8d9b0..e5bc2fd9b3d0954 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -236,20 +236,41 @@ def _is_gui_available():
# if Python is running as a service (such as the buildbot service),
# gui interaction may be disallowed
import ctypes
+ import ctypes.util
import ctypes.wintypes
+
UOI_FLAGS = 1
WSF_VISIBLE = 0x0001
- class USEROBJECTFLAGS(ctypes.Structure):
- _fields_ = [("fInherit", ctypes.wintypes.BOOL),
- ("fReserved", ctypes.wintypes.BOOL),
- ("dwFlags", ctypes.wintypes.DWORD)]
- dll = ctypes.windll.user32
- h = dll.GetProcessWindowStation()
+
+ @ctypes.util.struct
+ class USEROBJECTFLAGS:
+ fInherit: ctypes.wintypes.BOOL
+ fReserved: ctypes.wintypes.BOOL
+ dwFlags: ctypes.wintypes.DWORD
+
+ user32 = ctypes.windll.user32
+
+ @ctypes.util.wrap_dll_function(user32)
+ def GetProcessWindowStation() -> ctypes.wintypes.HANDLE:
+ ...
+
+ h = GetProcessWindowStation()
if not h:
raise ctypes.WinError()
+
+ @ctypes.util.wrap_dll_function(user32)
+ def GetUserObjectInformationW(
+ hObj: ctypes.wintypes.HANDLE,
+ nIndex: ctypes.c_int,
+ pvInfo: ctypes.c_void_p,
+ nLength: ctypes.wintypes.DWORD,
+ lpnLengthNeeded: ctypes.POINTER(ctypes.wintypes.DWORD),
+ ) -> ctypes.wintypes.BOOL:
+ ...
+
uof = USEROBJECTFLAGS()
needed = ctypes.wintypes.DWORD()
- res = dll.GetUserObjectInformationW(h,
+ res = GetUserObjectInformationW(h,
UOI_FLAGS,
ctypes.byref(uof),
ctypes.sizeof(uof),
diff --git a/Lib/test/test_buffer.py b/Lib/test/test_buffer.py
index 3213a4751273431..453dafe2709eb2f 100644
--- a/Lib/test/test_buffer.py
+++ b/Lib/test/test_buffer.py
@@ -37,6 +37,7 @@
try:
import ctypes
+ import ctypes.util
except ImportError:
ctypes = None
@@ -2849,8 +2850,11 @@ def test_memoryview_cast_1D_ND(self):
if ctypes:
# format: "T{>l:x:>d:y:}"
- class BEPoint(ctypes.BigEndianStructure):
- _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_double)]
+ @ctypes.util.struct(endian='big')
+ class BEPoint:
+ x: ctypes.c_long
+ y: ctypes.c_double
+
point = BEPoint(100, 200.1)
m1 = memoryview(point)
m2 = m1.cast('B')
@@ -3250,8 +3254,11 @@ def test_memoryview_compare_special_cases(self):
# Some ctypes format strings are unknown to the struct module.
if ctypes:
# format: "T{>l:x:>l:y:}"
- class BEPoint(ctypes.BigEndianStructure):
- _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)]
+ @ctypes.util.struct(endian='big')
+ class BEPoint:
+ x: ctypes.c_long
+ y: ctypes.c_long
+
point = BEPoint(100, 200)
a = memoryview(point)
b = memoryview(point)
@@ -3988,8 +3995,11 @@ def test_memoryview_tobytes(self):
# Unknown formats are handled: tobytes() purely depends on itemsize.
if ctypes:
# format: "T{>l:x:>l:y:}"
- class BEPoint(ctypes.BigEndianStructure):
- _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)]
+ @ctypes.util.struct(endian='big')
+ class BEPoint:
+ x: ctypes.c_long
+ y: ctypes.c_long
+
point = BEPoint(100, 200)
a = memoryview(point)
self.assertEqual(a.tobytes(), bytes(point))
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
index c18b203f42f59f3..31704955df3e14e 100644
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -36,21 +36,25 @@ def check(input, expect):
self.assertEqual(coder(input), (expect, len(input)))
return check
-# On small versions of Windows like Windows IoT or Windows Nano Server not all
codepages are present
+# On small versions of Windows like Windows IoT or Windows Nano Server,
+# not all codepages are present
def is_code_page_present(cp):
- from ctypes import POINTER, WINFUNCTYPE, WinDLL, Structure
+ from ctypes import POINTER, WINFUNCTYPE, WinDLL
+ from ctypes.util import struct
from ctypes.wintypes import BOOL, BYTE, WCHAR, UINT, DWORD
MAX_LEADBYTES = 12 # 5 ranges, 2 bytes ea., 0 term.
MAX_DEFAULTCHAR = 2 # single or double byte
MAX_PATH = 260
- class CPINFOEXW(Structure):
- _fields_ = [("MaxCharSize", UINT),
- ("DefaultChar", BYTE*MAX_DEFAULTCHAR),
- ("LeadByte", BYTE*MAX_LEADBYTES),
- ("UnicodeDefaultChar", WCHAR),
- ("CodePage", UINT),
- ("CodePageName", WCHAR*MAX_PATH)]
+
+ @struct
+ class CPINFOEXW:
+ MaxCharSize: UINT
+ DefaultChar: BYTE * MAX_DEFAULTCHAR
+ LeadByte: BYTE * MAX_LEADBYTES
+ UnicodeDefaultChar: WCHAR
+ CodePage: UINT
+ CodePageName: WCHAR * MAX_PATH
prototype = WINFUNCTYPE(BOOL, UINT, DWORD, POINTER(CPINFOEXW))
GetCPInfoEx = prototype(("GetCPInfoExW", WinDLL("kernel32")))
diff --git a/Lib/test/test_ctypes/test_refcounts.py
b/Lib/test/test_ctypes/test_refcounts.py
index d79937277e7df69..5191862658c5671 100644
--- a/Lib/test/test_ctypes/test_refcounts.py
+++ b/Lib/test/test_ctypes/test_refcounts.py
@@ -54,8 +54,10 @@ def func(*args):
gc.collect()
self.assertEqual(sys.getrefcount(func), orig_refcount)
- class X(ctypes.Structure):
- _fields_ = [("a", OtherCallback)]
+ @ctypes.util.struct
+ class X:
+ a: OtherCallback
+
x = X()
x.a = OtherCallback(func)
diff --git a/Lib/test/test_ctypes/test_unicode.py
b/Lib/test/test_ctypes/test_unicode.py
index d9e17371d135724..8e91f8f2c566803 100644
--- a/Lib/test/test_ctypes/test_unicode.py
+++ b/Lib/test/test_ctypes/test_unicode.py
@@ -1,4 +1,4 @@
-import ctypes
+import ctypes.util
import unittest
from test.support import import_helper
_ctypes_test = import_helper.import_module("_ctypes_test")
@@ -26,8 +26,10 @@ def test_buffers(self):
self.assertEqual(buf[6:5:-1], "")
def test_embedded_null(self):
- class TestStruct(ctypes.Structure):
- _fields_ = [("unicode", ctypes.c_wchar_p)]
+ @ctypes.util.struct
+ class TestStruct:
+ unicode: ctypes.c_wchar_p
+
t = TestStruct()
# This would raise a ValueError:
t.unicode = "foo\0bar\0\0"
diff --git a/Lib/test/test_ctypes/test_win32_com_foreign_func.py
b/Lib/test/test_ctypes/test_win32_com_foreign_func.py
index 7e54f8f6c31d33f..c797ab98cf1e424 100644
--- a/Lib/test/test_ctypes/test_win32_com_foreign_func.py
+++ b/Lib/test/test_ctypes/test_win32_com_foreign_func.py
@@ -1,4 +1,4 @@
-import ctypes
+import ctypes.util
import gc
import sys
import unittest
@@ -20,14 +20,13 @@
E_NOINTERFACE = -2147467262
-class GUID(ctypes.Structure):
[email protected]
+class GUID:
#
https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
- _fields_ = [
- ("Data1", DWORD),
- ("Data2", WORD),
- ("Data3", WORD),
- ("Data4", BYTE * 8),
- ]
+ Data1: DWORD
+ Data2: WORD
+ Data3: WORD
+ Data4: BYTE * 8
def create_proto_com_method(name, index, restype, *argtypes):
diff --git a/Lib/test/test_io/utils.py b/Lib/test/test_io/utils.py
index 3b1faec2140fbc6..dde49337a24f0b9 100644
--- a/Lib/test/test_io/utils.py
+++ b/Lib/test/test_io/utils.py
@@ -8,12 +8,13 @@
try:
- import ctypes
+ import ctypes.util
except ImportError:
def byteslike(*pos, **kw):
return array.array("b", bytes(*pos, **kw))
else:
- class EmptyStruct(ctypes.Structure):
+ @ctypes.util.struct
+ class EmptyStruct:
pass
def byteslike(*pos, **kw):
_______________________________________________
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]