Possible bug in "metaclass resolution order" ?

Bengt Richter bokr at oz.net
Mon Sep 19 01:22:09 EDT 2005


On Sat, 17 Sep 2005 12:41:09 -0300, Pedro Werneck <pedro.werneck at terra.com.br> wrote:

>On 17 Sep 2005 02:04:39 -0700
>"Simon Percivall" <percivall at gmail.com> wrote:
>
>> Have you read the "Metaclasses" part of "Unifying types and classes in
>> Python 2.2"? (http://www.python.org/2.2.3/descrintro.html#metaclasses)
>
>Yes, I read. Have you read and understood my message ? :)
>
>A class B, subclass of class A, with a metaclass M_A should have M_A or
>a subclass of it as metaclass. If you try with anything else, you get a
>TypeError exception as expected. OK. But if you try with 'type', nothing
>happens. 
>
>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.
>>>> class M_A(type): pass
>... 
>>>> class A: __metaclass__ = M_A
>... 
>>>> class B(A): __metaclass__ = type
>... 
>>>> B.__class__
><class '__main__.M_A'>
>>>> B.__metaclass__
><type 'type'>
>
FWIW, I think __metaclass__ can be any callable, but it seems to be the
first argument to type.__new__ that invokes the checking and
type(name, bases, cdict) seems to have the same effect as
type.__new__(type, name, bases, cdict). Maybe there has to be
a "real" class on the mro, and type isn't one, or it's a wild card ;-)

I haven't really grokked the error message's true meaning, not having
dealt with metaclass conflict before. Not ready today, sorry ;-)

 >>> class M_A(type): pass
 ...
 >>> class A: __metaclass__ = M_A
 ...
 >>> def foo(*args): print args; return 'silliness'
 ...
 >>> def foo(cname, cbases, cdict):
 ...     print 'cname, cbases:', cname, cbases
 ...     print 'cdict:', cdict
 ...     mt = type('M_B',(type,),{})
 ...     print 'mt:', mt
 ...     print 'mt.mro(mt):', mt.mro(mt)
 ...     print 'mt.__new__:', mt.__new__
 ...     something = mt.__new__(mt, cname, cbases, cdict)
 ...     print 'something:', something
 ...     return something
 ...
 >>> class B(A): __metaclass__ = foo
 ...
 cname, cbases: B (<class '__main__.A'>,)
 cdict: {'__module__': '__main__', '__metaclass__': <function foo at 0x02EEBD84>}
 mt: <class '__main__.M_B'>
 mt.mro(mt): [<class '__main__.M_B'>, <type 'type'>, <type 'object'>]
 mt.__new__: <built-in method __new__ of type object at 0x1E1BF670>
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 8, in foo
 TypeError: Error when calling the metaclass bases
     metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the
 metaclasses of all its bases
 >>> def bar(cname, cbases, cdict):
 ...     print 'cname, cbases:', cname, cbases
 ...     print 'cdict:', cdict
 ...     mt = type
 ...     print 'mt:', mt
 ...     print 'mt.mro(mt):', mt.mro(mt)
 ...     print 'mt.__new__:', mt.__new__
 ...     something = mt.__new__(mt, cname, cbases, cdict)
 ...     print 'something:', something
 ...     return something
 ...
 >>> class B(A): __metaclass__ = bar
 ...
 cname, cbases: B (<class '__main__.A'>,)
 cdict: {'__module__': '__main__', '__metaclass__': <function bar at 0x02EEBDF4>}
 mt: <type 'type'>
 mt.mro(mt): [<type 'type'>, <type 'object'>]
 mt.__new__: <built-in method __new__ of type object at 0x1E1BF670>
 something: <class '__main__.B'>
 >>>

And the something returned, whatever it is, if no checking is triggered by normal use,
gets bound to the class name, e.g.,

 >>> class C(A): __metaclass__ = lambda *a:('silly', 'result')
 ...
 >>> C
 ('silly', 'result')

Regards,
Bengt Richter



More information about the Python-list mailing list