Deeper tracebacks?

B

brooklineTom

I want my exception handler to report the method that originally
raised an exception, at the deepest level in the call-tree. Let give
an example.

import sys, traceback
class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()

def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector, None)
answer = apply(aMethod, [], {})
except: AttributeError, anAttributeErrorException:
aRawStack = traceback.extract_stack()
answer = None

When I call "perform_" (... SomeClass().perform_('error')), I want to
collect and report the location *within the method ("error") that
failed*. The above code reports the location of "perform_", and no
deeper in the call tree.

Anybody know how to accomplish this?
 
G

Gabriel Genellina

I want my exception handler to report the method that originally
raised an exception, at the deepest level in the call-tree. Let give
an example.

That's the default behavior, you don't have to do anything special.
import sys, traceback
class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()

def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector, None)
answer = apply(aMethod, [], {})
except: AttributeError, anAttributeErrorException:
aRawStack = traceback.extract_stack()
answer = None

(I assume you're using Python < 3.0)
Use the 3-names form of the except statement:

try:
aMethod = getattr(self, aSelector, None)
answer = aMethod()
except AttributeError, e, tb:
# the tb variable holds the traceback up to the error
# the same thing you see printed by Python when
# an unhandled error happens
answer = None


Alternatively, you can obtain the same thing with sys.exc_info()[2]
Remember to delete any reference to the traceback object as soon as you're
done with it; see http://docs.python.org/library/sys.html#sys.exc_info
 
B

brooklineTom

I want my exception handler to report the method that originally
raised an exception, at the deepest level in the call-tree. Let give
an example.

That's the default behavior, you don't have to do anything special.
import sys, traceback
class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()
def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector, None)
answer = apply(aMethod, [], {})
except: AttributeError, anAttributeErrorException:
aRawStack = traceback.extract_stack()
answer = None

(I assume you're using Python < 3.0)
Use the 3-names form of the except statement:

try:
aMethod = getattr(self, aSelector, None)
answer = aMethod()
except AttributeError, e, tb:
# the tb variable holds the traceback up to the error
# the same thing you see printed by Python when
# an unhandled error happens
answer = None

Alternatively, you can obtain the same thing with sys.exc_info()[2]
Remember to delete any reference to the traceback object as soon as you're
done with it; seehttp://docs.python.org/library/sys.html#sys.exc_info

I'm using Python 2.5.

As I understand it, "aRawStack" (above) has the same information as
sys.exc_info()[2].

The deepest entry in aRawStack is the perform_ invocation. The
contents of the two bottom-most stack frames are:
aRawStack[8][3] "answer = anObject.perform_('error')"
aRawStack[9][3]
'aRawStack = traceback.extract_stack()'


By the time the handler is called, "zork" -- the method that was
called when the exception was raised, and "error", the method that
invoked zork, have already been removed from the stack.

In this example, I only show one call for simplicity. In practice, the
method being invoked by perform_ may nest calls arbitrarily deeply. I
know, by being in the exception handler, that an exception happened.
What I need to know is which nested call raised it.
 
B

brooklineTom

En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <[email protected]>
escribió:
I want my exception handler to report the method that originally
raised an exception, at the deepest level in the call-tree. Let give
an example.
That's the default behavior, you don't have to do anything special.
import sys, traceback
class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()
def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector, None)
answer = apply(aMethod, [], {})
except: AttributeError, anAttributeErrorException:
aRawStack = traceback.extract_stack()
answer = None
(I assume you're using Python < 3.0)
Use the 3-names form of the except statement:
try:
aMethod = getattr(self, aSelector, None)
answer = aMethod()
except AttributeError, e, tb:
# the tb variable holds the traceback up to the error
# the same thing you see printed by Python when
# an unhandled error happens
answer = None
Alternatively, you can obtain the same thing with sys.exc_info()[2]
Remember to delete any reference to the traceback object as soon as you're
done with it; seehttp://docs.python.org/library/sys.html#sys.exc_info
I'm using Python 2.5.
As I understand it, "aRawStack" (above) has the same information as
sys.exc_info()[2].
The deepest entry in aRawStack is the perform_ invocation. The
contents of the two bottom-most stack frames are:
aRawStack[8][3]
"answer = anObject.perform_('error')"
aRawStack[9][3]
'aRawStack = traceback.extract_stack()'
By the time the handler is called, "zork" -- the method that was
called when the exception was raised, and "error", the method that
invoked zork, have already been removed from the stack.

There is no zork() method, so it *can't have been called* and
therefore *can't be in the traceback*. The error lies in the method
that's trying to call a _nonexistent_ method, and that's where Python
reports the error.

Recall that:
x.zork()

is equivalent to:
_z = x.zork #error occurs here, in the attribute lookup
_z() #Python never gets here

So no call takes place because you get an AttributeError before Python
can get any further. zork() would only in the traceback if (1) it was
looked up and called successfully [not the case here] and (2) an error
occurred in zork()'s method body [again, not the case because it's not
defined].

Cheers,
Chris

I understand that there is no method named "zork". The attempted call
to zork occurred in the method named "error", *not* the method named
"perform_". I suggest that the failing method is "error", and the
reported line number should be the line that contains "int(3).zork".

