~crunch.io/ubuntu/precise/zope.interface/unstable

« back to all changes in this revision

Viewing changes to src/zope/interface/adapter.txt

  • Committer: Joseph Tate
  • Date: 2013-02-04 19:16:30 UTC
  • mfrom: (1.1.4)
  • Revision ID: jtate@dragonstrider.com-20130204191630-ye0hqihd37tdk47r
Update package version

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
================
2
 
Adapter Registry
3
 
================
4
 
 
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.)
9
 
 
10
 
The term "interface specification" refers both to interfaces and to
11
 
interface declarations, such as declarations of interfaces implemented
12
 
by a class.
13
 
 
14
 
 
15
 
Single Adapters
16
 
===============
17
 
 
18
 
Let's look at a simple example, using a single required specification::
19
 
 
20
 
  >>> from zope.interface.adapter import AdapterRegistry
21
 
  >>> import zope.interface
22
 
 
23
 
  >>> class IR1(zope.interface.Interface):
24
 
  ...     pass
25
 
  >>> class IP1(zope.interface.Interface):
26
 
  ...     pass
27
 
  >>> class IP2(IP1):
28
 
  ...     pass
29
 
 
30
 
  >>> registry = AdapterRegistry()
31
 
 
32
 
We'll register an object that depends on IR1 and "provides" IP2::
33
 
 
34
 
  >>> registry.register([IR1], IP2, '', 12)
35
 
 
36
 
Given the registration, we can look it up again::
37
 
 
38
 
  >>> registry.lookup([IR1], IP2, '')
39
 
  12
40
 
 
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.
47
 
 
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::
50
 
 
51
 
  >>> class IR2(IR1):
52
 
  ...     pass
53
 
  >>> registry.lookup([IR2], IP2, '')
54
 
  12
55
 
 
56
 
We can use a class implementation specification to look up the object::
57
 
 
58
 
  >>> class C2:
59
 
  ...     zope.interface.implements(IR2)
60
 
 
61
 
  >>> registry.lookup([zope.interface.implementedBy(C2)], IP2, '')
62
 
  12
63
 
 
64
 
 
65
 
and it can be looked up for interfaces that its provided interface
66
 
extends::
67
 
 
68
 
  >>> registry.lookup([IR1], IP1, '')
69
 
  12
70
 
  >>> registry.lookup([IR2], IP1, '')
71
 
  12
72
 
 
73
 
But if you require a specification that doesn't extend the specification the
74
 
object depends on, you won't get anything::
75
 
 
76
 
  >>> registry.lookup([zope.interface.Interface], IP1, '')
77
 
 
78
 
By the way, you can pass a default value to lookup::
79
 
 
80
 
  >>> registry.lookup([zope.interface.Interface], IP1, '', 42)
81
 
  42
82
 
 
83
 
If you try to get an interface the object doesn't provide, you also
84
 
won't get anything::
85
 
 
86
 
  >>> class IP3(IP2):
87
 
  ...     pass
88
 
  >>> registry.lookup([IR1], IP3, '')
89
 
 
90
 
You also won't get anything if you use the wrong name::
91
 
 
92
 
  >>> registry.lookup([IR1], IP1, 'bob')
93
 
  >>> registry.register([IR1], IP2, 'bob', "Bob's 12")
94
 
  >>> registry.lookup([IR1], IP1, 'bob')
95
 
  "Bob's 12"
96
 
 
97
 
You can leave the name off when doing a lookup::
98
 
 
99
 
  >>> registry.lookup([IR1], IP1)
100
 
  12
101
 
 
102
 
If we register an object that provides IP1::
103
 
 
104
 
  >>> registry.register([IR1], IP1, '', 11)
105
 
 
106
 
then that object will be prefered over O(12)::
107
 
 
108
 
  >>> registry.lookup([IR1], IP1, '')
109
 
  11
110
 
 
111
 
Also, if we register an object for IR2, then that will be prefered
112
 
when using IR2::
113
 
 
114
 
  >>> registry.register([IR2], IP1, '', 21)
115
 
  >>> registry.lookup([IR2], IP1, '')
116
 
  21
117
 
 
118
 
Finding out what, if anything, is registered
119
 
--------------------------------------------
120
 
 
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
123
 
exact match.
124
 
 
125
 
  >>> print registry.registered([IR1], IP1)
126
 
  11
127
 
 
128
 
  >>> print registry.registered([IR1], IP2)
129
 
  12
130
 
 
131
 
  >>> print registry.registered([IR1], IP2, 'bob')
132
 
  Bob's 12
133
 
  
134
 
 
135
 
  >>> print registry.registered([IR2], IP1)
136
 
  21
137
 
 
138
 
  >>> print registry.registered([IR2], IP2)
