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]

Reply via email to