Skip to content

Commit 541a914

Browse files
aiskerlend-aasland
authored andcommitted
pythongh-103092: Test _ctypes type hierarchy and features (python#113727)
Test the following features for _ctypes types: - disallow instantiation - inheritance (MRO) - immutability - type name The following _ctypes types are tested: - Array - CField - COMError - PyCArrayType - PyCFuncPtrType - PyCPointerType - PyCSimpleType - PyCStructType - Structure - Union - UnionType - _CFuncPtr - _Pointer - _SimpleCData Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
1 parent 888b7c5 commit 541a914

9 files changed

+130
-5
lines changed

Lib/test/test_ctypes/_support.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Some classes and types are not export to _ctypes module directly.
2+
3+
import ctypes
4+
from _ctypes import Structure, Union, _Pointer, Array, _SimpleCData, CFuncPtr
5+
6+
7+
_CData = Structure.__base__
8+
assert _CData.__name__ == "_CData"
9+
10+
class _X(Structure):
11+
_fields_ = [("x", ctypes.c_int)]
12+
CField = type(_X.x)
13+
14+
# metaclasses
15+
PyCStructType = type(Structure)
16+
UnionType = type(Union)
17+
PyCPointerType = type(_Pointer)
18+
PyCArrayType = type(Array)
19+
PyCSimpleType = type(_SimpleCData)
20+
PyCFuncPtrType = type(CFuncPtr)
21+
22+
# type flags
23+
Py_TPFLAGS_DISALLOW_INSTANTIATION = 1 << 7
24+
Py_TPFLAGS_IMMUTABLETYPE = 1 << 8

Lib/test/test_ctypes/test_arrays.py

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
88
c_long, c_ulonglong, c_float, c_double, c_longdouble)
99
from test.support import bigmemtest, _2G
10+
from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
11+
Py_TPFLAGS_IMMUTABLETYPE)
1012

1113

1214
formats = "bBhHiIlLqQfd"
@@ -23,6 +25,18 @@ def ARRAY(*args):
2325

2426

