Hi,<br><br>I am using zope.interface in a twisted based project. I have found two interrelated unwanted side effects of<br>zope.interface.implements. Here is a summary and self contained examples that show the problem:<br>
<br>1) The __provides__ attribute propagates * up* an inheritance tree to classes that know nothing of zope.interface.<br><br>2) The __provides__ attribute is a descriptor that will sometime raise an AttributeError even though it is listed in dir(cls). This means that things like inspect.getmembers doesn't work. Because __provides__ propagates *up* an inheritance tree, inspect.getmember has stopped working on all of my classes even though they are plain old objects.<br>
<br>Example 1: showing how __provides__ propagates up an inheritance tree<br>========================================================<br><br>import zope.interface as zi<br><br>class A(object):<br> pass<br><br>class IB(zi.Interface):<br>
pass<br><br>print "At this point _provides__ is NOT in dir(A): ", dir(A)<br>print "And hasattr returns False: ", hasattr(A, '__provides__')<br><br># Inheriting from A gives A additional attributes like __provides__!<br>
# But A is just a plain old object.<br>class B(A):<br> zi.implements(IB)<br><br>print "But now, __provides__ is in dir(A): ", dir(A)<br># hasattr(A, '__provides__') also returns True<br>if hasattr(A, '__provides__'):<br>
print "A (which is just an object) has been infected with __provides__"<br><br>Example 2: showing how __provides__ messes up dir and inspect.getmembers<br>============================================================<br>
<br>import zope.interface as zi<br><br>class MyBase(object):<br> <br> def __new__(cls, *args, **kw):<br> inst = super(MyBase, cls).__new__(cls, *args, **kw)<br> print "__provides__ in dir(%s): %s" % (cls.__name__, '__provides__' in dir(cls))<br>
print "hasattr(%s, '__provides__'): %s" % (cls.__name__, hasattr(cls, '__provides__'))<br> return inst<br><br>class P(MyBase):<br> pass<br><br>class IQ(zi.Interface):<br> pass<br>
<br>class Q(P):<br> zi.implements(IQ)<br><br># R->MyBase->object knows nothing about zope.interface<br>class R(MyBase):<br> pass<br><br># But, when you create an R, __provides__ is there, but in an inconsistent<br>
# manner.<br>r = R()<br><br># This blows up, even though R is just a MyBase->object!<br>print "Let's try inspect.getmembers(R):"<br>import inspect<br>inspect.getmembers(R)<br><br>Summary<br>=======<br><br>
zope.interface.implements should leave base classes that are unrelated to zope.interface (they don't<br>specify an implements) untouched.<br><br>zope.interface.__provides__ should not raise AttributeError so that classes that do have <br>
the __provides__ attribute always work with inspect.getmembers.<br><br>Cheers,<br><br>Brian<br>