Possible bug in "metaclass resolution order" ?

Pedro Werneck pedro.werneck at terra.com.br
Sat Sep 17 13:46:55 EDT 2005


On 17 Sep 2005 08:51:50 -0700
"Michele Simionato" <michele.simionato at gmail.com> wrote:

Hi

> I think this is more of a documentation issue than of a bug.

No... I don't think it's a documentation issue. What's the problem with
the documentation in this case ? Trying to use 'type' as a metaclass
with a subclass of another class using a custom metaclass is an error
and should raise the same exception the metaclass conflict does. In
fact, is the same error.

> It may seems strange at first, but __metaclass__ and __class__ may be
> different.
> 
> For instance, if M is metaclass
> 
> >>> class C(object): pass
> 
> >>> C.__metaclass__ = M
> 
> you get a class with metaclass hook equal to M, but C.__class__ is
> still 'type'.

But in this case, it's a __metaclass__ attribute defined after class
creation, after the call to the metaclass found in object.__class__, not
dict["__metaclass__"] which have priority over object.__class__. The
interpreter is not aware of this __metaclass__ attribute during class
creation, since it's not in the dict passed on the type(name, bases,
dict) call.

> In you example, setting the __metaclass__ to 'type' does not change
> the metaclass of the created class, which is inherited from the base
> class. 

Yes, but, as I said, dict["__metaclass__"] has priority over the base
class __class__. If I use a metaclass which is not a subclass of my base
classes metaclass I get a TypeError: metaclass conflict exception. If I
use 'type' which is also not a subclass of my base classes metaclass, I
was supposed to get the same exception, but the interpreter ignores
dict["__metaclass__"] and use the metaclass in base class __class__. 

Seems like the problem is in Objects/typeobject.c, PyType_IsSubtype
(814-846) or the for loop in 1604-1621. Seems like PyType_IsSubtype is
returning true for a type being a subtype of it's own subtypes and it
never reaches the exception and return NULL. The problem seems to be the
third if statement and after it, when the winner and metatype are
exchanged if different.

It's more evident using 'type' because it's the base of all types, but
after looking the source code, I tested with this code:

Python 2.4.1 (#1, Sep 16 2005, 17:47:47) 
[GCC 3.3.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
c>>> class M_A(type): pass
... 
>>> class A: __metaclass__ = M_A
... 
>>> class M_B(M_A): pass
... 
>>> class B(A): __metaclass__ = M_B
... 
>>> class C(B): __metaclass__ = M_A
... 
>>> C.__class__
<class '__main__.M_B'>
>>> C.__metaclass__
<class '__main__.M_A'>
>>> 

Is this supposed to happen ? C's metaclass must be a subclass of M_B. If
I try with any other class not related to this hierarchy, I will get a
metaclass conflict error. But with M_A, the error is ignored, probably
because PyType_IsSubtype is returning 1 for M_A being a subtype of M_B
and the winner and metatype are exchanged later, so we end with M_B as
C's real type.


> I suggest you to file a documentation bug. Unfortunately the basic
> documentation about metaclasses is a bit lacking and you have to
> discover many things by trial and errors.

I still think this is a bug, not a documentation issue. 

Regards

-- 
Pedro Werneck

> 
>        Michele Simionato
> 
> -- 
> http://mail.python.org/mailman/listinfo/python-list
> 





More information about the Python-list mailing list