2527
class ArrayTestCase(unittest.TestCase):
28+
def test_inheritance_hierarchy(self):
29+
self.assertEqual(Array.mro(), [Array, _CData, object])
30+
31+
self.assertEqual(PyCArrayType.__name__, "PyCArrayType")
32+
self.assertEqual(type(PyCArrayType), type)
33+
34+
def test_type_flags(self):
35+
for cls in Array, PyCArrayType:
36+
with self.subTest(cls=cls):
37+
self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
38+
self.assertFalse(cls.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
39+
2640
def test_simple(self):
2741
# create classes holding simple numeric types, and check
2842
# various properties.

Lib/test/test_ctypes/test_funcptr.py

+14
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import unittest
44
from ctypes import (CDLL, Structure, CFUNCTYPE, sizeof, _CFuncPtr,
55
c_void_p, c_char_p, c_char, c_int, c_uint, c_long)
6+
from ._support import (_CData, PyCFuncPtrType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
7+
Py_TPFLAGS_IMMUTABLETYPE)
68

79

810
try:
@@ -15,6 +17,18 @@
1517

1618

1719
class CFuncPtrTestCase(unittest.TestCase):
20+
def test_inheritance_hierarchy(self):
21+
self.assertEqual(_CFuncPtr.mro(), [_CFuncPtr, _CData, object])
22+
23+
self.assertEqual(PyCFuncPtrType.__name__, "PyCFuncPtrType")
24+
self.assertEqual(type(PyCFuncPtrType), type)
25+
26+
def test_type_flags(self):
27+
for cls in _CFuncPtr, PyCFuncPtrType:
28+
with self.subTest(cls=cls):
29+
self.assertTrue(_CFuncPtr.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
30+
self.assertFalse(_CFuncPtr.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
31+
1832
def test_basic(self):
1933
X = WINFUNCTYPE(c_int, c_int, c_int)
2034

Lib/test/test_ctypes/test_pointers.py

+14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
1111
c_long, c_ulong, c_longlong, c_ulonglong,
1212
c_float, c_double)
13+
from ._support import (_CData, PyCPointerType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
14+
Py_TPFLAGS_IMMUTABLETYPE)
1315

1416

1517
ctype_types = [c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
@@ -19,6 +21,18 @@
1921

2022

2123
class PointersTestCase(unittest.TestCase):
24+
def test_inheritance_hierarchy(self):
25+
self.assertEqual(_Pointer.mro(), [_Pointer, _CData, object])
26+
27+
self.assertEqual(PyCPointerType.__name__, "PyCPointerType")
28+
self.assertEqual(type(PyCPointerType), type)
29+
30+
def test_type_flags(self):
31+
for cls in _Pointer, PyCPointerType:
32+
with self.subTest(cls=cls):
33+
self.assertTrue(_Pointer.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
34+
self.assertFalse(_Pointer.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
35+
2236
def test_pointer_crash(self):
2337

2438
class A(POINTER(c_ulong)):

Lib/test/test_ctypes/test_simplesubclasses.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import unittest
2-
from ctypes import Structure, CFUNCTYPE, c_int
2+
from ctypes import Structure, CFUNCTYPE, c_int, _SimpleCData
3+
from ._support import (_CData, PyCSimpleType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
4+
Py_TPFLAGS_IMMUTABLETYPE)
35

46

57
class MyInt(c_int):
@@ -10,6 +12,19 @@ def __eq__(self, other):
1012

1113

1214
class Test(unittest.TestCase):
15+
def test_inheritance_hierarchy(self):
16+
self.assertEqual(_SimpleCData.mro(), [_SimpleCData, _CData, object])
17+
18+
self.assertEqual(PyCSimpleType.__name__, "PyCSimpleType")
19+
self.assertEqual(type(PyCSimpleType), type)
20+
21+
self.assertEqual(c_int.mro(), [c_int, _SimpleCData, _CData, object])
22+
23+
def test_type_flags(self):
24+
for cls in _SimpleCData, PyCSimpleType:
25+
with self.subTest(cls=cls):
26+
self.assertTrue(_SimpleCData.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
27+
self.assertFalse(_SimpleCData.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
1328

1429
def test_compare(self):
1530
self.assertEqual(MyInt(3), MyInt(3))

Lib/test/test_ctypes/test_struct_fields.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import unittest
22
from ctypes import Structure, Union, sizeof, c_char, c_int
3+
from ._support import (CField, Py_TPFLAGS_DISALLOW_INSTANTIATION,
4+
Py_TPFLAGS_IMMUTABLETYPE)
35

46

57
class StructFieldsTestCase(unittest.TestCase):
@@ -12,7 +14,6 @@ class StructFieldsTestCase(unittest.TestCase):
1214
# 4. The type is subclassed
1315
#
1416
# When they are finalized, assigning _fields_ is no longer allowed.
15-
1617
def test_1_A(self):
1718
class X(Structure):
1819
pass
@@ -56,11 +57,15 @@ class X(Structure):
5657
self.assertEqual(bytes(x), b'a\x00###')
5758

5859
def test_6(self):
59-
class X(Structure):
60-
_fields_ = [("x", c_int)]
61-
CField = type(X.x)
6260
self.assertRaises(TypeError, CField)
6361

62+
def test_cfield_type_flags(self):
63+
self.assertTrue(CField.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
64+
self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
65+
66+
def test_cfield_inheritance_hierarchy(self):
67+
self.assertEqual(CField.mro(), [CField, object])
68+
6469
def test_gh99275(self):
6570
class BrokenStructure(Structure):
6671
def __init_subclass__(cls, **kwargs):

Lib/test/test_ctypes/test_structures.py

+15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from struct import calcsize
1313
from collections import namedtuple
1414
from test import support
15+
from ._support import (_CData, PyCStructType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
16+
Py_TPFLAGS_IMMUTABLETYPE)
1517

1618

1719
class SubclassesTest(unittest.TestCase):
@@ -70,6 +72,19 @@ class StructureTestCase(unittest.TestCase):
7072
"d": c_double,
7173
}
7274

75+
def test_inheritance_hierarchy(self):
76+
self.assertEqual(Structure.mro(), [Structure, _CData, object])
77+
78+
self.assertEqual(PyCStructType.__name__, "PyCStructType")
79+
self.assertEqual(type(PyCStructType), type)
80+
81+
82+
def test_type_flags(self):
83+
for cls in Structure, PyCStructType:
84+
with self.subTest(cls=cls):
85+
self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
86+
self.assertFalse(Structure.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
87+
7388
def test_simple_structs(self):
7489
for code, tp in self.formats.items():
7590
class X(Structure):

Lib/test/test_ctypes/test_unions.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import unittest
2+
from ctypes import Union
3+
from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
4+
Py_TPFLAGS_IMMUTABLETYPE)
5+
6+
7+
class ArrayTestCase(unittest.TestCase):
8+
def test_inheritance_hierarchy(self):
9+
self.assertEqual(Union.mro(), [Union, _CData, object])
10+
11+
self.assertEqual(UnionType.__name__, "UnionType")
12+
self.assertEqual(type(UnionType), type)
13+
14+
def test_type_flags(self):
15+
for cls in Union, UnionType:
16+
with self.subTest(cls=Union):
17+
self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
18+
self.assertFalse(Union.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)

Lib/test/test_ctypes/test_win32.py

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
_pointer_type_cache,
1010
c_void_p, c_char, c_int, c_long)
1111
from test import support
12+
from ._support import Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE
1213

1314

1415
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
@@ -73,6 +74,11 @@ def test_COMError(self):
7374
self.assertEqual(ex.text, "text")
7475
self.assertEqual(ex.details, ("details",))
7576

77+
self.assertEqual(COMError.mro(),
78+
[COMError, Exception, BaseException, object])
79+
self.assertFalse(COMError.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
80+
self.assertTrue(COMError.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
81+
7682

7783
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
7884
class TestWinError(unittest.TestCase):

0 commit comments

Comments
 (0)