[Twisted-Python] Interface.adaptWith and Interface.__adapt__

Phillip J. Eby pje at telecommunity.com
Thu Jun 5 09:00:25 EDT 2003

At 01:59 AM 6/5/03 -0400, Glyph Lefkowitz wrote:
>On Wednesday, June 4, 2003, at 06:10 PM, Phillip J. Eby wrote:
>>Anyway...  I noticed that the only code in Twisted currently (that I 
>>could find) that actually uses the current __adapt__ setup is the test 
>>suite.  Is there any way this might be changeable to something more 
>>compatible with PEP 246?
>Thanks for bringing this to my attention.  As I was perusing the 
>implementation, I realized that the reason we committed this particular 
>incompatibility was being invalidated a scant two lines away :-).  I could 
>have sworn it didn't used to have a try:except: block, but I've checked in 
>a revised version now.
>As I understand it, PEP 246 cannot be implemented without raising 
>exceptions in __adapt__.

Actually, no.  __adapt__ is also allowed to return None to signal inability 
to adapt.  And, the consensus so far of people I've talked with (Jim Fulton 
and Guido) is that PEP 246 should be revised in that area anyway, such that 
it isn't even an *option* to raise TypeError to signal 
failure.  PyProtocols will only ignore TypeError if it looks like it came 
from calling an unbound class method, not if it looks like it came from the 
__conform__ or __adapt__ method body.

>>I mean, if I jump through enough hoops building adapters and 
>>monkeypatching, I can probably make it so that this will work 
>>transparently, and anybody who uses PyProtocols will then be able to mix 
>>Twisted, Zope, and PyProtocols interfaces in their applications.
>>But it's pretty messy, and I'm wondering about the copyright status if I 
>>have to copy a lot of code from MetaInterface.__call__ in order to 
>>monkeypatch a replacement.  (I'd like to release PyProtocols under the 
>>PSF license.)
>PyProtocols looks like an interesting project, and I'd like to support 
>such an effort at interoperability.  Monkey-patching MetaInterface is 
>almost certainly the wrong thing to do.  Perhaps it would be best if we 
>did not call our interface-aware adaptation mechanism "__adapt__", but 
>something like "adaptWithDefault"?

That would work; at least, it makes the monkeypatch quite a bit simpler.

Are you actually using the __adapt__ method for anything yet?  The only 
code I found with an actual __adapt__ method was in the test suite.

>I am not sure exactly how you're doing compatibility, but perhaps then you 
>could provide a subclass of Interface that provided an adaptWithDefault 
>that called __adapt__ with the proper arguments?

PyProtocols does compatibility by:

1. defining adapters to adapt "foreign" interface types (Zope, Twisted) to 
its own IOpenProtocol interface

2. monkeypatching the foreign interface type itself (Zope InterfaceClass, 
Twisted MetaInterface) to add an __adapt__ method that implements PEP 246 
by "calling down" to the foreign type's normal adaptation methods.  E.g. 
for Zope, the __adapt__ I patch in just does:

if self.isImplementedBy(obj):
     return obj

And for Twisted, the __adapt__ I add does the equivalent of:

     def __adapt__(self, obj):

         if implements(obj, self):
             return obj

         # Get Twisted to try and adapt
         return self(ob, None)

In other words, the __adapt__ I add checks whether Twisted says the object 
already implements the interface, and if so, return it.  Otherwise, adapt 
it by calling the interface on the object, with a default of None.

If you were to switch from using __adapt__ to adaptWithDefault in your 
__call__ method, and added the above as MetaInterface.__adapt__, your 
interfaces would be PEP 246 compatible without any monkeypatching.  For 
compatibility with PyProtocols' declaration API, I would just supply an 

(From recent conversations with Jim Fulton, it sounds like he plans to 
eventually add a PEP 246-compatible __adapt__ method to Zope interfaces as 
well, so at that point I could drop the monkeypatching for Zope too.)

I've done a preliminary sketch of an adapter for Twisted interfaces, but 
there are a few holes because I'm not 100% clear on the intended semantics 
of Twisted interfaces in some areas.  Actually, I get the impression that 
that's because Twisted hasn't really defined those semantics either.  For 
example, Twisted doesn't appear to distinguish between "object implements" 
and "class implements".  If I call 'getInterfaces()' on a class, it will 
tell me that the class implements the same interfaces that its instances 
do.  From my POV, that doesn't make any sense, but perhaps it is a 
documented or at least intended limitation?  Certainly, I will need to 
document it in my "Compatibility" section (not yet written).

For right now, my Twisted adapter just pokes a new __implements__ onto 
whatever object is having declarations made about it, whether it's a type 
declaration or an instance declaration.  This means that PyProtocols will 
behave oddly if you pass a class to adapt().  Of course, Twisted will also 
behave oddly if you pass a class to ISomething().

I'm also having trouble figuring out how to apply transitive adapter 
semantics to Twisted.  AFAICT, Twisted adapters are not normally 
transitive.  However, unlike PyProtocols, Twisted interfaces don't know 
about all their adaptations, so there doesn't seem to be a way to issue 
declarations to make them transitive.  But I guess that's optional, since I 
didn't really intend to make PyProtocols add any new functionality to Zope 
or Twisted, just make it reasonably possible to interact with them.

It should be possible at this point to have a PyProtocols interface declare 
that it extends both a Twisted and a Zope interface, without any metaclass 
incompatibilities.  Of course, Twisted and Zope still use mutually 
incompatible definitions of '__implements__', so actually *using* an 
interface that extended both a Twisted and a Zope interface would be 
problematic.  :)

More information about the Twisted-Python mailing list