summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Watson <cjwatson@canonical.com>2015-01-20 14:35:39 +0000
committerColin Watson <cjwatson@canonical.com>2015-01-20 14:35:39 +0000
commit0047f1d611d2377d908f89445ba5d4dfa72d86b0 (patch)
tree2287b3f5684af4f8a93afd449e4c17d373a7fe59
parent25878709d9fe3f50135005703dcfaccda23b9eca (diff)
downloadzope-pagetemplate-0047f1d611d2377d908f89445ba5d4dfa72d86b0.tar.gz
Allow short-circuit traversal for non-proxied dict subclasses
This change is based on https://code.launchpad.net/~wallyworld/zope.pagetemplate/fix-isinstance/+merge/38499 by Ian Booth. The original approach in that branch broke tests, because they rely on subclassing dict with an implementer of ITraversable. Rather than changing this, it seems safer to only extend the dict special-case to non-proxied subclasses.
-rw-r--r--src/zope/pagetemplate/engine.py22
1 files changed, 22 insertions, 0 deletions
diff --git a/src/zope/pagetemplate/engine.py b/src/zope/pagetemplate/engine.py
index 99634fa..df307e7 100644
--- a/src/zope/pagetemplate/engine.py
+++ b/src/zope/pagetemplate/engine.py
@@ -22,6 +22,7 @@ import sys
from zope import component
from zope.interface import implementer
from zope.component.interfaces import ComponentLookupError
+from zope.proxy import isProxy
from zope.traversing.interfaces import IPathAdapter, ITraversable
from zope.traversing.interfaces import TraversalError
from zope.traversing.adapters import traversePathElement
@@ -66,6 +67,8 @@ class ZopeTraverser(object):
# special-case dicts for performance reasons
if getattr(object, '__class__', None) == dict:
object = object[name]
+ elif isinstance(object, dict) and not isProxy(object):
+ object = object[name]
else:
object = traversePathElement(object, name, path_items,
request=request)
@@ -386,6 +389,25 @@ class ZopeEngine(ZopeBaseEngine):
...
KeyError: 'keys'
+ This special-casing also applies to non-proxied dict subclasses:
+
+ >>> class TraverserDict(dict):
+ ... def __init__(self):
+ ... self.item_requested = None
+ ... def __getitem__(self, item):
+ ... self.item_requested = item
+ ... return dict.__getitem__(self, item)
+
+ >>> d = engine.getBaseNames()
+ >>> foo = TraverserDict()
+ >>> d['foo'] = foo
+ >>> foo['bar'] = 'baz'
+ >>> context = engine.getContext(d)
+ >>> context.evaluate('foo/bar')
+ 'baz'
+ >>> foo.item_requested
+ 'bar'
+
>>> tearDown()
"""