[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.