Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbe76e3f

Browse files
gh-100980: ctypes: Test, document, and fix finalizing _fields_ (GH-124292)
- If setting `_fields_` fails, e.g. with AttributeError, don't set the attribute in `__dict__`- Document the “finalization” behaviour- Beef up tests: add `getattr`, test Union as well as Structure- Put common functionality in a common functionCo-authored-by: Peter Bierma <zintensitydev@gmail.com>
1 parente256a75 commitbe76e3f

File tree

4 files changed

+67
-55
lines changed

4 files changed

+67
-55
lines changed

‎Doc/library/ctypes.rst‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2499,6 +2499,8 @@ Structured data types
24992499

25002500
Abstract base class for unions in native byte order.
25012501

2502+
Unions share common attributes and behavior with structures;
2503+
see:class:`Structure` documentation for details.
25022504

25032505
..class::BigEndianUnion(*args, **kw)
25042506

@@ -2558,14 +2560,19 @@ fields, or any other data types containing pointer type fields.
25582560
...
25592561
]
25602562

2561-
The:attr:`_fields_` class variable must, however, be defined before the
2562-
type is first used (an instance is created,:func:`sizeof` is called on it,
2563-
and so on). Later assignments to the:attr:`_fields_` class variable will
2564-
raise an AttributeError.
2563+
The:attr:`!_fields_` class variable can only be set once.
2564+
Later assignments will raise an:exc:`AttributeError`.
25652565

2566-
It is possible to define sub-subclasses of structure types, they inherit
2567-
the fields of the base class plus the:attr:`_fields_` defined in the
2568-
sub-subclass, if any.
2566+
Additionally, the:attr:`!_fields_` class variable must be defined before
2567+
the structure or union type is first used: an instance or subclass is
2568+
created,:func:`sizeof` is called on it, and so on.
2569+
Later assignments to:attr:`!_fields_` will raise an:exc:`AttributeError`.
2570+
If:attr:`!_fields_` has not been set before such use,
2571+
the structure or union will have no own fields, as if:attr:`!_fields_`
2572+
was empty.
2573+
2574+
Sub-subclasses of structure types inherit the fields of the base class
2575+
plus the:attr:`_fields_` defined in the sub-subclass, if any.
25692576

25702577

25712578
..attribute::_pack_

‎Lib/test/test_ctypes/test_struct_fields.py‎

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
Py_TPFLAGS_IMMUTABLETYPE)
55

66

7-
classStructFieldsTestCase(unittest.TestCase):
7+
NOTHING=object()
8+
9+
classFieldsTestBase:
810
# Structure/Union classes must get 'finalized' sooner or
911
# later, when one of these things happen:
1012
#
@@ -14,42 +16,47 @@ class StructFieldsTestCase(unittest.TestCase):
1416
# 4. The type is subclassed
1517
#
1618
# When they are finalized, assigning _fields_ is no longer allowed.
19+
20+
defassert_final_fields(self,cls,expected=NOTHING):
21+
self.assertRaises(AttributeError,setattr,cls,"_fields_", [])
22+
self.assertEqual(getattr(cls,"_fields_",NOTHING),expected)
23+
1724
deftest_1_A(self):
18-
classX(Structure):
25+
classX(self.cls):
1926
pass
2027
self.assertEqual(sizeof(X),0)# not finalized
2128
X._fields_= []# finalized
22-
self.assertRaises(AttributeError,setattr,X,"_fields_",[])
29+
self.assert_final_fields(X,expected=[])
2330

2431
deftest_1_B(self):
25-
classX(Structure):
32+
classX(self.cls):
2633
_fields_= []# finalized
27-
self.assertRaises(AttributeError,setattr,X,"_fields_",[])
34+
self.assert_final_fields(X,expected=[])
2835

2936
deftest_2(self):
30-
classX(Structure):
37+
classX(self.cls):
3138
pass
3239
X()
33-
self.assertRaises(AttributeError,setattr,X,"_fields_", [])
40+
self.assert_final_fields(X)
3441

3542
deftest_3(self):
36-
classX(Structure):
43+
classX(self.cls):
3744
pass
38-
classY(Structure):
45+
classY(self.cls):
3946
_fields_= [("x",X)]# finalizes X
40-
self.assertRaises(AttributeError,setattr,X,"_fields_", [])
47+
self.assert_final_fields(X)
4148

4249
deftest_4(self):
43-
classX(Structure):
50+
classX(self.cls):
4451
pass
4552
classY(X):
4653
pass
47-
self.assertRaises(AttributeError,setattr,X,"_fields_", [])
54+
self.assert_final_fields(X)
4855
Y._fields_= []
49-
self.assertRaises(AttributeError,setattr,X,"_fields_", [])
56+
self.assert_final_fields(X)
5057

5158
deftest_5(self):
52-
classX(Structure):
59+
classX(self.cls):
5360
_fields_= (("char",c_char*5),)
5461