139
 
  None
140
 
 
141
 
In the last example, None was returned because nothing was registered
142
 
exactly for the given interfaces.
143
 
 
144
 
lookup1
145
 
-------
146
 
 
147
 
Lookup of single adapters is common enough that there is a specialized
148
 
version of lookup that takes a single required interface::
149
 
 
150
 
  >>> registry.lookup1(IR2, IP1, '')
151
 
  21
152
 
  >>> registry.lookup1(IR2, IP1)
153
 
  21
154
 
 
155
 
Actual Adaptation
156
 
-----------------
157
 
 
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
162
 
factories::
163
 
 
164
 
   >>> class IR(zope.interface.Interface):
165
 
   ...     pass
166
 
 
167
 
   >>> class X:
168
 
   ...     zope.interface.implements(IR)
169
 
           
170
 
   >>> class Y:
171
 
   ...     zope.interface.implements(IP1)
172
 
   ...     def __init__(self, context):
173
 
   ...         self.context = context
174
 
 
175
 
  >>> registry.register([IR], IP1, '', Y)
176
 
 
177
 
In this case, we registered a class as the factory. Now we can call
178
 
`queryAdapter` to get the adapted object::
179
 
 
180
 
  >>> x = X()
181
 
  >>> y = registry.queryAdapter(x, IP1)
182
 
  >>> y.__class__.__name__
183
 
  'Y'
184
 
  >>> y.context is x
185
 
  True
186
 
 
187
 
We can register and lookup by name too::
188
 
 
189
 
  >>> class Y2(Y):
190
 
  ...     pass
191
 
 
192
 
  >>> registry.register([IR], IP1, 'bob', Y2)
193
 
  >>> y = registry.queryAdapter(x, IP1, 'bob')
194
 
  >>> y.__class__.__name__
195
 
  'Y2'
196
 
  >>> y.context is x
197
 
  True
198
 
 
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.
203
 
 
204
 
  >>> def factory(context):
205
 
  ...     if context.name == 'object':
206
 
  ...         return 'adapter'
207
 
  ...     return None
208
 
 
209
 
  >>> class Object(object):
210
 
  ...     zope.interface.implements(IR)
211
 
  ...     name = 'object'
212
 
 
213
 
  >>> registry.register([IR], IP1, 'conditional', factory) 
214
 
  >>> obj = Object()
215
 
  >>> registry.queryAdapter(obj, IP1, 'conditional')
216
 
  'adapter'
217
 
  >>> obj.name = 'no object'
218
 
  >>> registry.queryAdapter(obj, IP1, 'conditional') is None
219
 
  True
220
 
  >>> registry.queryAdapter(obj, IP1, 'conditional', 'default')
221
 
  'default'
222
 
 
223
 
An alternate method that provides the same function as `queryAdapter()` is
224
 
`adapter_hook()`::
225
 
 
226
 
  >>> y = registry.adapter_hook(IP1, x)
227
 
  >>> y.__class__.__name__
228
 
  'Y'
229
 
  >>> y.context is x
230
 
  True
231
 
  >>> y = registry.adapter_hook(IP1, x, 'bob')
232
 
  >>> y.__class__.__name__
233
 
  'Y2'
234
 
  >>> y.context is x
235
 
  True
236
 
 
237
 
The `adapter_hook()` simply switches the order of the object and
238
 
interface arguments.  It is used to hook into the interface call
239
 
mechanism.
240
 
 
241
 
 
242
 
Default Adapters
243
 
----------------
244
 
  
245
 
Sometimes, you want to provide an adapter that will adapt anything.
246
 
For that, provide None as the required interface::
247
 
 
248
 
  >>> registry.register([None], IP1, '', 1)
249
 
  
250
 
then we can use that adapter for interfaces we don't have specific
251
 
adapters for::
252
 
 
253
 
  >>> class IQ(zope.interface.Interface):
254
 
  ...     pass
255
 
  >>> registry.lookup([IQ], IP1, '')
256
 
  1
257
 
 
258
 
Of course, specific adapters are still used when applicable::
259
 
 
260
 
  >>> registry.lookup([IR2], IP1, '')
261
 
  21
262
 
 
263
 
Class adapters
264
 
--------------
265
 
 
266
 
You can register adapters for class declarations, which is almost the
267
 
same as registering them for a class::
268
 
 
269
 
  >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', 'C21')
270
 
  >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
271
 
  'C21'
272
 
 
273
 
Dict adapters
274
 
-------------
275
 
 
276
 
At some point it was impossible to register dictionary-based adapters due a
277
 
bug. Let's make sure this works now:
278
 
 
279
 
  >>> adapter = {}
