`ctypes.POINTER().__init__()` does not cast ctypes argument.
import ctypes
MyPointer = ctypes.POINTER(ctypes.c_ubyte)
MyArray = ctypes.c_ubyte * 4
my_array = MyArray(0, 1, 2, 3)
my_pointer = MyPointer(my_array)
# Now `my_pointer` is an alias for `my_array`, so `my_pointer[0] == my_array[0]`, etc.
# Note that `my_pointer` has type `LP_c_ubyte`, while `my_array` is a `c_ubyte_Array_4`.
# More minimal example:
ctypes.POINTER(ctypes.c_ubyte)((ctypes.c_ubyte * 1)(0))
In CPython (tested in 3.8.10 and 3.9.5), this works, and implicitly casts the argument to the POINTER
's type.
In PyPy (tested in 7.3.5), this raises an error like: TypeError: expected c_ubyte instead of c_ubyte_Array_4
.
I suspect the issue is in the implementation of _ctypes._Pointer.setcontents
: the CPython version includes a preliminary check to see if the argument is a ctypes object; if so, that's good enough, and no further type checking is done (https://github.com/python/cpython/blob/main/Modules/_ctypes/_ctypes.c#L5190). The PyPy version instead goes straight to the isinstance
check (https://foss.heptapod.net/pypy/pypy/-/blob/branch/default/lib_pypy/_ctypes/pointer.py#L104), so other types (even if they should ostensibly be compatible, like char []
and char *
) are not accepted.
Maybe the test in setcontents
should be something like not (isinstance(value, _CData) or isinstance(value, self._type_))
?