[Zope-Checkins]
SVN: Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/
Change ZPublisher behavior to catch AttributeError and KeyError on
Sidnei da Silva
sidnei at awkly.org
Mon Oct 4 16:55:50 EDT 2004
Log message for revision 27745:
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.
Changed:
U Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/BaseRequest.py
U Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/tests/testBaseRequest.py
-=-
Modified: Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/BaseRequest.py
===================================================================
--- Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/BaseRequest.py 2004-10-04 20:44:01 UTC (rev 27744)
+++ Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/BaseRequest.py 2004-10-04 20:55:50 UTC (rev 27745)
@@ -308,15 +308,20 @@
"Object name begins with an underscore at: %s" % URL)
else: return response.forbiddenError(entry_name)
- if hasattr(object,'__bobo_traverse__'):
- subobject=object.__bobo_traverse__(request,entry_name)
- if type(subobject) is type(()) and len(subobject) > 1:
- # Add additional parents into the path
- parents[-1:] = list(subobject[:-1])
- object, subobject = subobject[-2:]
- else:
+ bobo_got = 0
+ try:
+ if hasattr(object,'__bobo_traverse__'):
+ subobject=object.__bobo_traverse__(request,entry_name)
+ bobo_got = 1
+ if type(subobject) is type(()) and len(subobject) > 1:
+ # Add additional parents into the path
+ parents[-1:] = list(subobject[:-1])
+ object, subobject = subobject[-2:]
+ except (AttributeError, KeyError):
+ pass
+
+ if not bobo_got:
try:
-
# Note - no_acquire_flag is necessary to support
# things like DAV. We have to make sure
# that the target object is not acquired
@@ -362,7 +367,7 @@
# certain mutable types (dicts, lists) to become publishable
# when they shouldn't be. The following check makes sure that
# the right thing happens in both 2.2.2+ and earlier versions.
-
+
if not typeCheck(subobject):
return response.debugError(
"The object at %s is not publishable." % URL
Modified: Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/tests/testBaseRequest.py
===================================================================
--- Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/tests/testBaseRequest.py 2004-10-04 20:44:01 UTC (rev 27744)
+++ Zope/branches/dc-bobo_traverse-branch/lib/python/ZPublisher/tests/testBaseRequest.py 2004-10-04 20:55:50 UTC (rev 27745)
@@ -43,6 +43,24 @@
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):
"""Dummy class with docstring."""
@@ -53,7 +71,14 @@
raise RuntimeError('Infinite loop detected.')
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):
def setUp(self):
@@ -64,6 +89,8 @@
self.f1._setObject('objWithDefaultNone', DummyObjectWithDefaultNone() )
self.f1._setObject('objWithBPTH', DummyObjectWithBPTH() )
self.f1._setObject('objWithBD', DummyObjectWithBD() )
+ self.f1._setObject('objWithBBT', DummyObjectWithBBT() )
+ self.f1._setObject('objWithBDBBT', DummyObjectWithBDBBT() )
def makeBaseRequest(self):
response = HTTPResponse()
@@ -131,6 +158,58 @@
self.f1.objWithBD._default_path = ['']
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):
r = self.makeBaseRequest()
r['PARENTS'] = [self.f1.objWithDefault]
More information about the Zope-Checkins
mailing list