280
 
  >>> registry.register((), IQ, '', adapter)
281
 
  >>> registry.lookup((), IQ, '') is adapter
282
 
  True
283
 
 
284
 
Unregistering
285
 
-------------
286
 
 
287
 
You can unregister by registering None, rather than an object::
288
 
 
289
 
  >>> registry.register([zope.interface.implementedBy(C2)], IP1, '', None)
290
 
  >>> registry.lookup([zope.interface.implementedBy(C2)], IP1, '')
291
 
  21
292
 
 
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.
296
 
 
297
 
Multi-adapters
298
 
==============
299
 
 
300
 
You can adapt multiple specifications::
301
 
 
302
 
  >>> registry.register([IR1, IQ], IP2, '', '1q2')
303
 
  >>> registry.lookup([IR1, IQ], IP2, '')
304
 
  '1q2'
305
 
  >>> registry.lookup([IR2, IQ], IP1, '')
306
 
  '1q2'
307
 
 
308
 
  >>> class IS(zope.interface.Interface):
309
 
  ...     pass
310
 
  >>> registry.lookup([IR2, IS], IP1, '')
311
 
 
312
 
  >>> class IQ2(IQ):
313
 
  ...     pass
314
 
 
315
 
  >>> registry.lookup([IR2, IQ2], IP1, '')
316
 
  '1q2'
317
 
 
318
 
  >>> registry.register([IR1, IQ2], IP2, '', '1q22')
319
 
  >>> registry.lookup([IR2, IQ2], IP1, '')
320
 
  '1q22'
321
 
 
322
 
Multi-adaptation
323
 
----------------
324
 
 
325
 
You can adapt multiple objects::
326
 
 
327
 
  >>> class Q:
328
 
  ...     zope.interface.implements(IQ)
329
 
 
330
 
As with single adapters, we register a factory, which is often a class::
331
 
 
332
 
  >>> class IM(zope.interface.Interface):
333
 
  ...     pass
334
 
  >>> class M:
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)
339
 
 
340
 
And then we can call `queryMultiAdapter` to compute an adapter::
341
 
 
342
 
  >>> q = Q()
343
 
  >>> m = registry.queryMultiAdapter((x, q), IM)
344
 
  >>> m.__class__.__name__
345
 
  'M'
346
 
  >>> m.x is x and m.q is q
347
 
  True
348
 
 
349
 
and, of course, we can use names::
350
 
 
351
 
  >>> class M2(M):
352
 
  ...     pass
353
 
  >>> registry.register([IR, IQ], IM, 'bob', M2)
354
 
  >>> m = registry.queryMultiAdapter((x, q), IM, 'bob')
355
 
  >>> m.__class__.__name__
356
 
  'M2'
357
 
  >>> m.x is x and m.q is q
358
 
  True
359
 
  
360
 
Default Adapters
361
 
----------------
362
 
 
363
 
As with single adapters, you can define default adapters by specifying
364
 
None for the *first* specification::
365
 
 
366
 
  >>> registry.register([None, IQ], IP2, '', 'q2')
367
 
  >>> registry.lookup([IS, IQ], IP2, '')
368
 
  'q2'
369
 
 
370
 
Null Adapters
371
 
=============
372
 
 
373
 
You can also adapt no specification::
374
 
 
375
 
  >>> registry.register([], IP2, '', 2)
376
 
  >>> registry.lookup([], IP2, '')
377
 
  2
378
 
  >>> registry.lookup([], IP1, '')
379
 
  2
380
 
 
381
 
Listing named adapters
382
 
----------------------
383
 
 
384
 
Adapters are named. Sometimes, it's useful to get all of the named
385
 
adapters for given interfaces::
386
 
 
387
 
  >>> adapters = list(registry.lookupAll([IR1], IP1))
388
 
  >>> adapters.sort()
389
 
  >>> assert adapters == [(u'', 11), (u'bob', "Bob's 12")]
390
 
 
391
 
This works for multi-adapters too::
392
 
 
393
 
  >>> registry.register([IR1, IQ2], IP2, 'bob', '1q2 for bob')
394
 
  >>> adapters = list(registry.lookupAll([IR2, IQ2], IP1))
395
 
  >>> adapters.sort()
396
 
  >>> assert adapters == [(u'', '1q22'), (u'bob', '1q2 for bob')]
397
 
 
398
 
And even null adapters::
399
 
 
400
 
  >>> registry.register([], IP2, 'bob', 3)
401
 
  >>> adapters = list(registry.lookupAll([], IP1))
402
 
  >>> adapters.sort()
403
 
  >>> assert adapters == [(u'', 2), (u'bob', 3)]
404
 
 
405
 
Subscriptions
406
 
