2

Given the following code:

from zope.component import getGlobalSiteManager, adapts, subscribers
from zope.interface import Interface, implements


class A(object): pass
class B(object): pass
class C(B): pass

class AB(object):
    implements(Interface)
    adapts(A, B)

    def __init__(self, a, b):
        pass

class AC(object):
    implements(Interface)
    adapts(A, C)

    def __init__(self, a, c):
        pass

gsm = getGlobalSiteManager()
gsm.registerSubscriptionAdapter(AB)
gsm.registerSubscriptionAdapter(AC)

a = A()
c = C()

for adapter in subscribers([a, c], Interface):
    print adapter

The output it produces is:

<__main__.AB object at 0xb242290>
<__main__.AC object at 0xb2422d0>

Why is an instance of AB returned? AB only declares that it adapts A and B. Is there a way I can achieve behavior where only AC would be returned?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Ben
  • 2,422
  • 2
  • 16
  • 23

1 Answers1

2

You are listing subscribers. Subscribers are notified of all things that implement the interface(s) they are interested in.

C is a subclass of B, so the B subscriber is interested, and will be notified. The fact that C implements a little more is of no concern to the B subscriber, as the object will implement at least the B interface.

Subscribers are generic, they just want objects that implement their interface or subclasses thereof. Adapters are more specific:

>>> gsm.registerAdapter(AB)
>>> gsm.registerAdapter(AC)
>>> from zope.component import getAdapters
>>> for adapter in getAdapters((a, c), Interface):
...     print adapter
... 
(u'', <__main__.AC object at 0x104b25a90>)

getAdapters() enumerates all registered adapters, together with their names:

>>> class AnotherAC(object):
...     implements(Interface)
...     adapts(A, C)
...     def __init__(self, a, c): pass
... 
>>> gsm.registerAdapter(AnotherAC, name=u'another')
>>> for adapter in getAdapters((a, c), Interface):
...     print adapter
... 
(u'', <__main__.AC object at 0x104b25ed0>)
(u'another', <__main__.AnotherAC object at 0x104b25a90>)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Is there a way to use named subscribers? I can see how to register them, but not how to retrieve them. – Ben May 02 '13 at 16:18
  • @Ben: Named subscribers or named adapters? – Martijn Pieters May 02 '13 at 16:18
  • @Ben: named subscribers are actually a feature that has yet to be implemented (registering a name for a subscriber raises a `TypeError` at the moment). – Martijn Pieters May 02 '13 at 16:21
  • @Ben: Perhaps you want to specify your use case? I suspect that *regular* adapters will do what you want. – Martijn Pieters May 02 '13 at 16:23
  • Just converted from regular adapters. Basically, I need to ability to create multiple adapters for a given thing to be adapted and interface -- hence subscribers. With adapters this was not an issue as it would choose the adapter that most directly adapted what I wanted. – Ben May 02 '13 at 16:32
  • @Ben: I use named adapters for such cases, together with `getAdapters()` to enumerate them. – Martijn Pieters May 02 '13 at 16:35
  • I can not think of a solution other than to directly validate that each subscriber directly adapts the instances it is adapting. – Ben May 02 '13 at 16:36
  • In other words, the adapter name is just used to register multiple adapters for the same class and interface, then discarded? – Ben May 02 '13 at 16:37
  • Thanks a lot. This looks like the most elegant way to accomplish what I am trying for. – Ben May 02 '13 at 16:42
  • @Ben: The name lets you register multiple adapters for the same combo of adaptees and target interface, each with a unique name. – Martijn Pieters May 02 '13 at 16:42