pythonzope.interfacezope.component

How to get "cast like" adaption to work with pure zope.interface?


I would like to get the "C++ cast like" adaption to work with the code from zope.interface. In my real use case, I'm using a registry from Pyramid but it derives from zope.interface.registry.Components, which according to the changes.txt was introduced to be able to use this stuff without any dependency on zope.components. And the following example is complete and self contained:

from zope.interface import Interface, implements                                 
from zope.interface.registry import Components  

registry = Components()                                                          

class IA(Interface):                                                             
    pass                                                                         

class IB(Interface):                                                             
    pass                                                                         

class A(object):                                                                 
    implements(IA)                                                               

class B(object):                                                                 
    implements(IB)                                                               
    def __init__(self,other):                                                    
        pass                                                                     

registry.registerAdapter(                                                        
    factory=B,                                                                   
    required=[IA]                                                                
)                                                                                

a = A()                                                                          
b = registry.getAdapter(a,IB) # why instance of B and not B?                                                 
b = IB(A()) # how to make it work?

I wonder why registry.getAdapter already returns the adapted object, which is an instance of B in my case. I would have expected to get back the class B, but perhaps my understanding of the term adapter is wrong. As this line works and obviously the adapting code is registered correctly, I would also expect the last line to work. But it fails with an error like this:

TypeError: ('Could not adapt', <....A object at 0x4d1c3d0>, < InterfaceClass ....IB>)

Any idea how to get this working?


Solution

  • To make IB(A()) work, you need to add a hook to the the zope.interface.adapter_hooks list; the IAdapterRegistry interface has a dedicated IAdapterRegistry.adapter_hook method we can use for this:

    from zope.interface.interface import adapter_hooks
    
    adapter_hooks.append(registry.adapters.adapter_hook)
    

    See Adaptation in the zope.interface README.

    You can use the IAdapterRegistry.lookup1() method to do single-adapter lookups without invoking the factory:

    from zope.interface import providedBy
    
    adapter_factory = registry.adapters.lookup1(providedBy(a), IB)
    

    Building on your sample:

    >>> from zope.interface.interface import adapter_hooks
    >>> adapter_hooks.append(registry.adapters.adapter_hook)
    >>> a = A()
    >>> IB(a)
    <__main__.B object at 0x100721110>
    >>> from zope.interface import providedBy
    >>> registry.adapters.lookup1(providedBy(a), IB)
    <class '__main__.B'>