#!/usr/bin/env python """ Here's the problem I ran into. ctypes documentation claims: It is possible to defined sub-subclasses of structures, they inherit the fields of the base class. If the subclass definition has a separate _fields_ variable, the fields specified in this are appended to the fields of the base class. Structure and union constructors accept both positional and keyword arguments. Positional arguments are used to initialize member fields in the same order as they are appear in _fields_. Keyword arguments in the constructor are interpreted as attribute assignments, so they will initialize _fields_ with the same name, or create new attributes for names not present in _fields_. And changes in 0.9.5 indicates The semantics of the _fields_ attribute in sub-subclasses of Structure and Union has been fixed. The baseclasses _fields_ list is extended, not replaced, in subclasses. Assigning _fields_ when it is no longer possible raises an error now. Unfortunately, the implementation doesn't behave exactly as I would expect. This runnable module details the problem. """ """ So here's what I want to do: """ import ctypes from traceback import print_exc class MyStruct(ctypes.Structure): _fields_ = [ ('id', ctypes.c_long), ('name', ctypes.c_wchar*32), ] class MySubStruct1(MyStruct): """ Because the structure of MySubStruct extends the structure of MyStruct, this structure can be used as a drop-in replacement for calls where the structure is passed by reference. """ _fields_ = [ ('special_name', ctypes.c_wchar*128), ] """An sure enough, this works great in all cases except where initializing from positional arguments. """ try: MySubStruct1(5, "ashbury") raise RuntimeError('expected TypeError or ValueError but ran fine') except ValueError: "too many initializers (python 2.5)" except TypeError: "too many initializers (python 2.6)" # hmm. It seems I can only initialize with positional arguments # in the sub-sub class res = MySubStruct1("param") assert res.special_name == 'param' and res.name == '' try: # maybe the structure expects the subclass params first res = MySubStruct1("param", 5) raise RuntimeError("expected TypeError or ValueError but ran fine") except ValueError: "too many initializers (python 2.5)" except TypeError: "too many initializers (python 2.6)" """ It seems the base structure attributes cannot be set by positional arguments. """ "I thought adding this to the subclass would address the issue" class MySubStruct2(MyStruct): _fields_ = [ ('special_name', ctypes.c_wchar*128), ] def __init__(self, *args, **kwargs): super_self = super(MySubStruct2, self) super_fields = super_self._fields_ super_args = args[:len(super_fields)] self_args = args[len(super_fields):] super_self.__init__(*super_args) ctypes.Structure.__init__(self, *self_args, **kwargs) """ but that didn't work because super_self.__init__ still gets the field names from the subclass. """ try: MySubStruct2(5, "downy") raise RuntimeError('expected TypeError or ValueError but ran fine') except ValueError: "too many initializers (python 2.5)" except TypeError: "too many initializers (python 2.6)" """ Then I tried adding a property, hoping that that would not be an issue during class creation. """ try: class MySubStruct3(MyStruct): _fields_ = [ ('special_name', ctypes.c_wchar*128), ] @property def _fields_(self): return DYNAMIC_TIME_ZONE_INFORMATION._fields_ + super(DYNAMIC_TIME_ZONE_INFORMATION)._fields_ raise RuntimeError('expected TypeError but ran fine') except TypeError: "_fields_ must be a sequence of pairs" """ I then tried reassigning the _fields_ member of the subclass after it had been created... """ class MySubStruct4(MyStruct): _fields_ = [ ('special_name', ctypes.c_wchar*128), ] try: MySubStruct4._fields_ = MyStruct._fields_ + MySubStruct4._fields_ raise RuntimeError('expected AttributeError but ran fine') except AttributeError: "_fields_ attribute is final" """Finally, I settled on this:""" class MySubStructFinal(MyStruct): _fields_ = [ ('special_name', ctypes.c_wchar*128), ] def __init__(self, *args, **kwargs): """Allow initialization from args from both this class and its superclass. Default ctypes implementation seems to assume that this class is only initialized with its own _fields_ (for non-keyword-args).""" super_self = super(MySubStructFinal, self) super_fields = super_self._fields_ super_args = args[:len(super_fields)] self_args = args[len(super_fields):] # convert the super args to keyword args so they're also handled for field, arg in zip(super_fields, super_args): field_name, spec = field kwargs[field_name] = arg super(MySubStructFinal, self).__init__(*self_args, **kwargs) s = MySubStructFinal(6, 'driver', 'module') assert s.name == 'driver' and s.special_name == 'module'