[Zope3-checkins] SVN: Zope3/branches/3.3/ Fix collector #639: Default ITraverser ignores dict methods.

Philipp von Weitershausen philikon at philikon.de
Sun May 28 20:13:32 EDT 2006


Log message for revision 68324:
  Fix collector #639: Default ITraverser ignores dict methods.
  
  Moved the special-casing of dicts to zope.app.pagetemplate (and refactored the
  traversal code there a little bit to avoid code duplication).
  

Changed:
  U   Zope3/branches/3.3/doc/CHANGES.txt
  U   Zope3/branches/3.3/src/zope/app/pagetemplate/engine.py
  U   Zope3/branches/3.3/src/zope/traversing/adapters.py
  U   Zope3/branches/3.3/src/zope/traversing/tests/test_traverser.py

-=-
Modified: Zope3/branches/3.3/doc/CHANGES.txt
===================================================================
--- Zope3/branches/3.3/doc/CHANGES.txt	2006-05-28 23:24:18 UTC (rev 68323)
+++ Zope3/branches/3.3/doc/CHANGES.txt	2006-05-29 00:13:26 UTC (rev 68324)
@@ -10,6 +10,8 @@
 
     Bugfixes
 
+      - Fixed issue 639: Default ITraverser ignores dict methods
+
       - Fixed issue 636: Default ITraverser can't traverse old style
         classes
 

Modified: Zope3/branches/3.3/src/zope/app/pagetemplate/engine.py
===================================================================
--- Zope3/branches/3.3/src/zope/app/pagetemplate/engine.py	2006-05-28 23:24:18 UTC (rev 68323)
+++ Zope3/branches/3.3/src/zope/app/pagetemplate/engine.py	2006-05-29 00:13:26 UTC (rev 68324)
@@ -26,7 +26,7 @@
 from zope.component.interfaces import ComponentLookupError
 from zope.traversing.interfaces import IPathAdapter, ITraversable
 from zope.traversing.interfaces import TraversalError
-from zope.traversing.adapters import Traverser, traversePathElement
+from zope.traversing.adapters import traversePathElement
 from zope.security.untrustedpython import rcompile
 from zope.security.proxy import ProxyFactory
 from zope.security.untrustedpython.builtins import SafeBuiltins
@@ -42,34 +42,42 @@
 class InlineCodeError(Exception):
     pass
 
+class ZopeTraverser(object):
 
-def zopeTraverser(object, path_items, econtext):
-    """Traverses a sequence of names, first trying attributes then items.
-    """
-    request = getattr(econtext, 'request', None)
-    path_items = list(path_items)
-    path_items.reverse()
+    def __init__(self, proxify=None):
+        if proxify is None:
+            self.proxify = lambda x: x
+        else:
+            self.proxify = proxify
 
-    while path_items:
-        name = path_items.pop()
-        object = traversePathElement(object, name, path_items,
-                                     request=request)
-        object = ProxyFactory(object)
-    return object
+    def __call__(self, object, path_items, econtext):
+        """Traverses a sequence of names, first trying attributes then items.
+        """
+        request = getattr(econtext, 'request', None)
+        path_items = list(path_items)
+        path_items.reverse()
 
+        while path_items:
+            name = path_items.pop()
+            
+            # special-case dicts for performance reasons        
+            if getattr(object, '__class__', None) == dict:
+                object = object[name]
+            else:
+                object = traversePathElement(object, name, path_items,
+                                             request=request)
+            object = self.proxify(object)
+        return object
+
+zopeTraverser = ZopeTraverser(ProxyFactory)
+
 class ZopePathExpr(PathExpr):
 
     def __init__(self, name, expr, engine):
         super(ZopePathExpr, self).__init__(name, expr, engine, zopeTraverser)
 
+trustedZopeTraverser = ZopeTraverser()
 
-def trustedZopeTraverser(object, path_items, econtext):
-    """Traverses a sequence of names, first trying attributes then items.
-    """
-    traverser = Traverser(object)
-    return traverser.traverse(path_items,
-                              request=getattr(econtext, 'request', None))
-
 class TrustedZopePathExpr(PathExpr):
 
     def __init__(self, name, expr, engine):
@@ -281,6 +289,21 @@
       >>> o1 is o2
       False
 
+    Note that this engine special-cases dicts during path traversal:
+    it traverses only to their items, but not to their attributes
+    (e.g. methods on dicts), because of performance reasons:
+
+      >>> d = engine.getBaseNames()
+      >>> d['adict'] = {'items': 123}
+      >>> d['anotherdict'] = {}
+      >>> context = engine.getContext(d)
+      >>> context.evaluate('adict/items')
+      123
+      >>> context.evaluate('anotherdict/keys')
+      Traceback (most recent call last):
+        ...
+      KeyError: 'keys'
+
       >>> tearDown()
 
     """

Modified: Zope3/branches/3.3/src/zope/traversing/adapters.py
===================================================================
--- Zope3/branches/3.3/src/zope/traversing/adapters.py	2006-05-28 23:24:18 UTC (rev 68323)
+++ Zope3/branches/3.3/src/zope/traversing/adapters.py	2006-05-29 00:13:26 UTC (rev 68324)
@@ -33,7 +33,6 @@
 
 class DefaultTraversable(object):
     """Traverses objects via attribute and item lookup"""
-
     zope.interface.implements(ITraversable)
 
     def __init__(self, subject):
@@ -52,7 +51,6 @@
                 pass
         raise TraversalError(subject, name)
 
-
 class RootPhysicallyLocatable(object):
     __doc__ = IPhysicallyLocatable.__doc__
 
@@ -158,11 +156,6 @@
         nm = name
 
     if traversable is None:
-        # not all objects have __class__, for example old style classes
-        if getattr(obj, '__class__', None) == dict:
-            # Special-case dicts
-            return obj[name]
-
         traversable = ITraversable(obj, None)
         if traversable is None:
             raise TraversalError('No traversable adapter found', obj)

Modified: Zope3/branches/3.3/src/zope/traversing/tests/test_traverser.py
===================================================================
--- Zope3/branches/3.3/src/zope/traversing/tests/test_traverser.py	2006-05-28 23:24:18 UTC (rev 68323)
+++ Zope3/branches/3.3/src/zope/traversing/tests/test_traverser.py	2006-05-29 00:13:26 UTC (rev 68324)
@@ -146,6 +146,18 @@
         tr = Traverser(container)
         self.assert_(tr.traverse('theclass/x') is AnOldStyleClass.x)
 
+    def testTraversingDictSeesDictAPI(self):
+        adict = {
+            'foo': 'bar',
+            'anotherdict': {'bar': 'foo'},
+            'items': '123',
+            }
+        tr = Traverser(adict)
+        self.assertEqual(tr.traverse('items'), adict.items)
+        self.assertEqual(tr.traverse('anotherdict/bar'), 'foo')
+        self.assertEqual(tr.traverse('anotherdict/items'),
+                         adict['anotherdict'].items)
+
 class RestrictedTraverseTests(PlacefulSetup, unittest.TestCase):
     _oldPolicy = None
     _deniedNames = ()



More information about the Zope3-Checkins mailing list