Specifically, I think that aRawStack should contain the following:
"answer = anObject.perform_('error')"
"answer = anObject.error()"
'aRawStack = traceback.extract_stack()'

The entry at aRawStack[9] is the stack frame that I suggest is
missing.

Thx,
Tom
 
R

R. Bernstein

brooklineTom said:
I want my exception handler to report the method that originally
raised an exception, at the deepest level in the call-tree. Let give
an example.

import sys, traceback
class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()

def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector, None)
answer = apply(aMethod, [], {})
except: AttributeError, anAttributeErrorException:
aRawStack = traceback.extract_stack()
answer = None

When I call "perform_" (... SomeClass().perform_('error')), I want to
collect and report the location *within the method ("error") that
failed*. The above code reports the location of "perform_", and no
deeper in the call tree.

Anybody know how to accomplish this?

extract_stack() without any arguments is getting this from the
*current frame* which as you noted doesn't have the last exception
info included which has been popped; variable sys.last_traceback has the frames
at the time of the exception, I think.

So in your code try changing:
aRawStack = traceback.extract_stack()
to
aRawStack = traceback.extract_stack(sys.last_traceback)
 
B

brooklineTom

BINGO! Give that man a CIGAR!

The specifics don't seem to be quite right (there is no
sys.last_traceback), but R.Bernstein put me directly on the correct
track. I misunderstood the traceback module documentation. OK, I just
plain didn't read it correctly, it's right there ("extract_stack():
Extract the raw traceback from the current stack frame."). The answer
is to use traceback.extract_tb (), called with sys.exc_info()[3] (the
stackframe that raised the exception).

Another sterling example of the benefits of reading the fine manual --
for comprehension this time.

Instead of:
aRawStack = traceback.extract_stack()
do this:
aRawStack = traceback.extract_tb(sys.exc_info()[2])

With this change, aRawStack contains the following:
aRawStack[0][3] 'answer = apply(aMethod, [], {})'
aRawStack[1][3]
'int(3).zork()'

This is *exactly* what I was seeking.

I appreciate all the responses, and especially appreciate the pointer
from R.Bernstein.

Thx,
Tom

brooklineTom said:
I want my exception handler to report the method that originally
raised an exception, at the deepest level in the call-tree. Let give
an example.
import sys, traceback
class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()
def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector, None)
answer = apply(aMethod, [], {})
except: AttributeError, anAttributeErrorException:
aRawStack = traceback.extract_stack()
answer = None
When I call "perform_" (... SomeClass().perform_('error')), I want to
collect and report the location *within the method ("error") that
failed*. The above code reports the location of "perform_", and no
deeper in the call tree.
Anybody know how to accomplish this?

extract_stack() without any arguments is getting this from the
*current frame* which as you noted doesn't have the last exception
info included which has been popped; variable sys.last_traceback has the frames
at the time of the exception, I think.

So in your code try changing:
aRawStack = traceback.extract_stack()
to
aRawStack = traceback.extract_stack(sys.last_traceback)
 
G

Gabriel Genellina

extract_stack() without any arguments is getting this from the
*current frame* which as you noted doesn't have the last exception
info included which has been popped; variable sys.last_traceback has the
frames
at the time of the exception, I think.

So in your code try changing:
aRawStack = traceback.extract_stack()
to
aRawStack = traceback.extract_stack(sys.last_traceback)

No, last_traceback is the last *printed* traceback in the interactive
interpreter. Use the third element in sys.exc_info() instead:

import sys, traceback

class SomeClass:
def error(self):
"""Raises an AttributeError exception."""
int(3).zork()

def perform_(self, aSelector):
try:
aMethod = getattr(self, aSelector)
answer = aMethod()
except AttributeError:
tb = sys.exc_info()[2]
try:
print "Using traceback.print_tb:"
traceback.print_tb(tb)
print "Using traceback.print_exception:"
traceback.print_exception(*sys.exc_info())
print "Using traceback.extract_tb:"
print traceback.extract_tb(tb)
finally:
del tb

SomeClass().perform_("error")

output:

Using traceback.print_tb:
File "test_tb.py", line 11, in perform_
answer = aMethod()
File "test_tb.py", line 6, in error
int(3).zork()
Using traceback.print_exception:
Traceback (most recent call last):
File "test_tb.py", line 11, in perform_
answer = aMethod()
File "test_tb.py", line 6, in error
int(3).zork()
AttributeError: 'int' object has no attribute 'zork'
Using traceback.extract_tb:
[('test_tb.py', 11, 'perform_', 'answer = aMethod()'), ('test_tb.py', 6,
'error'
, 'int(3).zork()')]

(The "3-name form of the except clause" that I menctioned previously only
exists in my imagination :) )
 
R

R. Bernstein

No, last_traceback is the last *printed* traceback in the interactive
interpreter.

Well more precisely the traceback that is passed to sys.excepthook()
when an unhandled exception occcurs, since the hook that might not
decide to print anything ;-)
Use the third element in sys.exc_info() instead:

Hmm... I'm not sure what I was thinking when I read that way back
when, but you are correct and caught a bug in my code. I really do
need to do better about writing tests. Maybe next
incarnation... Thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top