[Zope3-dev] Traversal without hiding deep exceptions

Steve Alexander steve at canonical.com
Thu Oct 14 15:18:58 EDT 2004


Recently, a software developer I work with had some difficulty debugging 
a problem in out large Zope3 application.

In DefaultTraversable, from src/zope/app/traversing/adapters.py, the 
traverse() method uses 'getattr' to see if 'subject' has the attribute 
'name'.

     def traverse(self, name, furtherPath):
         subject = self._subject
         __traceback_info__ = (subject, name, furtherPath)
         attr = getattr(subject, name, _marker)
         if attr is not _marker:
             return attr

         if hasattr(subject, '__getitem__'):
             # Let exceptions propagate.
             return subject[name]
         else:
             raise NotFoundError(subject, name)


The problem with this use of getattr is that the _marker will be 
returned not only if 'subject' lacks an attribute called 'name', but 
also if the attribute 'name' is a property (or other descriptor) that 
causes code to run that contains errors and raises AttributeError.

This "hiding" of AttributeErrors is intrinsic to Python.  I hope the 
behavior of getattr will change in a future version of Python so that it 
does not hide "deep" AttributeErrors.

But, I think we need to do better than the Python behaviour for Zope 3. 
  Zope 3 is all about integrating software in interesting and complex 
ways.  To make the process of integration workable, we need to avoid 
hiding such exceptions.

There is a way around this:  Some code in the interface package uses a 
trick from Phillip Eby to see whether an exception was raised 
"immediately" or "deeply" by examining the depth of the traceback.  I've 
put this code into a function:

   import sys

   def deep_exception():
       return sys.exc_info()[2].tb_next is not None

So, the code in traverse() could read something like:


     def traverse(self, name, furtherPath):
         subject = self._subject
         __traceback_info__ = (subject, name, furtherPath)

!       try:
!           return getattr(subject, name)
!       except AttributeError:
!           if deep_exception():
!               raise

         if hasattr(subject, '__getitem__'):
             # Let exceptions propagate.
             return subject[name]
         else:
             raise NotFoundError(subject, name)

-- 
Steve Alexander


More information about the Zope3-dev mailing list