https://github.com/python/cpython/commit/be89ee5649031e08f191bf596fa20a09c5698079
commit: be89ee5649031e08f191bf596fa20a09c5698079
branch: main
author: AN Long <[email protected]>
committer: erlend-aasland <[email protected]>
date: 2024-01-09T18:28:43+01:00
summary:

gh-103092: Test _ctypes type hierarchy and features (#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 <[email protected]>

files:
A Lib/test/test_ctypes/_support.py
A Lib/test/test_ctypes/test_unions.py
M Lib/test/test_ctypes/test_arrays.py
M Lib/test/test_ctypes/test_funcptr.py
M Lib/test/test_ctypes/test_pointers.py
M Lib/test/test_ctypes/test_simplesubclasses.py
M Lib/test/test_ctypes/test_struct_fields.py
M Lib/test/test_ctypes/test_structures.py
M Lib/test/test_ctypes/test_win32.py

diff --git a/Lib/test/test_ctypes/_support.py b/Lib/test/test_ctypes/_support.py
new file mode 100644
index 00000000000000..e4c2b33825ae8f
--- /dev/null
+++ b/Lib/test/test_ctypes/_support.py
@@ -0,0 +1,24 @@
+# Some classes and types are not export to _ctypes module directly.
+
+import ctypes
+from _ctypes import Structure, Union, _Pointer, Array, _SimpleCData, CFuncPtr
+
+
+_CData = Structure.__base__
+assert _CData.__name__ == "_CData"
+
+class _X(Structure):
+    _fields_ = [("x", ctypes.c_int)]
+CField = type(_X.x)
+
+# metaclasses
+PyCStructType = type(Structure)
+UnionType = type(Union)
+PyCPointerType = type(_Pointer)
+PyCArrayType = type(Array)
+PyCSimpleType = type(_SimpleCData)
+PyCFuncPtrType = type(CFuncPtr)
+
+# type flags
+Py_TPFLAGS_DISALLOW_INSTANTIATION = 1 << 7
+Py_TPFLAGS_IMMUTABLETYPE = 1 << 8
diff --git a/Lib/test/test_ctypes/test_arrays.py 
b/Lib/test/test_ctypes/test_arrays.py
index 6b6cebd3e20285..774316e227ff73 100644
--- a/Lib/test/test_ctypes/test_arrays.py
+++ b/Lib/test/test_ctypes/test_arrays.py
@@ -7,6 +7,8 @@
                     c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, 
c_int, c_uint,
                     c_long, c_ulonglong, c_float, c_double, c_longdouble)
 from test.support import bigmemtest, _2G
+from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
 
 
 formats = "bBhHiIlLqQfd"
@@ -23,6 +25,18 @@ def ARRAY(*args):
 
 
 class ArrayTestCase(unittest.TestCase):
