Skip to content

Commit 25680f4

Browse files
committed
gh-158418: Fix zero-argument super() in ctypes.util.struct methods
@struct builds a new class and copies the decorated class's namespace into it, so the __class__ cell of the copied methods still pointed at the discarded class. Rebind that cell to the returned class, as dataclasses does for slots=True.
1 parent dc0b1f8 commit 25680f4

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

‎Lib/ctypes/util.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import sys
33

4-
from dataclasses import dataclass
4+
from dataclasses import dataclass, _update_func_cell_for__class__
55

66
lazy import functools
77
lazy import inspect
@@ -478,6 +478,17 @@ class _Struct(endian_class):
478478
_fields_ = fields
479479
_anonymous_ = anonymous
480480

481+
for member in vars(_Struct).values():
482+
if not isinstance(member, type) and hasattr(member, '__wrapped__'):
483+
member = inspect.unwrap(member)
484+
if isinstance(member, property):
485+
functions = (member.fget, member.fset, member.fdel)
486+
else:
487+
functions = (member,)
488+
for func in functions:
489+
if inspect.isfunction(func):
490+
_update_func_cell_for__class__(func, decorated_class, _Struct)
491+
481492
return _Struct
482493

483494

‎Lib/test/test_ctypes/test_structures.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,37 @@ class Foo:
955955

956956
self.assertEqual(Foo.__name__, "Foo")
957957

958+
@subTests("endian", ["native", "little", "big"])
959+
def test_struct_util_class_cell(self, endian):
960+
@struct_util(endian=endian)
961+
class Foo:
962+
x: c_int
963+
964+
def __init__(self, x=0):
965+
super().__init__(x)
966+
967+
self.assertEqual(Foo(1).x, 1)
968+
969+
@struct_util(endian=endian)
970+
class Foo:
971+
x: c_int
972+
973+
@classmethod
974+
def create(cls):
975+
return super().__new__(cls)
976+
977+
self.assertIsInstance(Foo.create(), Foo)
978+
979+
@struct_util(endian=endian)
980+
class Foo:
981+
x: c_int
982+
983+
@property
984+
def cls(self):
985+
return __class__
986+
987+
self.assertIs(Foo().cls, Foo)
988+
958989
def test_string_annotations(self):
959990
from test.test_ctypes import struct_str_ann
960991
Point = struct_str_ann.Point

0 commit comments

Comments
 (0)