[Zope3-checkins] SVN: Zope3/trunk/src/zope/interface/ Fixed bug 396
in multi-adapter lookup:
Jim Fulton
jim at zope.com
Sun Nov 6 16:38:21 EST 2005
Log message for revision 39948:
Fixed bug 396 in multi-adapter lookup:
http://www.zope.org/Collectors/Zope3-dev/396
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 2005-11-06 16:41:15 UTC (rev 39947)
+++ Zope3/trunk/src/zope/interface/adapter.py 2005-11-06 21:38:20 UTC (rev 39948)
@@ -51,7 +51,7 @@
# 'name' is a unicode adapter name. Unnamed adapters have an empty
# name.
-# 'specification' is the interface being adapted to.
+# 'specification' is the interface being adapted to, the provided interface.
# 'factories' is normally a tuple of factories, but can be anything.
# (See the "raw" option to the query-adapter calls.) For subscription
@@ -172,6 +172,7 @@
# override less-specific data.
ancestors.reverse()
for ancestor in ancestors:
+ ancestor_spec = ancestor.spec()
for key, v in ancestor.selfImplied.iteritems():
@@ -206,8 +207,20 @@
if not oldbyname:
implied[key] = oldbyname = {}
+
# v is {name -> {with -> ?}}
for name, withobs in v.iteritems():
+
+ # withobs is {with -> value}
+
+ # If ancestor is not the default,
+ # add in ancestor so we can get ordering right
+ if ancestor_spec is not Default:
+ withobs = dict([
+ (((ancestor_spec,) + with), value)
+ for (with, value) in withobs.iteritems()
+ ])
+
oldwithobs = oldbyname.get(name)
if not oldwithobs:
oldwithobs = oldbyname[name] = {}
@@ -227,8 +240,8 @@
for name, value in byname.iteritems():
if isinstance(value, dict):
# We have {with -> value}
- # convert it to sorted [(with, value]
- byname[name] = orderwith(value)
+ # convert it to [(with, value]
+ byname[name] = value.items()
self.get = implied.get
@@ -287,33 +300,7 @@
def __repr__(self):
return '<%s(%s)>' % (self.__class__.__name__, self.spec())
-def orderwith(bywith):
- # Convert {with -> adapter} to withs, [(with, value)]
- # such that there are no i, j, i < j, such that
- # withs[j][0] extends withs[i][0].
-
- withs = []
- for with, value in bywith.iteritems():
- for i, (w, v) in enumerate(withs):
- if withextends(with, w):
- withs.insert(i, (with, value))
- break
- else:
- withs.append((with, value))
-
- return withs
-
-
-def withextends(with1, with2):
- for spec1, spec2 in zip(with1, with2):
- if spec1.extends(spec2):
- return True
- if spec1 != spec2:
- break
- return False
-
-
class AdapterLookup(object):
# Adapter lookup support
# We have a class here because we want to provide very
@@ -362,43 +349,46 @@
# Multi adapter
- with = required[1:]
+ with = required
key = provided, order
for surrogate in self.get(required[0]), self._default:
byname = surrogate.get(key)
- if not byname:
- continue
+ if byname:
+ bywith = byname.get(name)
+ if bywith:
+ # 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
- bywith = byname.get(name)
- if not bywith:
- continue
+ # Determine the rank of this particular
+ # specification.
+ rank.append(list(spec.__sro__).index(rspec))
+ else:
+ # 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]
- # 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.
+ with = with[1:] # on second pass through, don't use first spec
- # `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:
- # 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
def lookup1(self, required, provided, name='', default=None):
@@ -584,41 +574,45 @@
# Multi adapter
- with = required[1:]
+ with = required
key = provided, order
first = ()
for surrogate in self.get(required[0]), self._default:
byname = surrogate.get(key)
- if not byname:
- continue
+ if byname:
+ for name, bywith in byname.iteritems():
+ if not bywith or name in first:
+ continue
- for name, bywith in byname.iteritems():
- if not bywith or name in first:
- continue
+ # See comments on lookup() above
+ 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:
+ # 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
- # See comments on lookup() above
- 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:
- # 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:
+ yield name, best[1]
- # If any match was found, return the best one.
- if best:
- yield name, best[1]
+ first = byname
- first = byname
+ with = with[1:] # on second pass through, don't use first spec
def subscribe(self, required, provided, value):
if required:
Modified: Zope3/trunk/src/zope/interface/tests/test_adapter.py
===================================================================
--- Zope3/trunk/src/zope/interface/tests/test_adapter.py 2005-11-06 16:41:15 UTC (rev 39947)
+++ Zope3/trunk/src/zope/interface/tests/test_adapter.py 2005-11-06 21:38:20 UTC (rev 39948)
@@ -35,24 +35,6 @@
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()
@@ -310,7 +292,15 @@
42
"""
-
+def test_correct_multi_adapter_lookup():
+ """
+ >>> registry = AdapterRegistry()
+ >>> registry.register([IF0, IB1], IR0, '', 'A01')
+ >>> registry.register([IF1, IB0], IR0, '', 'A10')
+ >>> registry.lookup((IF1, IB1), IR0, '')
+ 'A10'
+ """
+
def test_suite():
from zope.testing import doctest, doctestunit
return unittest.TestSuite((
More information about the Zope3-Checkins
mailing list