Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiating a just-created class in a metaclass results in a RuntimeError ("super(): empty __class__ cell")

So I have this metaclass that I want to use for automatic registration of new components, i.e. subclasses of some base component class. When registering a new component, its instance is expected to be passed to the register_component() function that handles that.

Metaclass code (stripped-down version):

class AutoRegisteredMeta(type):
    def __new__(metacls, name, bases, attrs):

        # ... (omitted) check if "name" has already been registered ...

        new_class = super().__new__(metacls, name, bases, attrs)
        register_component(name, new_class())  # RuntimeError(super(): empty __class__ cell)
        return new_class

The problem is that invoking new_class() results in an error - but not for all classes. After some experimenting I realized that this only happens if a subclass calls super().__init__() in its own __init__() method.

Sample component and the base class:

class BaseComponent(metaclass=AutoRegisteredMeta):
    def __init__(self):
        # do some work here ...

class ComponentFoo(BaseComponent):
    def __init__(self):
        super().__init__()  # <--- RuntimeError occurs here
        self.foo = 'bar'

What am I doing wrong here? Reading this I found out that I probably shouldn't be doing instantiation in metaclass'es __new__() or __init__(), right? Can this perhaps be circumvented somehow?

Also, some explanation in layman's terms would be nice, I don't know much of the internals of the CPython implementation.

Thanks in advance!

(FWIW, I use Python 3.3.6, Ubuntu)


EDIT: I'm adding the minimal example that was requested, you can run it directly and see the error in action yourself.

#!/usr/bin/env python3


class AutoRegisteredMeta(type):
    def __new__(metacls, name, bases, attrs):
        new_class = super().__new__(metacls, name, bases, attrs)
        new_class()  # <--- RuntimeError can occur here
        return new_class


class BaseComponent(metaclass=AutoRegisteredMeta):
    def __init__(self):
        print("BaseComponent __init__()")


class GoodComponent(BaseComponent):
    def __init__(self):
        print("GoodComponent __init__()")


class BadComponent(BaseComponent):
    def __init__(self):
        print("BadComponent __init__()")
        super().__init__()  # <--- RuntimeError occurs because of this
like image 843
plamut Avatar asked Jun 23 '15 12:06

plamut


1 Answers

maybe this works:

#!/usr/bin/env python3


class AutoRegisteredMeta(type):
    def __new__(metacls, name, bases, attrs):
        new_class = super().__new__(metacls, name, bases, attrs)
        new_class()
        return new_class


class BaseComponent(metaclass=AutoRegisteredMeta):
    def __init__(self):
        print("BaseComponent __init__()")


class GoodComponent(BaseComponent):
    def __init__(self):
        print("GoodComponent __init__()")


class BadComponent(BaseComponent):
    def __init__(self):
        print("BadComponent __init__()")
        super(self.__class__, self).__init__()
like image 190
svs Avatar answered Oct 09 '22 12:10

svs