[Zope3-checkins] SVN: Zope3/trunk/src/zope/interface/ Fixed the multi-adapter lookup to truly find the best adapter. This fixes

Stephan Richter srichter at cosmos.phy.tufts.edu
Sun Sep 19 07:41:27 EDT 2004


Log message for revision 27639:
  Fixed the multi-adapter lookup to truly find the best adapter. This fixes 
  the skin lookup and ZopeTop and other skins work correctly again.
  
  All the credit goes to Gustavo Niemeyer, who debugged the issue and 
  provided this great fix. I just contributed some debugging and the 
  new tests.
  


Changed:
  U   Zope3/trunk/src/zope/interface/adapter.py
  U   Zope3/trunk/src/zope/interface/tests/test_adapter.py


-=-
Modified: Zope3/trunk/src/zope/interface/adapter.py
===================================================================
--- Zope3/trunk/src/zope/interface/adapter.py	2004-09-19 08:33:49 UTC (rev 27638)
+++ Zope3/trunk/src/zope/interface/adapter.py	2004-09-19 11:41:26 UTC (rev 27639)
@@ -352,12 +352,30 @@
             if not bywith:
                 continue
 
+            # Selecting multi-adapters is not just a matter of matching the
+            # required interfaces of the adapter to the ones passed. Several
+            # adapters might match, but we only want the best one. We use a
+            # ranking algorithm to determine the best match.
+
+            # `best` carries the rank and value of the best found adapter.
+            best = None
             for rwith, value in bywith:
+                # the `rank` describes how well the found adapter matches.
+                rank = []
                 for rspec, spec in zip(rwith, with):
                     if not spec.isOrExtends(rspec):
                         break # This one is no good
+                    # Determine the rank of this particular specification.
+                    rank.append(list(spec.__sro__).index(rspec))
                 else:
-                    return value
+                    # If the new rank is better than the best previously
+                    # recorded one, make the new adapter the best one found. 
+                    rank = tuple(rank)
+                    if best is None or rank < best[0]:
+                        best = rank, value
+            # If any match was found, return the best one.
+            if best:
+                return best[1]
 
         return default
 

Modified: Zope3/trunk/src/zope/interface/tests/test_adapter.py
===================================================================
--- Zope3/trunk/src/zope/interface/tests/test_adapter.py	2004-09-19 08:33:49 UTC (rev 27638)
+++ Zope3/trunk/src/zope/interface/tests/test_adapter.py	2004-09-19 11:41:26 UTC (rev 27639)
@@ -35,6 +35,51 @@
 class IR1(IR0):
     pass
 
+
+def test_orderwith():
+    """
+    >>> Interface = zope.interface.Interface
+    >>> bywith = {(Interface, Interface): 'A0',
+    ...           (IF0,       Interface): 'A1', 
+    ...           (Interface, IB0):       'A2', 
+    ...           (IF0,       IB0):       'A3', 
+    ...           (IF1,       IB0):       'A4', 
+    ...           (IF0,       IB1):       'A5', 
+    ...           (IF1,       IB1):       'A6', 
+    ...          }
+
+    >>> [value for spec, value in zope.interface.adapter.orderwith(bywith)]
+    ['A6', 'A4', 'A5', 'A3', 'A1', 'A2', 'A0']
+    """
+
+
+def test_multi_adapter_get_best_match():
+    """
+    >>> registry = AdapterRegistry()
+
+    >>> class IB2(IB0):
+    ...     pass
+    >>> class IB3(IB2, IB1):
+    ...     pass
+    >>> class IB4(IB1, IB2):
+    ...     pass
+
+    >>> registry.register([None, IB1], IR0, '', 'A1')
+    >>> registry.register([None, IB0], IR0, '', 'A0')
+    >>> registry.register([None, IB2], IR0, '', 'A2')
+
+    >>> registry.lookup((IF1, IB1), IR0, '')
+    'A1'
+    >>> registry.lookup((IF1, IB2), IR0, '')
+    'A2'
+    >>> registry.lookup((IF1, IB0), IR0, '')
+    'A0'
+    >>> registry.lookup((IF1, IB3), IR0, '')
+    'A2'
+    >>> registry.lookup((IF1, IB4), IR0, '')
+    'A1'
+    """
+
 def test_multi_adapter_w_default():
     """
     >>> registry = AdapterRegistry()
@@ -141,4 +186,5 @@
         doctest.DocTestSuite(),
         ))
 
-if __name__ == '__main__': unittest.main()
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')



More information about the Zope3-Checkins mailing list