mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
[3.12] gh-125783: Add tests to prevent regressions with the combination of ctypes and metaclasses. (GH-125881) (GH-125988)
cherry picked from commit1384409460by Jun Komoda Also: Add test_ctypes/_support.py from 3.13+ This partially backportsbe89ee5649(https://github.com/python/cpython/pull/113727) by AN Long Co-authored-by: Jun Komoda <45822440+junkmd@users.noreply.github.com> Co-authored-by: AN Long <aisk@users.noreply.github.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
This commit is contained in:
parent
bc9ae4a1ca
commit
bce9df97d5
2 changed files with 111 additions and 0 deletions
24
Lib/test/test_ctypes/_support.py
Normal file
24
Lib/test/test_ctypes/_support.py
Normal file
|
|
@ -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
|
||||
87
Lib/test/test_ctypes/test_c_simple_type_meta.py
Normal file
87
Lib/test/test_ctypes/test_c_simple_type_meta.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import unittest
|
||||
import ctypes
|
||||
from ctypes import POINTER, c_void_p
|
||||
|
||||
from ._support import PyCSimpleType
|
||||
|
||||
|
||||
class PyCSimpleTypeAsMetaclassTest(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
# to not leak references, we must clean _pointer_type_cache
|
||||
ctypes._reset_cache()
|
||||
|
||||
def test_creating_pointer_in_dunder_new_1(self):
|
||||
# Test metaclass whose instances are C types; when the type is
|
||||
# created it automatically creates a pointer type for itself.
|
||||
# The pointer type is also an instance of the metaclass.
|
||||
# Such an implementation is used in `IUnknown` of the `comtypes`
|
||||
# project. See gh-124520.
|
||||
|
||||
class ct_meta(type):
|
||||
def __new__(cls, name, bases, namespace):
|
||||
self = super().__new__(cls, name, bases, namespace)
|
||||
|
||||
# Avoid recursion: don't set up a pointer to
|
||||
# a pointer (to a pointer...)
|
||||
if bases == (c_void_p,):
|
||||
# When creating PtrBase itself, the name
|
||||
# is not yet available
|
||||
return self
|
||||
if issubclass(self, PtrBase):
|
||||
return self
|
||||
|
||||
if bases == (object,):
|
||||
ptr_bases = (self, PtrBase)
|
||||
else:
|
||||
ptr_bases = (self, POINTER(bases[0]))
|
||||
p = p_meta(f"POINTER({self.__name__})", ptr_bases, {})
|
||||
ctypes._pointer_type_cache[self] = p
|
||||
return self
|
||||
|
||||
class p_meta(PyCSimpleType, ct_meta):
|
||||
pass
|
||||
|
||||
class PtrBase(c_void_p, metaclass=p_meta):
|
||||
pass
|
||||
|
||||
class CtBase(object, metaclass=ct_meta):
|
||||
pass
|
||||
|
||||
class Sub(CtBase):
|
||||
pass
|
||||
|
||||
class Sub2(Sub):
|
||||
pass
|
||||
|
||||
self.assertIsInstance(POINTER(Sub2), p_meta)
|
||||
self.assertTrue(issubclass(POINTER(Sub2), Sub2))
|
||||
self.assertTrue(issubclass(POINTER(Sub2), POINTER(Sub)))
|
||||
self.assertTrue(issubclass(POINTER(Sub), POINTER(CtBase)))
|
||||
|
||||
def test_creating_pointer_in_dunder_new_2(self):
|
||||
# A simpler variant of the above, used in `CoClass` of the `comtypes`
|
||||
# project.
|
||||
|
||||
class ct_meta(type):
|
||||
def __new__(cls, name, bases, namespace):
|
||||
self = super().__new__(cls, name, bases, namespace)
|
||||
if isinstance(self, p_meta):
|
||||
return self
|
||||
p = p_meta(f"POINTER({self.__name__})", (self, c_void_p), {})
|
||||
ctypes._pointer_type_cache[self] = p
|
||||
return self
|
||||
|
||||
class p_meta(PyCSimpleType, ct_meta):
|
||||
pass
|
||||
|
||||
class Core(object):
|
||||
pass
|
||||
|
||||
class CtBase(Core, metaclass=ct_meta):
|
||||
pass
|
||||
|
||||
class Sub(CtBase):
|
||||
pass
|
||||
|
||||
self.assertIsInstance(POINTER(Sub), p_meta)
|
||||
self.assertTrue(issubclass(POINTER(Sub), Sub))
|
||||
Loading…
Add table
Add a link
Reference in a new issue