5562
x=X(b'#'*5)
@@ -59,14 +66,8 @@ class X(Structure):
5966
deftest_6(self):
6067
self.assertRaises(TypeError,CField)
6168

62-
deftest_cfield_type_flags(self):
63-
self.assertTrue(CField.__flags__&Py_TPFLAGS_IMMUTABLETYPE)
64-
65-
deftest_cfield_inheritance_hierarchy(self):
66-
self.assertEqual(CField.mro(), [CField,object])
67-
6869
deftest_gh99275(self):
69-
classBrokenStructure(Structure):
70+
classBrokenStructure(self.cls):
7071
def__init_subclass__(cls,**kwargs):
7172
cls._fields_= []# This line will fail, `stginfo` is not ready
7273

@@ -77,26 +78,28 @@ class Subclass(BrokenStructure): ...
7778
# __set__ and __get__ should raise a TypeError in case their self
7879
# argument is not a ctype instance.
7980
deftest___set__(self):
80-
classMyCStruct(Structure):
81+
classMyCStruct(self.cls):
8182
_fields_= (("field",c_int),)
8283
self.assertRaises(TypeError,
8384
MyCStruct.field.__set__,'wrong type self',42)
8485

85-
classMyCUnion(Union):
86-
_fields_= (("field",c_int),)
87-
self.assertRaises(TypeError,
88-
MyCUnion.field.__set__,'wrong type self',42)
89-
9086
deftest___get__(self):
91-
classMyCStruct(Structure):
87+
classMyCStruct(self.cls):
9288
_fields_= (("field",c_int),)
9389
self.assertRaises(TypeError,
9490
MyCStruct.field.__get__,'wrong type self',42)
9591

96-
classMyCUnion(Union):
97-
_fields_= (("field",c_int),)
98-
self.assertRaises(TypeError,
99-
MyCUnion.field.__get__,'wrong type self',42)
92+
classStructFieldsTestCase(unittest.TestCase,FieldsTestBase):
93+
cls=Structure
94+
95+
deftest_cfield_type_flags(self):
96+
self.assertTrue(CField.__flags__&Py_TPFLAGS_IMMUTABLETYPE)
97+
98+
deftest_cfield_inheritance_hierarchy(self):
99+
self.assertEqual(CField.mro(), [CField,object])
100+
101+
classUnionFieldsTestCase(unittest.TestCase,FieldsTestBase):
102+
cls=Union
100103

101104

102105
if__name__=="__main__":
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The:attr:`~ctypes.Structure._fields_` attribute of
2+
:class:`ctypes.Structure` and:class:`~ctypes.Union` is no longer set if
3+
the setattr operation raises an error.

‎Modules/_ctypes/_ctypes.c‎

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,32 +1067,31 @@ CType_Type_repeat(PyObject *self, Py_ssize_t length)
10671067
returnPyCArrayType_from_ctype(st,self,length);
10681068
}
10691069

1070-
10711070
staticint
1072-
PyCStructType_setattro(PyObject*self,PyObject*key,PyObject*value)
1071+
_structunion_setattro(PyObject*self,PyObject*key,PyObject*value,intis_struct)
10731072
{
10741073
/* XXX Should we disallow deleting _fields_? */
1075-
if (-1==PyType_Type.tp_setattro(self,key,value))
1076-
return-1;
1074+
if (PyUnicode_Check(key)
1075+
&&_PyUnicode_EqualToASCIIString(key,"_fields_"))
1076+
{
1077+
if (PyCStructUnionType_update_stginfo(self,value,is_struct)<0) {
1078+
return-1;
1079+
}
1080+
}
10771081

1078-
if (value&&PyUnicode_Check(key)&&
1079-
_PyUnicode_EqualToASCIIString(key,"_fields_"))
1080-
returnPyCStructUnionType_update_stginfo(self,value,1);
1081-
return0;
1082+
returnPyType_Type.tp_setattro(self,key,value);
10821083
}
10831084

1085+
staticint
1086+
PyCStructType_setattro(PyObject*self,PyObject*key,PyObject*value)
1087+
{
1088+
return_structunion_setattro(self,key,value,1);
1089+
}
10841090

10851091
staticint
10861092
UnionType_setattro(PyObject*self,PyObject*key,PyObject*value)
10871093
{
1088-
/* XXX Should we disallow deleting _fields_? */
1089-
if (-1==PyType_Type.tp_setattro(self,key,value))
1090-
return-1;
1091-
1092-
if (PyUnicode_Check(key)&&
1093-
_PyUnicode_EqualToASCIIString(key,"_fields_"))
1094-
returnPyCStructUnionType_update_stginfo(self,value,0);
1095-
return0;
1094+
return_structunion_setattro(self,key,value,0);
10961095
}
10971096

10981097
staticPyType_Slotpycstruct_type_slots[]= {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp