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]