[Zope3-dev] Follow-up: Interface package broken for Python 2.2 metaclasses

Phillip J. Eby pje@telecommunity.com
Thu, 31 Jan 2002 23:06:53 -0500


At 06:49 PM 1/31/02 -0500, Phillip J. Eby wrote:

>The problem is that the routines in the Interface.iclass module check 
>whether a type is in 'ClassTypes' in order to know if an object is a class 
>(and thus whether '__implements__' or '__class_implements__' should be 
>used).  If you have a class based on a Python metaclass, the Interface 
>package will think the class implements what its instances implement, 
>which leads to the collapse of the type system as we know it.  :)

I think I may have come up with a better and simpler fix...  it seems that 
if 'isinstance(x,type)' is true, then 'x' is either a type, or a metaclass 
instance.  There is, however, no way I can think of to distinguish between 
the two that will be consistent, but then again all the Interface package 
really needs to know is whether to look for '__implements__' or 
'__class_implements__', so this should suffice.

Incidentally, the way iclass.py reads right now, it already will consider 
standard type objects to be classes, so changing to use 
'isinstance(x,type)' and removing 'type(object)' from 'ClassTypes' does not 
change the existing behavior.

Let me try and make this clearer...  the fix to ensure detection of 
metaclass instances as types/classes (i.e. things that use 
'__class_implements__') is to:

1. Change the 't in ClassTypes' test to 't in ClassTypes or 
isinstance(object,type)'

2. Change the 'type(klass) in ClassTypes' test to 'type(klass) in 
ClassTypes or isinstance(klass,type)'

3. Remove 'type(object)' from the 'ClassTypes' tuple, since its presence is 
now redundant.

Applying these changes to my local copy of the Interface.iclass module 
(after removing my previous 'ClassTypes'-to-dictionary hack), appears to 
work correctly.  The TransWarp test suite involves processing literally 
thousands of objects through interface checks, including ExtensionClasses, 
metaclasses (new and old styles), metaclass instances (ditto), classic 
classes, and new-style classes, so that should be a pretty good indication 
the patch works correctly.  :)

The fix is not, however, 100% perfect.  It is easy to create meta-metatypes 
that will break the test...  A simple example::

     class metaOne(type):
         pass

     class metaTwo:
         __metaclass__ = metaOne

     class metaThree:
         __metaclass__ = metaTwo

'isinstance(metaOne,type)' will be true, as will 
'isinstance(metaTwo,type)'.  But 'isinstance(metaThree,type)' will 
fail.  In practice, this is easily fixed by ensuring that 'metaTwo' 
inherits from 'type', but perhaps someone with a more intimate 
understanding of the type/class continuum can offer a better argument one 
way or another.