1 ##############################################################################
3 # Copyright (c) 2003 Zope Foundation and Contributors.
6 # This software is subject to the provisions of the Zope Public License,
7 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 # FOR A PARTICULAR PURPOSE.
13 ##############################################################################
14 """Adapter registry tests
19 from zope.interface.adapter import AdapterRegistry
22 class IF0(zope.interface.Interface):
27 class IB0(zope.interface.Interface):
32 class IR0(zope.interface.Interface):
37 def test_multi_adapter_get_best_match():
39 >>> registry = AdapterRegistry()
43 >>> class IB3(IB2, IB1):
45 >>> class IB4(IB1, IB2):
48 >>> registry.register([None, IB1], IR0, '', 'A1')
49 >>> registry.register([None, IB0], IR0, '', 'A0')
50 >>> registry.register([None, IB2], IR0, '', 'A2')
52 >>> registry.lookup((IF1, IB1), IR0, '')
54 >>> registry.lookup((IF1, IB2), IR0, '')
56 >>> registry.lookup((IF1, IB0), IR0, '')
58 >>> registry.lookup((IF1, IB3), IR0, '')
60 >>> registry.lookup((IF1, IB4), IR0, '')
64 def test_multi_adapter_lookupAll_get_best_matches():
66 >>> registry = AdapterRegistry()
70 >>> class IB3(IB2, IB1):
72 >>> class IB4(IB1, IB2):
75 >>> registry.register([None, IB1], IR0, '', 'A1')
76 >>> registry.register([None, IB0], IR0, '', 'A0')
77 >>> registry.register([None, IB2], IR0, '', 'A2')
79 >>> tuple(registry.lookupAll((IF1, IB1), IR0))[0][1]
81 >>> tuple(registry.lookupAll((IF1, IB2), IR0))[0][1]
83 >>> tuple(registry.lookupAll((IF1, IB0), IR0))[0][1]
85 >>> tuple(registry.lookupAll((IF1, IB3), IR0))[0][1]
87 >>> tuple(registry.lookupAll((IF1, IB4), IR0))[0][1]
92 def test_multi_adapter_w_default():
94 >>> registry = AdapterRegistry()
96 >>> registry.register([None, None], IB1, 'bob', 'A0')
98 >>> registry.lookup((IF1, IR1), IB0, 'bob')
101 >>> registry.register([None, IR0], IB1, 'bob', 'A1')
103 >>> registry.lookup((IF1, IR1), IB0, 'bob')
106 >>> registry.lookup((IF1, IR1), IB0, 'bruce')
108 >>> registry.register([None, IR1], IB1, 'bob', 'A2')
109 >>> registry.lookup((IF1, IR1), IB0, 'bob')
113 def test_multi_adapter_w_inherited_and_multiple_registrations():
115 >>> registry = AdapterRegistry()
117 >>> class IX(zope.interface.Interface):
120 >>> registry.register([IF0, IR0], IB1, 'bob', 'A1')
121 >>> registry.register([IF1, IX], IB1, 'bob', 'AX')
123 >>> registry.lookup((IF1, IR1), IB0, 'bob')
127 def test_named_adapter_with_default():
128 """Query a named simple adapter
130 >>> registry = AdapterRegistry()
132 If we ask for a named adapter, we won't get a result unless there
133 is a named adapter, even if the object implements the interface:
135 >>> registry.lookup([IF1], IF0, 'bob')
137 >>> registry.register([None], IB1, 'bob', 'A1')
138 >>> registry.lookup([IF1], IB0, 'bob')
141 >>> registry.lookup([IF1], IB0, 'bruce')
143 >>> registry.register([None], IB0, 'bob', 'A2')
144 >>> registry.lookup([IF1], IB0, 'bob')
148 def test_multi_adapter_gets_closest_provided():
150 >>> registry = AdapterRegistry()
151 >>> registry.register([IF1, IR0], IB0, 'bob', 'A1')
152 >>> registry.register((IF1, IR0), IB1, 'bob', 'A2')
153 >>> registry.lookup((IF1, IR1), IB0, 'bob')
156 >>> registry = AdapterRegistry()
157 >>> registry.register([IF1, IR0], IB1, 'bob', 'A2')
158 >>> registry.register([IF1, IR0], IB0, 'bob', 'A1')
159 >>> registry.lookup([IF1, IR0], IB0, 'bob')
162 >>> registry = AdapterRegistry()
163 >>> registry.register([IF1, IR0], IB0, 'bob', 'A1')
164 >>> registry.register([IF1, IR1], IB1, 'bob', 'A2')
165 >>> registry.lookup([IF1, IR1], IB0, 'bob')
168 >>> registry = AdapterRegistry()
169 >>> registry.register([IF1, IR1], IB1, 'bob', 2)
170 >>> registry.register([IF1, IR0], IB0, 'bob', 1)
171 >>> registry.lookup([IF1, IR1], IB0, 'bob')
175 def test_multi_adapter_check_non_default_dont_hide_default():
177 >>> registry = AdapterRegistry()
179 >>> class IX(zope.interface.Interface):
183 >>> registry.register([None, IR0], IB0, 'bob', 1)
184 >>> registry.register([IF1, IX], IB0, 'bob', 2)
185 >>> registry.lookup([IF1, IR1], IB0, 'bob')
189 def test_adapter_hook_with_factory_producing_None():
191 >>> registry = AdapterRegistry()
192 >>> default = object()
194 >>> class Object1(object):
195 ... zope.interface.implements(IF0)
196 >>> class Object2(object):
197 ... zope.interface.implements(IF0)
199 >>> def factory(context):
200 ... if isinstance(context, Object1):
204 >>> registry.register([IF0], IB0, '', factory)
206 >>> registry.adapter_hook(IB0, Object1())
208 >>> registry.adapter_hook(IB0, Object2()) is None
210 >>> registry.adapter_hook(IB0, Object2(), default=default) is default
214 def test_adapter_registry_update_upon_interface_bases_change():
216 Let's first create a adapter registry and a simple adaptation hook:
218 >>> globalRegistry = AdapterRegistry()
220 >>> def _hook(iface, ob, lookup=globalRegistry.lookup1):
221 ... factory = lookup(zope.interface.providedBy(ob), iface)
222 ... if factory is None:
225 ... return factory(ob)
227 >>> zope.interface.interface.adapter_hooks.append(_hook)
229 Now we create some interfaces and an implementation:
231 >>> class IX(zope.interface.Interface):
234 >>> class IY(zope.interface.Interface):
241 ... zope.interface.implements(IY)
242 ... def __init__(self, original):
243 ... self.original=original
245 and register an adapter:
247 >>> globalRegistry.register((IX,), IY, '', Y)
249 at first, we still expect the adapter lookup from `X` to `IY` to fail:
251 >>> IY(X()) #doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
252 Traceback (most recent call last):
254 TypeError: ('Could not adapt',
255 <zope.interface.tests.test_adapter.X object at ...>,
256 <InterfaceClass zope.interface.tests.test_adapter.IY>)
258 But after we declare an interface on the class `X`, it should pass:
260 >>> zope.interface.classImplementsOnly(X, IX)
262 >>> IY(X()) #doctest: +ELLIPSIS
263 <zope.interface.tests.test_adapter.Y object at ...>
265 >>> hook = zope.interface.interface.adapter_hooks.pop()
269 def test_changing_declarations():
272 If we change declarations for a class, those adapter lookup should
275 >>> class I1(zope.interface.Interface):
277 >>> class I2(zope.interface.Interface):
280 >>> registry = AdapterRegistry()
281 >>> registry.register([I1], I2, '', 42)
286 >>> registry.lookup([zope.interface.implementedBy(C)], I2, '')
288 >>> zope.interface.classImplements(C, I1)
290 >>> registry.lookup([zope.interface.implementedBy(C)], I2, '')
294 def test_correct_multi_adapter_lookup():
296 >>> registry = AdapterRegistry()
297 >>> registry.register([IF0, IB1], IR0, '', 'A01')
298 >>> registry.register([IF1, IB0], IR0, '', 'A10')
299 >>> registry.lookup((IF1, IB1), IR0, '')
303 def test_duplicate_bases():
305 There was a bug that caused problems if a spec had multiple bases:
307 >>> class I(zope.interface.Interface):
311 >>> registry = AdapterRegistry()
312 >>> registry.register([I2], IR0, 'x', 'X')
313 >>> registry.lookup((I2, ), IR0, 'x')
315 >>> registry.register([I2], IR0, 'y', 'Y')
316 >>> registry.lookup((I2, ), IR0, 'x')
318 >>> registry.lookup((I2, ), IR0, 'y')
322 def test_register_objects_with_cmp():
324 The registry should never use == as that will tend to fail when
325 objects are picky about what they are compared with:
328 ... def __cmp__(self, other):
329 ... raise TypeError("I\'m too picky for comparison!")
330 >>> class I(zope.interface.Interface):
335 >>> registry = AdapterRegistry()
337 >>> registry.register([I2], IR0, '', picky)
338 >>> registry.unregister([I2], IR0, '', picky)
340 >>> registry.subscribe([I2], IR0, picky)
341 >>> registry.unsubscribe([I2], IR0, picky)
345 def test_unregister_cleans_up_empties():
347 >>> class I(zope.interface.Interface):
349 >>> class IP(zope.interface.Interface):
354 >>> registry = AdapterRegistry()
356 >>> registry.register([], IP, '', C)
357 >>> registry.register([I], IP, '', C)
358 >>> registry.register([I], IP, 'name', C)
359 >>> registry.register([I, I], IP, '', C)
360 >>> len(registry._adapters)
362 >>> map(len, registry._adapters)
365 >>> registry.unregister([], IP, '', C)
366 >>> registry.unregister([I], IP, '', C)
367 >>> registry.unregister([I], IP, 'name', C)
368 >>> registry.unregister([I, I], IP, '', C)
369 >>> registry._adapters
374 def test_unsubscribe_cleans_up_empties():
376 >>> class I1(zope.interface.Interface):
378 >>> class I2(zope.interface.Interface):
380 >>> class IP(zope.interface.Interface):
383 >>> registry = AdapterRegistry()
384 >>> def handler(event):
387 >>> registry.subscribe([I1], I1, handler)
388 >>> registry.subscribe([I2], I1, handler)
389 >>> len(registry._subscribers)
391 >>> map(len, registry._subscribers)
394 >>> registry.unsubscribe([I1], I1, handler)
395 >>> registry.unsubscribe([I2], I1, handler)
396 >>> registry._subscribers
403 return unittest.TestSuite((
404 doctest.DocFileSuite('../adapter.txt', '../adapter.ru.txt',
405 '../human.txt', '../human.ru.txt',
406 'foodforthought.txt',
407 globs={'__name__': '__main__'}),
408 doctest.DocTestSuite(),
411 if __name__ == '__main__':
412 unittest.main(defaultTest='test_suite')