=============
407
 
 
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::
413
 
 
414
 
  >>> registry.subscribe([IR1], IP2, 'sub12 1')
415
 
  >>> registry.subscriptions([IR1], IP2)
416
 
  ['sub12 1']
417
 
 
418
 
Note that, unlike regular adapters, subscriptions are unnamed.
419
 
 
420
 
You can have multiple subscribers for the same specification::
421
 
 
422
 
  >>> registry.subscribe([IR1], IP2, 'sub12 2')
423
 
  >>> registry.subscriptions([IR1], IP2)
424
 
  ['sub12 1', 'sub12 2']
425
 
 
426
 
If subscribers are registered for the same required interfaces, they
427
 
are returned in the order of definition.
428
 
 
429
 
You can register subscribers for all specifications using None::
430
 
 
431
 
  >>> registry.subscribe([None], IP1, 'sub_1')
432
 
  >>> registry.subscriptions([IR2], IP1)
433
 
  ['sub_1', 'sub12 1', 'sub12 2']
434
 
 
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.
438
 
 
439
 
Subscriptions may be combined over multiple compatible specifications::
440
 
 
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']
451
 
 
452
 
Subscriptions can be on multiple specifications::
453
 
 
454
 
  >>> registry.subscribe([IR1, IQ], IP2, 'sub1q2')
455
 
  >>> registry.subscriptions([IR1, IQ], IP2)
456
 
  ['sub1q2']
457
 
  
458
 
As with single subscriptions and non-subscription adapters, you can
459
 
specify None for the first required interface, to specify a default::
460
 
 
461
 
  >>> registry.subscribe([None, IQ], IP2, 'sub_q2')
462
 
  >>> registry.subscriptions([IS, IQ], IP2)
463
 
  ['sub_q2']
464
 
  >>> registry.subscriptions([IR1, IQ], IP2)
465
 
  ['sub_q2', 'sub1q2']
466
 
 
467
 
You can have subscriptions that are indepenent of any specifications::
468
 
  
469
 
  >>> list(registry.subscriptions([], IP1))
470
 
  []
471
 
 
472
 
  >>> registry.subscribe([], IP2, 'sub2')
473
 
  >>> registry.subscriptions([], IP1)
474
 
  ['sub2']
475
 
  >>> registry.subscribe([], IP1, 'sub1')
476
 
  >>> registry.subscriptions([], IP1)
477
 
  ['sub2', 'sub1']
478
 
  >>> registry.subscriptions([], IP2)
479
 
  ['sub2']
480
 
 
481
 
Unregistering subscribers
482
 
-------------------------
483
 
 
484
 
We can unregister subscribers.  When unregistering a subscriber, we
485
 
can unregister a specific subscriber::
486
 
 
487
 
  >>> registry.unsubscribe([IR1], IP1, 'sub11')
488
 
  >>> registry.subscriptions([IR1], IP1)
489
 
  ['sub_1', 'sub12 1', 'sub12 2']
490
 
 
491
 
If we don't specify a value, then all subscribers matching the given
492
 
interfaces will be unsubscribed:
493
 
 
494
 
  >>> registry.unsubscribe([IR1], IP2)
495
 
  >>> registry.subscriptions([IR1], IP1)
496
 
  ['sub_1']
497
 
 
498
 
 
499
 
Subscription adapters
500
 
---------------------
501
 
 
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::
505
 
 
506
 
  >>> registry.subscribe([IR, IQ], IM, M)
507
 
  >>> registry.subscribe([IR, IQ], IM, M2)
508
 
 
509
 
  >>> subscribers = registry.subscribers((x, q), IM)
510
 
  >>> len(subscribers)
511
 
  2
512
 
  >>> class_names = [s.__class__.__name__ for s in subscribers]
513
 
  >>> class_names.sort()
514
 
  >>> class_names
515
 
  ['M', 'M2']
516
 
  >>> [(s.x is x and s.q is q) for s in subscribers]
517
 
  [True, True]
518
 
 
519
 
adapter factory subcribers can't return None values::
520
 
 
521
 
  >>> def M3(x, y):
522
 
  ...     return None
523
 
 
524
 
  >>> registry.subscribe([IR, IQ], IM, M3)
525
 
  >>> subscribers = registry.subscribers((x, q), IM)
526
 
  >>> len(subscribers)
527
 
  2
528
 
 
529
 
Handlers
530
 
--------
531
 
 
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.
535
 
 
536
 
To register a handler, simply provide None as the provided interface::
537
 
 
538
 
  >>> def handler(event):
539
 
  ...     print 'handler', event
540
 
 
541
 
  >>> registry.subscribe([IR1], None, handler)
542
 
  >>> registry.subscriptions([IR1], None) == [handler]
543
 
  True