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 <[email protected]>
<http://bugs.python.org/issue20581>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com