New submission from Jérémie Detrey: Dear all,
I've been noticing a strange (and probably incorrect) behaviour of the super() function when using a metaclass in order to automatically replace a class declaration by a subclass of itself. In the attached test case, the function `wrap' is used to create such a subclass: given the original class `cls' in input, it declares and returns the subclass `Wrapper(cls)' whose constructor calls the original one from `cls': >>> def wrap(cls): ... print("In wrap() for class %s" % cls.__name__) ... name = cls.__name__ ... class Wrapper(cls): ... def __init__(self, *args, **kwargs): ... print("In Wrapper(%s).__init__()" % name) ... super().__init__(*args, **kwargs) ... return Wrapper ... When `wrap' is used as a decorator (as for class `B' in the test case), the correct behaviour is observed: the identifier `B' now represents the `Wrapper(B)' subclass, the MRO is as expected (`Wrapper', then the original class `B', then `object'), and instantiating a `B' object correctly calls the constructors in the right order: >>> @wrap ... class B: ... def __init__(self): ... print("In B.__init__()") ... super().__init__() ... In wrap() for class B >>> B.mro() [<class '__main__.wrap.<locals>.Wrapper'>, <class '__main__.B'>, <class 'object'>] >>> B() In Wrapper(B).__init__() In B.__init__() <__main__.wrap.<locals>.Wrapper object at 0x7fa8c6adb410> Now, let us automatically call the `wrap' function from a metaclass' `__new__' method upon declaration of the class (such as class `A' in the test case): >>> class Metaclass(type): ... def __new__(meta, name, bases, namespace): ... print("In Metaclass.__new__() for class %s" % name) ... cls = super().__new__(meta, name, bases, namespace) ... return cls if cls.__name__ == 'Wrapper' else wrap(cls) ... >>> class A(metaclass = Metaclass): ... def __init__(self): ... print("In A.__init__()") ... super().__init__() ... In Metaclass.__new__() for class A In wrap() for class A In Metaclass.__new__() for class Wrapper The MRO still looks correct: >>> A.mro() [<class '__main__.wrap.<locals>.Wrapper'>, <class '__main__.A'>, <class 'object'>] However, instantiating the class `A' creates an infinite recursion in the original constructor `A.__init__', just as if the `super()' call had somehow gotten confused between the original class declared as `A' and its subclass, which is now referred to by the identifier `A': >>> A() In Wrapper(A).__init__() In A.__init__() In A.__init__() In A.__init__() In A.__init__() [...] Maybe I'm doing something wrong somewhere, but it seems to me that the behaviour should at least be the same for classes `A' and `B'. Kind regards, Jérémie. ---------- components: Interpreter Core files: bug.py messages: 210829 nosy: jdetrey priority: normal severity: normal status: open title: Incorrect behaviour of super() in a metaclass-created subclass type: behavior versions: Python 3.4, Python 3.5 Added file: http://bugs.python.org/file34020/bug.py _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue20581> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com