简体   繁体   中英

locals() and globals() in stack trace on exception (Python)

虽然堆栈跟踪在 Python 中很有用,但问题根源的数据通常会丢失——有没有办法确保至少将 locals()(可能还有 globals())添加到打印的堆栈跟踪中?

You can install your own exception hook and output what you need from there:

import sys, traceback

def excepthook(type, value, tb):
    traceback.print_exception(type, value, tb)

    while tb.tb_next:
        tb = tb.tb_next

    print >>sys.stderr, 'Locals:',  tb.tb_frame.f_locals
    print >>sys.stderr, 'Globals:', tb.tb_frame.f_globals

sys.excepthook = excepthook

def x():
    y()

def y():
    foo = 1
    bar = 0

    foo/bar

x()

To print vars from each frame in a traceback, change the above loop to

    while tb:
        print >>sys.stderr, 'Locals:',  tb.tb_frame.f_locals
        print >>sys.stderr, 'Globals:', tb.tb_frame.f_globals
        tb = tb.tb_next

This is a Box of Pandora. Values can be very large in printed form; printing all locals in a stack trace can easily lead to new problems just due to error output. That's why this is not implemented in general in Python.

In small examples, though, ie if you know that your values aren't too large to be printed properly, you can step along the traceback yourself:

import sys
import traceback

def c():
  clocal = 1001
  raise Exception("foo")

def b():
  blocal = 23
  c()

def a():
  alocal = 42
  b()

try:
  a()
except Exception:
  frame = sys.exc_info()[2]
  formattedTb = traceback.format_tb(frame)    
  frame = frame.tb_next
  while frame:
    print formattedTb.pop(0), '\t', frame.tb_frame.f_locals
    frame = frame.tb_next

The output will be sth like this:

  File "/home/alfe/tmp/stacktracelocals.py", line 19, in <module>
    a()
        {'alocal': 42}
  File "/home/alfe/tmp/stacktracelocals.py", line 16, in a
    b()
        {'blocal': 23}
  File "/home/alfe/tmp/stacktracelocals.py", line 12, in b
    c()
        {'clocal': 1001}

And you can, of course, install your own except hook as thg435 suggested in his answer.

if you didn't know about this already, use the pdb post-mortem feature:

x = 3.0
y = 0.0
print x/y
def div(a, b):
    return a / b
print div(x,y)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-3-d03977de5fc3> in div(a, b)
      1 def div(a, b):
----> 2     return a / b

ZeroDivisionError: float division

import pdb
pdb.pm()
> <ipython-input-3-148da0dcdc9e>(2)div()
      0     return a/b

ipdb> l
      1 def div(a,b):
----> 2     return a/b

ipdb> a
3.0
ipdb> b
0.0

etc.

there are cases where you really need the prints though, of course. you're better off instrumenting the code (via try/except) to print out extra information around a specific weird exception you are debugging than putting this for everything though, imho.

Try traceback-with-variables package.

Usage:

from traceback_with_variables import traceback_with_variables

def main():
    ...
    with traceback_with_variables():
        ...your code...

Exceptions with it:

Traceback with variables (most recent call last):
  File "./temp.py", line 7, in main
    return get_avg_ratio([h1, w1], [h2, w2])
      sizes_str = '300 200 300 0'
      h1 = 300
      w1 = 200
      h2 = 300
      w2 = 0
  File "./temp.py", line 10, in get_avg_ratio
    return mean([get_ratio(h, w) for h, w in [size1, size2]])
      size1 = [300, 200]
      size2 = [300, 0]
  File "./temp.py", line 10, in <listcomp>
    return mean([get_ratio(h, w) for h, w in [size1, size2]])
      .0 = <tuple_iterator object at 0x7ff61e35b820>
      h = 300
      w = 0
  File "./temp.py", line 13, in get_ratio
    return height / width
      height = 300
      width = 0
builtins.ZeroDivisionError: division by zero

Installation:

pip install traceback-with-variables

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM