5 Adapter registries provide a way to register objects that depend on
6 one or more interface specifications and provide (perhaps indirectly)
7 some interface. In addition, the registrations have names. (You can
8 think of the names as qualifiers of the provided interfaces.)
10 The term "interface specification" refers both to interfaces and to
11 interface declarations, such as declarations of interfaces implemented
18 Let's look at a simple example, using a single required specification::
20 >>> from zope.interface.adapter import AdapterRegistry
21 >>> import zope.interface
23 >>> class IR1(zope.interface.Interface):
25 >>> class IP1(zope.interface.Interface):
30 >>> registry = AdapterRegistry()
32 We'll register an object that depends on IR1 and "provides" IP2::
34 >>> registry.register([IR1], IP2, '', 12)
36 Given the registration, we can look it up again::
38 >>> registry.lookup([IR1], IP2, '')
41 Note that we used an integer in the example. In real applications,
42 one would use some objects that actually depend on or provide
43 interfaces. The registry doesn't care about what gets registered, so
44 we'll use integers and strings to keep the examples simple. There is
45 one exception. Registering a value of None unregisters any
46 previously-registered value.
48 If an object depends on a specification, it can be looked up with a
49 specification that extends the specification that it depends on::
53 >>> registry.lookup([IR2], IP2, '')
56 We can use a class implementation specification to look up the object::
59 ... zope.interface.implements(IR2)
61 >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '')
65 and it can be looked up for interfaces that its provided interface
68 >>> registry.lookup([IR1], IP1, '')
70 >>> registry.lookup([IR2], IP1, '')
73 But if you require a specification that doesn't extend the specification the
74 object depends on, you won't get anything::
76 >>> registry.lookup([zope.interface.Interface], IP1, '')
78 By the way, you can pass a default value to lookup::
80 >>> registry.lookup([zope.interface.Interface], IP1, '', 42)
83 If you try to get an interface the object doesn't provide, you also
88 >>> registry.lookup([IR1], IP3, '')
90 You also won't get anything if you use the wrong name::
92 >>> registry.lookup([IR1], IP1, 'bob')
93 >>> registry.register([IR1], IP2, 'bob', "Bob's 12")
94 >>> registry.lookup([IR1], IP1, 'bob')
97 You can leave the name off when doing a lookup::
99 >>> registry.lookup([IR1], IP1)
102 If we register an object that provides IP1::
104 >>> registry.register([IR1], IP1, '', 11)
106 then that object will be prefered over O(12)::
108 >>> registry.lookup([IR1], IP1, '')
111 Also, if we register an object for IR2, then that will be prefered
114 >>> registry.register([IR2], IP1, '', 21)
115 >>> registry.lookup([IR2], IP1, '')
118 Finding out what, if anything, is registered
119 --------------------------------------------
121 We can ask if there is an adapter registered for a collection of
122 interfaces. This is different than lookup, because it looks for an
125 >>> print registry.registered([IR1], IP1)
128 >>> print registry.registered([IR1], IP2)
131 >>> print registry.registered([IR1], IP2, 'bob')
135 >>> print registry.registered([IR2], IP1)
138 >>> print registry.registered([IR2], IP2)
141 In the last example, None was returned because nothing was registered
142 exactly for the given interfaces.
147 Lookup of single adapters is common enough that there is a specialized
148 version of lookup that takes a single required interface::
150 >>> registry.lookup1(IR2, IP1, '')
152 >>> registry.lookup1(IR2, IP1)
158 The adapter registry is intended to support adaptation, where one
159 object that implements an interface is adapted to another object that
160 supports a different interface. The adapter registry supports the
161 computation of adapters. In this case, we have to register adapter
164 >>> class IR(zope.interface.Interface):
168 ... zope.interface.implements(IR)
171 ... zope.interface.implements(IP1)
172 ... def __init__(self, context):
173 ... self.context = context
175 >>> registry.register([IR], IP1, '', Y)
177 In this case, we registered a class as the factory. Now we can call
178 `queryAdapter` to get the adapted object::
181 >>> y = registry.queryAdapter(x, IP1)
182 >>> y.__class__.__name__
187 We can register and lookup by name too::
192 >>> registry.register([IR], IP1, 'bob', Y2)
193 >>> y = registry.queryAdapter(x, IP1, 'bob')
194 >>> y.__class__.__name__
199 When the adapter factory produces `None`, then this is treated as if no
200 adapter has been found. This allows us to prevent adaptation (when desired)
201 and let the adapter factory determine whether adaptation is possible based on
202 the state of the object being adapted.
204 >>> def factory(context):
205 ... if context.name == 'object':
209 >>> class Object(object):
210 ... zope.interface.implements(IR)
213 >>> registry.register([IR], IP1, 'conditional', factory)
215 >>> registry.queryAdapter(obj, IP1, 'conditional')
217 >>> obj.name = 'no object'
218 >>> registry.queryAdapter(obj, IP1, 'conditional') is None
220 >>> registry.queryAdapter(obj, IP1, 'conditional', 'default')
223 An alternate method that provides the same function as `queryAdapter()` is
226 >>> y = registry.adapter_hook(IP1, x)
227 >>> y.__class__.__name__
231 >>> y = registry.adapter_hook(IP1, x, 'bob')
232 >>> y.__class__.__name__
237 The `adapter_hook()` simply switches the order of the object and
238 interface arguments. It is used to hook into the interface call
245 Sometimes, you want to provide an adapter that will adapt anything.
246 For that, provide None as the required interface::
248 >>> registry.register([None], IP1, '', 1)
250 then we can use that adapter for interfaces we don't have specific
253 >>> class IQ(zope.interface.Interface):
255 >>> registry.lookup([IQ], IP1, '')
258 Of course, specific adapters are still used when applicable::
260 >>> registry.lookup([IR2], IP1, '')
266 You can register adapters for class declarations, which is almost the
267 same as registering them for a class::
269 >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21')
270 >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
276 At some point it was impossible to register dictionary-based adapters due a
277 bug. Let's make sure this works now:
280 >>> registry.register((), IQ, '', adapter)
281 >>> registry.lookup((), IQ, '') is adapter
287 You can unregister by registering None, rather than an object::
289 >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None)
290 >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
293 Of course, this means that None can't be registered. This is an
294 exception to the statement, made earlier, that the registry doesn't
295 care what gets registered.
300 You can adapt multiple specifications::
302 >>> registry.register([IR1, IQ], IP2, '', '1q2')
303 >>> registry.lookup([IR1, IQ], IP2, '')
305 >>> registry.lookup([IR2, IQ], IP1, '')
308 >>> class IS(zope.interface.Interface):
310 >>> registry.lookup([IR2, IS], IP1, '')
315 >>> registry.lookup([IR2, IQ2], IP1, '')
318 >>> registry.register([IR1, IQ2], IP2, '', '1q22')
319 >>> registry.lookup([IR2, IQ2], IP1, '')
325 You can adapt multiple objects::
328 ... zope.interface.implements(IQ)
330 As with single adapters, we register a factory, which is often a class::
332 >>> class IM(zope.interface.Interface):
335 ... zope.interface.implements(IM)
336 ... def __init__(self, x, q):
337 ... self.x, self.q = x, q
338 >>> registry.register([IR, IQ], IM, '', M)
340 And then we can call `queryMultiAdapter` to compute an adapter::
343 >>> m = registry.queryMultiAdapter((x, q), IM)
344 >>> m.__class__.__name__
346 >>> m.x is x and m.q is q
349 and, of course, we can use names::
353 >>> registry.register([IR, IQ], IM, 'bob', M2)
354 >>> m = registry.queryMultiAdapter((x, q), IM, 'bob')
355 >>> m.__class__.__name__
357 >>> m.x is x and m.q is q
363 As with single adapters, you can define default adapters by specifying
364 None for the *first* specification::
366 >>> registry.register([None, IQ], IP2, '', 'q2')
367 >>> registry.lookup([IS, IQ], IP2, '')
373 You can also adapt no specification::
375 >>> registry.register([], IP2, '', 2)
376 >>> registry.lookup([], IP2, '')
378 >>> registry.lookup([], IP1, '')
381 Listing named adapters
382 ----------------------
384 Adapters are named. Sometimes, it's useful to get all of the named
385 adapters for given interfaces::
387 >>> adapters = list(registry.lookupAll([IR1], IP1))
389 >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")]
391 This works for multi-adapters too::
393 >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob')
394 >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1))
396 >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')]
398 And even null adapters::
400 >>> registry.register([], IP2, 'bob', 3)
401 >>> adapters = list(registry.lookupAll([], IP1))
403 >>> assert adapters == [(u'', 2), (u'bob', 3)]
408 Normally, we want to look up an object that most-closely matches a
409 specification. Sometimes, we want to get all of the objects that
410 match some specification. We use subscriptions for this. We
411 subscribe objects against specifications and then later find all of
412 the subscribed objects::
414 >>> registry.subscribe([IR1], IP2, 'sub12 1')
415 >>> registry.subscriptions([IR1], IP2)
418 Note that, unlike regular adapters, subscriptions are unnamed.
420 You can have multiple subscribers for the same specification::
422 >>> registry.subscribe([IR1], IP2, 'sub12 2')
423 >>> registry.subscriptions([IR1], IP2)
424 ['sub12 1', 'sub12 2']
426 If subscribers are registered for the same required interfaces, they
427 are returned in the order of definition.
429 You can register subscribers for all specifications using None::
431 >>> registry.subscribe([None], IP1, 'sub_1')
432 >>> registry.subscriptions([IR2], IP1)
433 ['sub_1', 'sub12 1', 'sub12 2']
435 Note that the new subscriber is returned first. Subscribers defined
436 for less general required interfaces are returned before subscribers
437 for more general interfaces.
439 Subscriptions may be combined over multiple compatible specifications::
441 >>> registry.subscriptions([IR2], IP1)
442 ['sub_1', 'sub12 1', 'sub12 2']
443 >>> registry.subscribe([IR1], IP1, 'sub11')
444 >>> registry.subscriptions([IR2], IP1)
445 ['sub_1', 'sub12 1', 'sub12 2', 'sub11']
446 >>> registry.subscribe([IR2], IP2, 'sub22')
447 >>> registry.subscriptions([IR2], IP1)
448 ['sub_1', 'sub12 1', 'sub12 2', 'sub11', 'sub22']
449 >>> registry.subscriptions([IR2], IP2)
450 ['sub12 1', 'sub12 2', 'sub22']
452 Subscriptions can be on multiple specifications::
454 >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2')
455 >>> registry.subscriptions([IR1, IQ], IP2)
458 As with single subscriptions and non-subscription adapters, you can
459 specify None for the first required interface, to specify a default::
461 >>> registry.subscribe([None, IQ], IP2, 'sub_q2')
462 >>> registry.subscriptions([IS, IQ], IP2)
464 >>> registry.subscriptions([IR1, IQ], IP2)
467 You can have subscriptions that are indepenent of any specifications::
469 >>> list(registry.subscriptions([], IP1))
472 >>> registry.subscribe([], IP2, 'sub2')
473 >>> registry.subscriptions([], IP1)
475 >>> registry.subscribe([], IP1, 'sub1')
476 >>> registry.subscriptions([], IP1)
478 >>> registry.subscriptions([], IP2)
481 Unregistering subscribers
482 -------------------------
484 We can unregister subscribers. When unregistering a subscriber, we
485 can unregister a specific subscriber::
487 >>> registry.unsubscribe([IR1], IP1, 'sub11')
488 >>> registry.subscriptions([IR1], IP1)
489 ['sub_1', 'sub12 1', 'sub12 2']
491 If we don't specify a value, then all subscribers matching the given
492 interfaces will be unsubscribed:
494 >>> registry.unsubscribe([IR1], IP2)
495 >>> registry.subscriptions([IR1], IP1)
499 Subscription adapters
500 ---------------------
502 We normally register adapter factories, which then allow us to compute
503 adapters, but with subscriptions, we get multiple adapters. Here's an
504 example of multiple-object subscribers::
506 >>> registry.subscribe([IR, IQ], IM, M)
507 >>> registry.subscribe([IR, IQ], IM, M2)
509 >>> subscribers = registry.subscribers((x, q), IM)
512 >>> class_names = [s.__class__.__name__ for s in subscribers]
513 >>> class_names.sort()
516 >>> [(s.x is x and s.q is q) for s in subscribers]
519 adapter factory subcribers can't return None values::
524 >>> registry.subscribe([IR, IQ], IM, M3)
525 >>> subscribers = registry.subscribers((x, q), IM)
532 A handler is a subscriber factory that doesn't produce any normal
533 output. It returns None. A handler is unlike adapters in that it does
534 all of its work when the factory is called.
536 To register a handler, simply provide None as the provided interface::
538 >>> def handler(event):
539 ... print 'handler', event
541 >>> registry.subscribe([IR1], None, handler)
542 >>> registry.subscriptions([IR1], None) == [handler]