Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

metaclass conflict with ctypes Structure

I'm trying to create a metaclass for the class I created here: ctypes variable length structures

I want to simplify the Points class so it looks like this (Python 3.2):

class Points(c.Structure, metaclass=VariableMeta):
    _fields_ = [
        ('num_points', c.c_uint32),
        ('points', 'Point*self.num_points')
    ]
    def __init__(self):
        self.num_points = 0
        self.points = [0,]*MAX_SIZE

This is the metaclass I have so far:

class VariableMeta(type):
    def __new__(cls, name, bases, dct):
        dct['_inner_fields'] = dct['_fields_']
        dct['_fields_'] = [('_buffer', c.c_byte*MAX_PACKET_SIZE)]
        return type.__new__(cls, name, bases, dct)

    def parse(self):
        fields = []
        for name, ctype in self._inner_fields:
            if type(ctype) == str:
                ctype = eval(ctype)
            fields.append((name, ctype))
            class Inner(c.Structure, PrettyPrinter):
                _fields_ = fields
            inner = Inner.from_address(c.addressof(self._buffer))
            setattr(self, name, getattr(inner, name))
        self = inner
        return self

    def pack(self):
        fields = []
        for name, ctype in self._inner_fields:
            if type(ctype) == str:
                ctype = eval(ctype)
            fields.append((name, ctype))
        class Inner(c.Structure, PrettyPrinter):
            _fields_ = fields
        inner = Inner()
        for name, ctype in self._inner_fields:
            value = getattr(self, name)
            if type(value) == list:
                l = getattr(inner, name)
                for i in range(len(l)):
                    l[i] = getattr(self, name)[i]
            else:
                setattr(inner, name, value)
        return inner

It looks like it should work, but when I run it I get the error: TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases.

I searched for hints to the solution of this problem, but ctypes Structure looks to be implemented in a c library. I am not sure how to fix this, any help or the specific solution is appreciated!

like image 246
Jake Avatar asked Dec 06 '25 01:12

Jake


1 Answers

The problem is that ctypes.Structure uses its own custom metaclass: _ctypes.StructType. Since you inherit the metaclass from Structure, Python does not know which metaclass to use when constructing your class.

You can fix this by inheriting your metaclass from _ctypes.StructType. Since the name of the metaclass is an implementation detail of the ctypes module, I recommend writing type(ctypes.Structure) to get the metaclass dynamically.

import ctypes

class VariableMeta(type(ctypes.Structure)):
    pass

The drawback with this approach is that you limit the use of your metaclass. This might be OK if you only plan to use it for subclasses of ctypes.Structure.

Another approach is to create a intermediate metaclass that inherits from both metaclasses.

class PointsMetaClass(type(ctypes.Structure), VariableMeta):
    pass

class Points(c.Structure, metaclass=PointsMetaClass):
    # ...

Always make sure that you use super() instead of hard-coding type in your metaclass' __new__!

return super(VariableMeta, cls).__new__(cls, name, bases, dct)

As Guido once wrote: Writing metaclasses in Python will cause your head to explode!

like image 69
Ferdinand Beyer Avatar answered Dec 07 '25 16:12

Ferdinand Beyer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!