Deeper tracebacks?

Discussion in 'Python' started by brooklineTom, Dec 10, 2008.

  1. brooklineTom

    brooklineTom Guest

    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?
    brooklineTom, Dec 10, 2008
    #1
    1. Advertising

  2. En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <>
    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
    Gabriel Genellina, Dec 10, 2008
    #2
    1. Advertising

  3. brooklineTom

    brooklineTom Guest

    On Dec 10, 5:03 pm, "Gabriel Genellina" <>
    wrote:
    > En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <>
    > 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, Dec 10, 2008
    #3
  4. brooklineTom

    brooklineTom Guest

    On Dec 10, 10:49 pm, "Chris Rebert" <> wrote:
    > On Wed, Dec 10, 2008 at 3:50 PM, brooklineTom <> wrote:
    > > On Dec 10, 5:03 pm, "Gabriel Genellina" <>
    > > wrote:
    > >> En Wed, 10 Dec 2008 16:59:16 -0200, brooklineTom <>
    > >> 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
    brooklineTom, Dec 11, 2008
    #4
  5. brooklineTom

    R. Bernstein Guest

    brooklineTom <> 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)
    R. Bernstein, Dec 11, 2008
    #5
  6. brooklineTom

    brooklineTom Guest

    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, (R. Bernstein) wrote:
    > brooklineTom <> 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, Dec 11, 2008
    #6
  7. En Thu, 11 Dec 2008 07:49:42 -0200, R. Bernstein <>
    escribió:
    > brooklineTom <> 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
    Gabriel Genellina, Dec 11, 2008
    #7
  8. brooklineTom

    R. Bernstein Guest

    "Gabriel Genellina" <> 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.
    R. Bernstein, Dec 13, 2008
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?U0VC?=

    how to parse deeper nested tags in custom control

    =?Utf-8?B?U0VC?=, Nov 25, 2005, in forum: ASP .Net
    Replies:
    2
    Views:
    1,840
    =?Utf-8?B?U0VC?=
    Nov 26, 2005
  2. jenniferyiu
    Replies:
    0
    Views:
    347
    jenniferyiu
    Dec 3, 2003
  3. jenniferyiu
    Replies:
    0
    Views:
    390
    jenniferyiu
    Dec 3, 2003
  4. Replies:
    7
    Views:
    468
  5. Durgesh Sharma
    Replies:
    5
    Views:
    882
    E. Robert Tisdale
    Dec 21, 2004
Loading...

Share This Page