+    def test_inheritance_hierarchy(self):
+        self.assertEqual(Array.mro(), [Array, _CData, object])
+
+        self.assertEqual(PyCArrayType.__name__, "PyCArrayType")
+        self.assertEqual(type(PyCArrayType), type)
+
+    def test_type_flags(self):
+        for cls in Array, PyCArrayType:
+            with self.subTest(cls=cls):
+                self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+                self.assertFalse(cls.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
     def test_simple(self):
         # create classes holding simple numeric types, and check
         # various properties.
diff --git a/Lib/test/test_ctypes/test_funcptr.py 
b/Lib/test/test_ctypes/test_funcptr.py
index 2ad40647e0cfbb..0eed39484fb39e 100644
--- a/Lib/test/test_ctypes/test_funcptr.py
+++ b/Lib/test/test_ctypes/test_funcptr.py
@@ -3,6 +3,8 @@
 import unittest
 from ctypes import (CDLL, Structure, CFUNCTYPE, sizeof, _CFuncPtr,
                     c_void_p, c_char_p, c_char, c_int, c_uint, c_long)
+from ._support import (_CData, PyCFuncPtrType, 
Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
 
 
 try:
@@ -15,6 +17,18 @@
 
 
 class CFuncPtrTestCase(unittest.TestCase):
+    def test_inheritance_hierarchy(self):
+        self.assertEqual(_CFuncPtr.mro(), [_CFuncPtr, _CData, object])
+
+        self.assertEqual(PyCFuncPtrType.__name__, "PyCFuncPtrType")
+        self.assertEqual(type(PyCFuncPtrType), type)
+
+    def test_type_flags(self):
+        for cls in _CFuncPtr, PyCFuncPtrType:
+            with self.subTest(cls=cls):
+                self.assertTrue(_CFuncPtr.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+                self.assertFalse(_CFuncPtr.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
     def test_basic(self):
         X = WINFUNCTYPE(c_int, c_int, c_int)
 
diff --git a/Lib/test/test_ctypes/test_pointers.py 
b/Lib/test/test_ctypes/test_pointers.py
index 8410174358c19d..8cf2114c282cab 100644
--- a/Lib/test/test_ctypes/test_pointers.py
+++ b/Lib/test/test_ctypes/test_pointers.py
@@ -10,6 +10,8 @@
                     c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
                     c_long, c_ulong, c_longlong, c_ulonglong,
                     c_float, c_double)
+from ._support import (_CData, PyCPointerType, 
Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
 
 
 ctype_types = [c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
@@ -19,6 +21,18 @@
 
 
 class PointersTestCase(unittest.TestCase):
+    def test_inheritance_hierarchy(self):
+        self.assertEqual(_Pointer.mro(), [_Pointer, _CData, object])
+
+        self.assertEqual(PyCPointerType.__name__, "PyCPointerType")
+        self.assertEqual(type(PyCPointerType), type)
+
+    def test_type_flags(self):
+        for cls in _Pointer, PyCPointerType:
+            with self.subTest(cls=cls):
+                self.assertTrue(_Pointer.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+                self.assertFalse(_Pointer.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
     def test_pointer_crash(self):
 
         class A(POINTER(c_ulong)):
diff --git a/Lib/test/test_ctypes/test_simplesubclasses.py 
b/Lib/test/test_ctypes/test_simplesubclasses.py
index 6072b62de5d53a..c96798e67f23f7 100644
--- a/Lib/test/test_ctypes/test_simplesubclasses.py
+++ b/Lib/test/test_ctypes/test_simplesubclasses.py
@@ -1,5 +1,7 @@
 import unittest
-from ctypes import Structure, CFUNCTYPE, c_int
+from ctypes import Structure, CFUNCTYPE, c_int, _SimpleCData
+from ._support import (_CData, PyCSimpleType, 
Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
 
 
 class MyInt(c_int):
@@ -10,6 +12,19 @@ def __eq__(self, other):
 
 
 class Test(unittest.TestCase):
+    def test_inheritance_hierarchy(self):
+        self.assertEqual(_SimpleCData.mro(), [_SimpleCData, _CData, object])
+
+        self.assertEqual(PyCSimpleType.__name__, "PyCSimpleType")
+        self.assertEqual(type(PyCSimpleType), type)
+
+        self.assertEqual(c_int.mro(), [c_int, _SimpleCData, _CData, object])
+
+    def test_type_flags(self):
+        for cls in _SimpleCData, PyCSimpleType:
+            with self.subTest(cls=cls):
+                self.assertTrue(_SimpleCData.__flags__ & 
Py_TPFLAGS_IMMUTABLETYPE)
+                self.assertFalse(_SimpleCData.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
 
     def test_compare(self):
         self.assertEqual(MyInt(3), MyInt(3))
diff --git a/Lib/test/test_ctypes/test_struct_fields.py 
b/Lib/test/test_ctypes/test_struct_fields.py
index f60dfe5b42ef65..f474a02fa8db06 100644
--- a/Lib/test/test_ctypes/test_struct_fields.py
+++ b/Lib/test/test_ctypes/test_struct_fields.py
@@ -1,5 +1,7 @@
 import unittest
 from ctypes import Structure, Union, sizeof, c_char, c_int
+from ._support import (CField, Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
 
 
 class StructFieldsTestCase(unittest.TestCase):
@@ -12,7 +14,6 @@ class StructFieldsTestCase(unittest.TestCase):
     # 4. The type is subclassed
     #
     # When they are finalized, assigning _fields_ is no longer allowed.
-
     def test_1_A(self):
         class X(Structure):
             pass
@@ -56,11 +57,15 @@ class X(Structure):
         self.assertEqual(bytes(x), b'a\x00###')
 
     def test_6(self):
-        class X(Structure):
-            _fields_ = [("x", c_int)]
-        CField = type(X.x)
         self.assertRaises(TypeError, CField)
 
+    def test_cfield_type_flags(self):
+        self.assertTrue(CField.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION)
+        self.assertTrue(CField.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+
+    def test_cfield_inheritance_hierarchy(self):
+        self.assertEqual(CField.mro(), [CField, object])
+
     def test_gh99275(self):
         class BrokenStructure(Structure):
             def __init_subclass__(cls, **kwargs):
diff --git a/Lib/test/test_ctypes/test_structures.py 
b/Lib/test/test_ctypes/test_structures.py
index 3eafc77ca70aea..98bc4bdcac9306 100644
--- a/Lib/test/test_ctypes/test_structures.py
+++ b/Lib/test/test_ctypes/test_structures.py
@@ -12,6 +12,8 @@
 from struct import calcsize
 from collections import namedtuple
 from test import support
+from ._support import (_CData, PyCStructType, 
Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
 
 
 class SubclassesTest(unittest.TestCase):
@@ -70,6 +72,19 @@ class StructureTestCase(unittest.TestCase):
                "d": c_double,
                }
 
+    def test_inheritance_hierarchy(self):
+        self.assertEqual(Structure.mro(), [Structure, _CData, object])
+
+        self.assertEqual(PyCStructType.__name__, "PyCStructType")
+        self.assertEqual(type(PyCStructType), type)
+
+
+    def test_type_flags(self):
+        for cls in Structure, PyCStructType:
+            with self.subTest(cls=cls):
+                self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+                self.assertFalse(Structure.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+
     def test_simple_structs(self):
         for code, tp in self.formats.items():
             class X(Structure):
diff --git a/Lib/test/test_ctypes/test_unions.py 
b/Lib/test/test_ctypes/test_unions.py
new file mode 100644
index 00000000000000..cf5344bdf19165
--- /dev/null
+++ b/Lib/test/test_ctypes/test_unions.py
@@ -0,0 +1,18 @@
+import unittest
+from ctypes import Union
+from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
+                       Py_TPFLAGS_IMMUTABLETYPE)
+
+
+class ArrayTestCase(unittest.TestCase):
+    def test_inheritance_hierarchy(self):
+        self.assertEqual(Union.mro(), [Union, _CData, object])
+
+        self.assertEqual(UnionType.__name__, "UnionType")
+        self.assertEqual(type(UnionType), type)
+
+    def test_type_flags(self):
+        for cls in Union, UnionType:
+            with self.subTest(cls=Union):
+                self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+                self.assertFalse(Union.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
diff --git a/Lib/test/test_ctypes/test_win32.py 
b/Lib/test/test_ctypes/test_win32.py
index 01e624f76f0685..4aaecd8d38f98f 100644
--- a/Lib/test/test_ctypes/test_win32.py
+++ b/Lib/test/test_ctypes/test_win32.py
@@ -9,6 +9,7 @@
                     _pointer_type_cache,
                     c_void_p, c_char, c_int, c_long)
 from test import support
+from ._support import Py_TPFLAGS_DISALLOW_INSTANTIATION, 
Py_TPFLAGS_IMMUTABLETYPE
 
 
 @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
@@ -73,6 +74,11 @@ def test_COMError(self):
         self.assertEqual(ex.text, "text")
         self.assertEqual(ex.details, ("details",))
 
+        self.assertEqual(COMError.mro(),
+                         [COMError, Exception, BaseException, object])
+        self.assertFalse(COMError.__flags__ & 
Py_TPFLAGS_DISALLOW_INSTANTIATION)
+        self.assertTrue(COMError.__flags__ & Py_TPFLAGS_IMMUTABLETYPE)
+
 
 @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
 class TestWinError(unittest.TestCase):

_______________________________________________
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