Commit 2c737818 authored by Christian Zagrodnick's avatar Christian Zagrodnick

- 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.
parent 56178d52
......@@ -127,6 +127,11 @@ Restructuring
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.
......
......@@ -30,6 +30,7 @@ from Acquisition import aq_parent
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 @@ class Traversable:
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:
# 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)
else:
if getattr(aq_base(obj), name, _marker) is not _marker:
if restricted:
next = guarded_getattr(obj, name)
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:
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)
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
......
......@@ -354,6 +354,33 @@ class TestTraverse( unittest.TestCase ):
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
......
......@@ -20,6 +20,7 @@ import xmlrpc
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 @@ class DefaultPublishTraverse(object):
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)
......
......@@ -56,5 +56,17 @@ class IPubBeforeStreaming(Interface):
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.
"""
......@@ -124,6 +124,27 @@ class BaseRequest_factory:
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 @@ class TestBaseRequest(unittest.TestCase, BaseRequest_factory):
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__
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment