Nick Coghlan <ncogh...@gmail.com> added the comment:

Note that my suggestion was to move the if statement out of the loop as-is: you 
would still be pulling the traceback to display from the caught exception 
rather than displaying the stack from the current point of execution. If you 
want the bottom most point to display where the actual exception occurred 
rather than the last line executed in the frame that caught it you need to be 
even smarter than that, though.

The information to construct the full stack trace properly is actually 
available, but it is necessary to be very careful as to how it is stitched 
together at the point where the traceback and the stack trace meet up.

For a full stack trace, this stitching actually needs to occur every time there 
is a jump from one exception to another. For finally clauses and reraised 
exceptions, the interpreter handles this internally so the traceback reflects 
the appropriate lines, but recreating a complete stack trace for the original 
exception in the face of PEP 3134 is going to require a bit of work in the 
traceback module.

Alternatively, you could just provide the full stack trace for the very last 
exception caught, leaving it to the reader to follow the traceback chain back 
down to the original exception.

Here's some useful code to explore this (I just spent some time playing with it 
to make sure I was giving the right answer here):

import sys
from traceback import print_exc, print_stack, print_tb
def f(n):
  this = "F%d" % n
  if n:
    try:
      f(n-1)
    except:
      print("*** Traceback in", this)
      print_exc(chain=False)
      print("*** Call stack in", this)
      print_stack()
      print("*** Replacing exception in", this)
      raise RuntimeError(this)
  print("*** Call stack in", this)
  print_stack()
  raise RuntimeError(this)

try:
  f(2)
except:
  etype, ex, tb = sys.exc_info()

"raise ex" will then show you the native display of that exception.

You can then use the context attributes to see what state is available to you:
>>> ex
RuntimeError('F2',)
>>> ex.__context__
RuntimeError('F1',)
>>> ex.__context__.__context__
RuntimeError('F0',)

In particular, we can see that the two inner exceptions are attached to frame 
objects which were used to run the nested function calls and hence have a frame 
that called them:
>>> ex.__traceback__.tb_frame.f_back
>>> ex.__context__.__traceback__.tb_frame.f_back
<frame object at 0x2118ff0>
>>> ex.__context__.__context__.__traceback__.tb_frame.f_back
<frame object at 0x2115b80>

The issue we have is that landing in the exception handlers means the state of 
those frames has been altered by the stack unwinding process. Let's compare the 
traceback for each exception with the current state of the corresponding frame 
(we skip the first traceback entry for our outermost function - it is there 
courtesy of the interactive loop and irrelevant to the current exploration):

>>> ex.__traceback__.tb_next.tb_lineno # Top level exception line
2
>>> ex.__traceback__.tb_next.tb_frame.f_lineno # Last executed line
4
>>> ex.__context__.__traceback__.tb_lineno # f(2) exception line
5
>>> ex.__context__.__traceback__.tb_frame.f_lineno # Last executed line
12
>>> ex.__context__.__context__.__traceback__.tb_lineno # f(1) exception line
5
>>> ex.__context__.__context__.__traceback__.tb_frame.f_lineno # Last executed 
>>> line
12

f(0) avoids triggering the exception handler and we can see that the traceback 
line and the last executed line match in that case:

>>> ex.__context__.__context__.__traceback__.tb_next.tb_lineno
15
>>> ex.__context__.__context__.__traceback__.tb_next.tb_frame.f_lineno
15

So yes, the idea proposed is possible, but no, a simple call to print_stack 
isn't going to do the right thing.

----------
keywords:  -needs review

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue1553375>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to