[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