Commit aa87b334 authored by Sidnei da Silva's avatar Sidnei da Silva

Change ZPublisher behavior to catch AttributeError and KeyError on

__bobo_traverse__ and then fallback to attribute and item access if
such an exception is caught. Added some tests to verify the behavior.
parent ae3e9a42
...@@ -308,15 +308,20 @@ class BaseRequest: ...@@ -308,15 +308,20 @@ class BaseRequest:
"Object name begins with an underscore at: %s" % URL) "Object name begins with an underscore at: %s" % URL)
else: return response.forbiddenError(entry_name) else: return response.forbiddenError(entry_name)
if hasattr(object,'__bobo_traverse__'): bobo_got = 0
subobject=object.__bobo_traverse__(request,entry_name) try:
if type(subobject) is type(()) and len(subobject) > 1: if hasattr(object,'__bobo_traverse__'):
# Add additional parents into the path subobject=object.__bobo_traverse__(request,entry_name)
parents[-1:] = list(subobject[:-1]) bobo_got = 1
object, subobject = subobject[-2:] if type(subobject) is type(()) and len(subobject) > 1:
else: # Add additional parents into the path
parents[-1:] = list(subobject[:-1])
object, subobject = subobject[-2:]
except (AttributeError, KeyError):
pass
if not bobo_got:
try: try:
# Note - no_acquire_flag is necessary to support # Note - no_acquire_flag is necessary to support
# things like DAV. We have to make sure # things like DAV. We have to make sure
# that the target object is not acquired # that the target object is not acquired
...@@ -362,7 +367,7 @@ class BaseRequest: ...@@ -362,7 +367,7 @@ class BaseRequest:
# certain mutable types (dicts, lists) to become publishable # certain mutable types (dicts, lists) to become publishable
# when they shouldn't be. The following check makes sure that # when they shouldn't be. The following check makes sure that
# the right thing happens in both 2.2.2+ and earlier versions. # the right thing happens in both 2.2.2+ and earlier versions.
if not typeCheck(subobject): if not typeCheck(subobject):
return response.debugError( return response.debugError(
"The object at %s is not publishable." % URL "The object at %s is not publishable." % URL
......
...@@ -43,6 +43,24 @@ class DummyObjectWithBPTH(DummyObjectBasic): ...@@ -43,6 +43,24 @@ class DummyObjectWithBPTH(DummyObjectBasic):
REQUEST._hacked_path=1 REQUEST._hacked_path=1
class DummyObjectWithBBT(DummyObjectBasic):
""" Dummy class with docstring.
"""
def __bobo_traverse__(self, REQUEST, name):
raise AttributeError, name
def dummyMethod(self):
"""Dummy method with docstring."""
return 'Dummy Value'
def __getitem__(self, name):
if name.startswith('no_key_'):
raise KeyError, name
name = name.replace('key_', '')
return getattr(self, name)
class DummyObjectWithBD(DummyObjectBasic): class DummyObjectWithBD(DummyObjectBasic):
"""Dummy class with docstring.""" """Dummy class with docstring."""
...@@ -53,6 +71,13 @@ class DummyObjectWithBD(DummyObjectBasic): ...@@ -53,6 +71,13 @@ class DummyObjectWithBD(DummyObjectBasic):
raise RuntimeError('Infinite loop detected.') raise RuntimeError('Infinite loop detected.')
return self, self._default_path return self, self._default_path
class DummyObjectWithBDBBT(DummyObjectWithBD):
"""Dummy class with docstring."""
def __bobo_traverse__(self, REQUEST, name):
if name == self.default_path[0]:
return getattr(self, name)
raise AttributeError, name
class TestBaseRequest(TestCase): class TestBaseRequest(TestCase):
...@@ -64,6 +89,8 @@ class TestBaseRequest(TestCase): ...@@ -64,6 +89,8 @@ class TestBaseRequest(TestCase):
self.f1._setObject('objWithDefaultNone', DummyObjectWithDefaultNone() ) self.f1._setObject('objWithDefaultNone', DummyObjectWithDefaultNone() )
self.f1._setObject('objWithBPTH', DummyObjectWithBPTH() ) self.f1._setObject('objWithBPTH', DummyObjectWithBPTH() )
self.f1._setObject('objWithBD', DummyObjectWithBD() ) self.f1._setObject('objWithBD', DummyObjectWithBD() )
self.f1._setObject('objWithBBT', DummyObjectWithBBT() )
self.f1._setObject('objWithBDBBT', DummyObjectWithBDBBT() )
def makeBaseRequest(self): def makeBaseRequest(self):
response = HTTPResponse() response = HTTPResponse()
...@@ -131,6 +158,58 @@ class TestBaseRequest(TestCase): ...@@ -131,6 +158,58 @@ class TestBaseRequest(TestCase):
self.f1.objWithBD._default_path = [''] self.f1.objWithBD._default_path = ['']
self.failUnlessRaises(NotFound, r.traverse, 'folder/objWithBD') self.failUnlessRaises(NotFound, r.traverse, 'folder/objWithBD')
def test_traverse_withBBT_handles_AttributeError(self):
# Test that if __bobo_traverse__ raises AttributeError
# that we get a NotFound
from ZPublisher import NotFound
r = self.makeBaseRequest()
self.failUnlessRaises(NotFound, r.traverse, 'folder/objWithBBT/bbt_foo')
def test_traverse_withBBT_fallback_getattr(self):
# Test that if __bobo_traverse__ raises AttributeError
# that we fallback to getattr()
r = self.makeBaseRequest()
r.traverse('folder/objWithBBT/dummyMethod')
self.assertEqual(r.URL, '/folder/objWithBBT/dummyMethod')
def test_traverse_withBBT_fallback_getitem(self):
# Test that if __bobo_traverse__ raises AttributeError
# and getattr raises AttributeError
# that we fallback to __getitem__
r = self.makeBaseRequest()
r.traverse('folder/objWithBBT/key_dummyMethod')
self.assertEqual(r.URL, '/folder/objWithBBT/key_dummyMethod')
def test_traverse_withBBT_fallback_getitem_NotFound(self):
# Test that if all else fails, we get a NotFound
from ZPublisher import NotFound
r = self.makeBaseRequest()
self.failUnlessRaises(NotFound, r.traverse,
'folder/objWithBBT/no_key_dummyMethod')
def test_traverse_withBDBBT(self):
# Test for an object which has a __browser_default__
# and __bobo_traverse__
# __bobo_traverse__ should return the object
# pointed by __browser_default__
r = self.makeBaseRequest()
self.f1.objWithBDBBT._default_path = ['view']
r.traverse('folder/objWithBDBBT')
self.assertEqual(r.URL, '/folder/objWithBDBBT/view')
self.assertEqual(r.response.base, '/folder/objWithBDBBT/')
def test_traverse_withBDBBT_NotFound(self):
# Test for an object which has a __browser_default__
# and __bobo_traverse__
# __bobo_traverse__ should raise an AttributeError, which will
# end up falling back to getattr, then __getitem__ to finally
# raise a NotFound
from ZPublisher import NotFound
r = self.makeBaseRequest()
self.f1.objWithBDBBT._default_path = ['xxx']
r = self.makeBaseRequest()
self.failUnlessRaises(NotFound, r.traverse, 'folder/objWithBDBBT')
def test_traverse_slash(self): def test_traverse_slash(self):
r = self.makeBaseRequest() r = self.makeBaseRequest()
r['PARENTS'] = [self.f1.objWithDefault] r['PARENTS'] = [self.f1.objWithDefault]
......
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