I am working on a ctypes
drop-in-replacement / extension and ran into an issue I do not fully understand.
I am trying to build a class factory for call-back function decorators similar to CFUNCTYPE and WINFUNCTYPE. Both factories produce classes derived from ctypes._CFuncPtr
. Like every ctypes
function interface, they have properties like argtypes
and restype
. I want to extend the classes allowing an additional property named some_param
and I thought, why not, let's try this with "getter" and "setter" methods - how hard can it be ...
Because I am trying to use "getter" and "setter" methods (@property
) on a property of a class (NOT a property of objects), I ended up writing a metaclass. Because my class is derived from ctypes._CFuncPtr
, I think my metaclass must be derived from ctypes._CFuncPtr.__class__
(I could be wrong here).
The example below works, sort of:
import ctypes
class a_class:
def b_function(self, some_param_parg):
class c_class_meta(ctypes._CFuncPtr.__class__):
def __init__(cls, *args):
super().__init__(*args) # no idea if this is good ...
cls._some_param_ = some_param_parg
@property
def some_param(cls):
return cls._some_param_
@some_param.setter
def some_param(cls, value):
if not isinstance(value, list):
raise TypeError('some_param must be a list')
cls._some_param_ = value
class c_class(ctypes._CFuncPtr, metaclass = c_class_meta):
_argtypes_ = ()
_restype_ = None
_flags_ = ctypes._FUNCFLAG_STDCALL # change for CFUNCTYPE or WINFUNCTYPE etc ...
return c_class
d_class = a_class().b_function([1, 2, 3])
print(d_class.some_param)
d_class.some_param = [2, 6]
print(d_class.some_param)
d_class.some_param = {} # Raises an error - as expected
So far so good - using the above any further does NOT work anymore. The following pseudo-code (if used on an actual function from a DLL or shared object) will fail - in fact, it will cause the CPython interpreter to segfault ...
some_routine = ctypes.windll.LoadLibrary('some.dll').some_routine
func_type = d_class(ctypes.c_int16, ctypes.c_int16) # similar to CFUNCTYPE/WINFUNCTYPE
func_type.some_param = [4, 5, 6] # my "special" property
some_routine.argtypes = (ctypes.c_int16, func_type)
@func_type
def demo(x):
return x - 1
some_routine(4, demo) # segfaults HERE!
I am not entirely sure what goes wrong. ctypes._CFuncPtr
is implemented in C, which could be a relevant limitation ... I could also have made a mistake in the implementation of the metaclass. Can someone enlighten me?
(For additional context, I am working on this function.)