From: Victor Stinner Date: Sun, 19 Jul 2026 12:08:56 +0000 (+0200) Subject: gh-104533: Use `@ctypes.util.struct` decorator (#154031) X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=ee50ed3ed010e26495300778b30b57da5cadc933;p=thirdparty%2FPython%2Fcpython.git gh-104533: Use `@ctypes.util.struct` decorator (#154031) Use also `@ctypes.util.wrap_dll_function()` decorator. --- diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 115a187a8a85..8f665b3a98a0 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -161,9 +161,10 @@ PRELOAD = ['__main__', 'test.test_multiprocessing_forkserver'] # 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 @@ class _TestHeap(BaseTestCase): # # -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 d17d9a2ecf8d..e5bc2fd9b3d0 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 3213a4751273..453dafe2709e 100644 --- a/Lib/test/test_buffer.py +++ b/Lib/test/test_buffer.py @@ -37,6 +37,7 @@ except ImportError: try: import ctypes + import ctypes.util except ImportError: ctypes = None @@ -2849,8 +2850,11 @@ class TestBufferProtocol(unittest.TestCase): 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 @@ class TestBufferProtocol(unittest.TestCase): # 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 @@ class TestBufferProtocol(unittest.TestCase): # 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 c18b203f42f5..31704955df3e 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -36,21 +36,25 @@ def coding_checker(self, coder): 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 d79937277e7d..5191862658c5 100644 --- a/Lib/test/test_ctypes/test_refcounts.py +++ b/Lib/test/test_ctypes/test_refcounts.py @@ -54,8 +54,10 @@ class RefcountTestCase(unittest.TestCase): 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 d9e17371d135..8e91f8f2c566 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 @@ class UnicodeTestCase(unittest.TestCase): 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 7e54f8f6c31d..c797ab98cf1e 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 @@ TRUE = 1 E_NOINTERFACE = -2147467262 -class GUID(ctypes.Structure): +@ctypes.util.struct +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 3b1faec2140f..dde49337a24f 100644 --- a/Lib/test/test_io/utils.py +++ b/Lib/test/test_io/utils.py @@ -8,12 +8,13 @@ import _pyio as pyio # Python implementation of io 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):