[Zope-Checkins] SVN: Zope/trunk/ - LP #374810: ``__bobo_traverse__`` implementation can raise
Christian Zagrodnick
cz at gocept.com
Mon Jun 21 10:47:19 EDT 2010
Log message for revision 113725:
- LP #374810: ``__bobo_traverse__`` implementation can raise
``ZPublisher.interfaces.UseTraversalDefault`` to indicate that there is no
special casing for the given name and that standard traversal logic should be
applied.
Changed:
U Zope/trunk/doc/CHANGES.rst
U Zope/trunk/src/OFS/Traversable.py
U Zope/trunk/src/OFS/tests/testTraverse.py
U Zope/trunk/src/ZPublisher/BaseRequest.py
U Zope/trunk/src/ZPublisher/interfaces.py
U Zope/trunk/src/ZPublisher/tests/testBaseRequest.py
-=-
Modified: Zope/trunk/doc/CHANGES.rst
===================================================================
--- Zope/trunk/doc/CHANGES.rst 2010-06-21 11:08:59 UTC (rev 113724)
+++ Zope/trunk/doc/CHANGES.rst 2010-06-21 14:47:19 UTC (rev 113725)
@@ -127,6 +127,11 @@
Features Added
++++++++++++++
+- LP #374810: ``__bobo_traverse__`` implementation can raise
+ ``ZPublisher.interfaces.UseTraversalDefault`` to indicate that there is no
+ special casing for the given name and that standard traversal logic should
+ be applied.
+
- LP #142226: Added an extra keyword argument to the HTTPResponse
setCookie method to suppress enclosing the cookie value field
in double quotes.
Modified: Zope/trunk/src/OFS/Traversable.py
===================================================================
--- Zope/trunk/src/OFS/Traversable.py 2010-06-21 11:08:59 UTC (rev 113724)
+++ Zope/trunk/src/OFS/Traversable.py 2010-06-21 14:47:19 UTC (rev 113725)
@@ -30,6 +30,7 @@
from Acquisition.interfaces import IAcquirer
from OFS.interfaces import ITraversable
from zExceptions import NotFound
+from ZPublisher.interfaces import UseTraversalDefault
from ZODB.POSException import ConflictError
from zope.interface import implements
@@ -207,60 +208,66 @@
except LocationError:
raise AttributeError(name)
- elif bobo_traverse is not None:
- next = bobo_traverse(REQUEST, name)
- if restricted:
- if aq_base(next) is not next:
- # The object is wrapped, so the acquisition
- # context is the container.
- container = aq_parent(aq_inner(next))
- elif getattr(next, 'im_self', None) is not None:
- # Bound method, the bound instance
- # is the container
- container = next.im_self
- elif getattr(aq_base(obj), name, _marker) is next:
- # Unwrapped direct attribute of the object so
- # object is the container
- container = obj
+ else:
+ next = UseTraversalDefault # indicator
+ try:
+ if bobo_traverse is not None:
+ next = bobo_traverse(REQUEST, name)
+ if restricted:
+ if aq_base(next) is not next:
+ # The object is wrapped, so the acquisition
+ # context is the container.
+ container = aq_parent(aq_inner(next))
+ elif getattr(next, 'im_self', None) is not None:
+ # Bound method, the bound instance
+ # is the container
+ container = next.im_self
+ elif getattr(aq_base(obj), name, _marker) is next:
+ # Unwrapped direct attribute of the object so
+ # object is the container
+ container = obj
+ else:
+ # Can't determine container
+ container = None
+ # If next is a simple unwrapped property, its
+ # parentage is indeterminate, but it may have
+ # been acquired safely. In this case validate
+ # will raise an error, and we can explicitly
+ # check that our value was acquired safely.
+ try:
+ ok = validate(obj, container, name, next)
+ except Unauthorized:
+ ok = False
+ if not ok:
+ if (container is not None or
+ guarded_getattr(obj, name, _marker)
+ is not next):
+ raise Unauthorized(name)
+ except UseTraversalDefault:
+ # behave as if there had been no '__bobo_traverse__'
+ bobo_traverse = None
+ if next is UseTraversalDefault:
+ if getattr(aq_base(obj), name, _marker) is not _marker:
+ if restricted:
+ next = guarded_getattr(obj, name)
+ else:
+ next = getattr(obj, name)
else:
- # Can't determine container
- container = None
- # If next is a simple unwrapped property, its
- # parentage is indeterminate, but it may have
- # been acquired safely. In this case validate
- # will raise an error, and we can explicitly
- # check that our value was acquired safely.
- try:
- ok = validate(obj, container, name, next)
- except Unauthorized:
- ok = False
- if not ok:
- if (container is not None or
- guarded_getattr(obj, name, _marker)
- is not next):
+ try:
+ next = obj[name]
+ # The item lookup may return a NullResource,
+ # if this is the case we save it and return it
+ # if all other lookups fail.
+ if isinstance(next, NullResource):
+ resource = next
+ raise KeyError(name)
+ except AttributeError:
+ # Raise NotFound for easier debugging
+ # instead of AttributeError: __getitem__
+ raise NotFound(name)
+ if restricted and not validate(
+ obj, obj, None, next):
raise Unauthorized(name)
- else:
- if getattr(aq_base(obj), name, _marker) is not _marker:
- if restricted:
- next = guarded_getattr(obj, name)
- else:
- next = getattr(obj, name)
- else:
- try:
- next = obj[name]
- # The item lookup may return a NullResource,
- # if this is the case we save it and return it
- # if all other lookups fail.
- if isinstance(next, NullResource):
- resource = next
- raise KeyError(name)
- except AttributeError:
- # Raise NotFound for easier debugging
- # instead of AttributeError: __getitem__
- raise NotFound(name)
- if restricted and not validate(
- obj, obj, None, next):
- raise Unauthorized(name)
except (AttributeError, NotFound, KeyError), e:
# Try to look for a view
Modified: Zope/trunk/src/OFS/tests/testTraverse.py
===================================================================
--- Zope/trunk/src/OFS/tests/testTraverse.py 2010-06-21 11:08:59 UTC (rev 113724)
+++ Zope/trunk/src/OFS/tests/testTraverse.py 2010-06-21 14:47:19 UTC (rev 113725)
@@ -354,6 +354,33 @@
self.failUnlessRaises(Unauthorized,
self.root.folder1.restrictedTraverse, 'stuff')
+ def testBoboTraverseTraversalDefault(self):
+ from OFS.SimpleItem import SimpleItem
+ from ZPublisher.interfaces import UseTraversalDefault
+ class BoboTraversableUseTraversalDefault(SimpleItem):
+ """
+ A BoboTraversable class which may use "UseTraversalDefault"
+ (dependent on "name") to indicate that standard traversal should
+ be used.
+ """
+ default = 'Default'
+
+ def __bobo_traverse__(self, request, name):
+ if name == 'normal': return 'Normal'
+ raise UseTraversalDefault
+
+
+ bb = BoboTraversableUseTraversalDefault()
+ # normal access -- no traversal default used
+ self.assertEqual(bb.unrestrictedTraverse('normal'), 'Normal')
+ # use traversal default
+ self.assertEqual(bb.unrestrictedTraverse('default'), 'Default')
+ # test traversal default with acqires attribute
+ si = SimpleItem()
+ si.default_acquire = 'Default_Acquire'
+ si.bb = bb
+ self.assertEqual(si.unrestrictedTraverse('bb/default_acquire'), 'Default_Acquire')
+
def testAcquiredAttributeDenial(self):
# Verify that restrictedTraverse raises the right kind of exception
# on denial of access to an acquired attribute. If it raises
Modified: Zope/trunk/src/ZPublisher/BaseRequest.py
===================================================================
--- Zope/trunk/src/ZPublisher/BaseRequest.py 2010-06-21 11:08:59 UTC (rev 113724)
+++ Zope/trunk/src/ZPublisher/BaseRequest.py 2010-06-21 14:47:19 UTC (rev 113725)
@@ -20,6 +20,7 @@
from Acquisition import aq_base
from Acquisition.interfaces import IAcquirer
+from ZPublisher.interfaces import UseTraversalDefault
from zExceptions import Forbidden
from zExceptions import NotFound
from zope.component import queryMultiAdapter
@@ -79,31 +80,35 @@
if name[:1]=='_':
raise Forbidden("Object name begins with an underscore at: %s" % URL)
- if hasattr(object,'__bobo_traverse__'):
- try:
- subobject=object.__bobo_traverse__(request, name)
- if type(subobject) is type(()) and len(subobject) > 1:
- # Add additional parents into the path
- # XXX There are no tests for this:
- request['PARENTS'][-1:] = list(subobject[:-1])
- object, subobject = subobject[-2:]
- except (AttributeError, KeyError, NotFound), e:
- # Try to find a view
- subobject = queryMultiAdapter((object, request), Interface, name)
- if subobject is not None:
- # OFS.Application.__bobo_traverse__ calls
- # REQUEST.RESPONSE.notFoundError which sets the HTTP
- # status code to 404
- request.response.setStatus(200)
- # We don't need to do the docstring security check
- # for views, so lets skip it and return the object here.
- if IAcquirer.providedBy(subobject):
- subobject = subobject.__of__(object)
- return subobject
- # No view found. Reraise the error raised by __bobo_traverse__
- raise e
- else:
- # No __bobo_traverse__
+ subobject = UseTraversalDefault # indicator
+ try:
+ if hasattr(object,'__bobo_traverse__'):
+ try:
+ subobject=object.__bobo_traverse__(request, name)
+ if type(subobject) is type(()) and len(subobject) > 1:
+ # Add additional parents into the path
+ # XXX There are no tests for this:
+ request['PARENTS'][-1:] = list(subobject[:-1])
+ object, subobject = subobject[-2:]
+ except (AttributeError, KeyError, NotFound), e:
+ # Try to find a view
+ subobject = queryMultiAdapter((object, request), Interface, name)
+ if subobject is not None:
+ # OFS.Application.__bobo_traverse__ calls
+ # REQUEST.RESPONSE.notFoundError which sets the HTTP
+ # status code to 404
+ request.response.setStatus(200)
+ # We don't need to do the docstring security check
+ # for views, so lets skip it and return the object here.
+ if IAcquirer.providedBy(subobject):
+ subobject = subobject.__of__(object)
+ return subobject
+ # No view found. Reraise the error raised by __bobo_traverse__
+ raise e
+ except UseTraversalDefault:
+ pass
+ if subobject is UseTraversalDefault:
+ # No __bobo_traverse__ or default traversal requested
# Try with an unacquired attribute:
if hasattr(aq_base(object), name):
subobject = getattr(object, name)
Modified: Zope/trunk/src/ZPublisher/interfaces.py
===================================================================
--- Zope/trunk/src/ZPublisher/interfaces.py 2010-06-21 11:08:59 UTC (rev 113724)
+++ Zope/trunk/src/ZPublisher/interfaces.py 2010-06-21 14:47:19 UTC (rev 113725)
@@ -56,5 +56,17 @@
something calls response.write() for the first time. Note that this is
carries a reference to the *response*, not the request.
"""
-
+
response = Attribute(u"The current HTTP response")
+
+
+# Exceptions
+
+class UseTraversalDefault(Exception):
+ """Indicate default traversal in ``__bobo_traverse__``
+
+ This exception can be raised by '__bobo_traverse__' implementations to
+ indicate that it has no special casing for the given name and that standard
+ traversal logic should be applied.
+
+ """
Modified: Zope/trunk/src/ZPublisher/tests/testBaseRequest.py
===================================================================
--- Zope/trunk/src/ZPublisher/tests/testBaseRequest.py 2010-06-21 11:08:59 UTC (rev 113724)
+++ Zope/trunk/src/ZPublisher/tests/testBaseRequest.py 2010-06-21 14:47:19 UTC (rev 113725)
@@ -124,6 +124,27 @@
return self, self._default_path
return DummyObjectWithBD()
+ def _makeObjectWithBBT(self):
+ from ZPublisher.interfaces import UseTraversalDefault
+
+ class _DummyResult(object):
+ ''' '''
+ def __init__(self, tag):
+ self.tag = tag
+
+ class DummyObjectWithBBT(self._makeBasicObjectClass()):
+ """ Dummy class with __bobo_traverse__
+ """
+ default = _DummyResult('Default')
+
+ def __bobo_traverse__(self, REQUEST, name):
+ if name == 'normal':
+ return _DummyResult('Normal')
+ elif name == 'default':
+ raise UseTraversalDefault
+ raise AttributeError(name)
+ return DummyObjectWithBBT()
+
def _makeObjectWithBDBBT(self):
class DummyObjectWithBDBBT(self._makeBasicObjectClass()):
"""Dummy class with __browser_default__."""
@@ -255,6 +276,16 @@
self.failUnlessRaises(NotFound, r.traverse,
'folder/objWithBBT/bbt_foo')
+ def test_traverse_UseTraversalDefault(self):
+ root, folder = self._makeRootAndFolder()
+ folder._setObject('objWithBBT', self._makeObjectWithBBT())
+ # test non default usage
+ r = self._makeOne(root)
+ self.assertEqual(r.traverse('folder/objWithBBT/normal').tag, 'Normal')
+ # test default usage
+ r = self._makeOne(root)
+ self.assertEqual(r.traverse('folder/objWithBBT/default').tag, 'Default')
+
def test_traverse_withBDBBT(self):
# Test for an object which has a __browser_default__
# and __bobo_traverse__
More information about the Zope-Checkins
mailing list