Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   Deeper tracebacks? (http://www.velocityreviews.com/forums/t648086-deeper-tracebacks.html)

brooklineTom 12-10-2008 06:59 PM

Deeper tracebacks?
 
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?

Gabriel Genellina 12-10-2008 10:03 PM

Re: Deeper tracebacks?
 
En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <BrooklineTom@gmail.com>
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; see http://docs.python.org/library/sys.html#sys.exc_info

--
Gabriel Genellina


brooklineTom 12-10-2008 11:50 PM

Re: Deeper tracebacks?
 
On Dec 10, 5:03 pm, "Gabriel Genellina" <gagsl-...@yahoo.com.ar>
wrote:
> En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <Brookline...@gmail.com>
> 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
>
> --
> Gabriel Genellina


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.

brooklineTom 12-11-2008 04:48 AM

Re: Deeper tracebacks?
 
On Dec 10, 10:49 pm, "Chris Rebert" <c...@rebertia.com> wrote:
> On Wed, Dec 10, 2008 at 3:50 PM, brooklineTom <Brookline...@gmail.com> wrote:
> > On Dec 10, 5:03 pm, "Gabriel Genellina" <gagsl-...@yahoo.com.ar>
> > wrote:
> >> En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <Brookline...@gmail.com>
> >> 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

>
> >> --
> >> Gabriel Genellina

>
> > 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
>
> --
> Follow the path of the Iguana...http://rebertia.com


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:

>>> aRawStack[8][3]

"answer = anObject.perform_('error')"

>>> aRawStack[9][3]

"answer = anObject.error()"

>>> aRawStack[10][3]

'aRawStack = traceback.extract_stack()'

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

Thx,
Tom

R. Bernstein 12-11-2008 09:49 AM

Re: Deeper tracebacks?
 
brooklineTom <BrooklineTom@gmail.com> writes:

> 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)

brooklineTom 12-11-2008 05:26 PM

Re: Deeper tracebacks?
 
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

On Dec 11, 4:49 am, ro...@panix.com (R. Bernstein) wrote:
> brooklineTom <Brookline...@gmail.com> writes:
> > 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)


Gabriel Genellina 12-11-2008 06:33 PM

Re: Deeper tracebacks?
 
En Thu, 11 Dec 2008 07:49:42 -0200, R. Bernstein <rocky@panix.com>
escribió:
> brooklineTom <BrooklineTom@gmail.com> writes:
>
>> 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.


> 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 :) )

--
Gabriel Genellina


R. Bernstein 12-13-2008 04:14 AM

Re: Deeper tracebacks?
 
"Gabriel Genellina" <gagsl-py2@yahoo.com.ar> writes:
...

> 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.


All times are GMT. The time now is 06:20 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.