Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







Recipe 8.6. Getting More Information from Tracebacks


Credit: Bryn Keller


Problem


You want to display all of the available information when an uncaught
exception is raised.


Solution


A traceback object is basically a linked list of nodes, in which each
node refers to a frame object. Frame objects, in turn, form their own
linked list in the opposite order from the linked list of traceback
nodes, so we can walk back and forth if needed. This recipe exploits
this structure and the rich amount of information held by frame
objects, including, in particular, the dictionary of local variables
for the function corresponding to each frame:

import sys, traceback
def print_exc_plus( ):
"" Print the usual traceback information, followed by a listing of
all the local variables in each frame.
""
tb = sys.exc_info( )[2]
while tb.tb_next:
tb = tb.tb_next
stack = [ ]
f = tb.tb_frame
while f:
stack.append(f)
f = f.f_back
stack.reverse( )
traceback.print_exc( )
print "Locals by frame, innermost last"
for frame in stack:
print
print "Frame %s in %s at line %s" % (frame.f_code.co_name,
frame.f_code.co_filename,
frame.f_lineno)
for key, value in frame.f_locals.items( ):
print "\t%20s = " % key,
# we must _absolutely_ avoid propagating exceptions, and str(value)
# COULD cause any exception, so we MUST catch any...:
try:
print value
except:
print "<ERROR WHILE PRINTING VALUE>"


Discussion


The standard Python TRaceback module provides
useful functions to give information about where and why an error
occurred. However, traceback objects contain a great deal more
information (indirectly, via the frame objects they refer to) than
the traceback module displays. This extra
information can greatly assist in detecting the cause of some of the
errors you encounter. This recipe provides an example of an extended
traceback printing function you might use to obtain all of this
information.

Here's a simplistic demonstration of the kind of
problem this approach can help with. Basically, we have a simple
function that manipulates all the strings in a list. The function
doesn't do any error checking, so, when we pass a
list that contains something other than strings, we get an error.
Figuring out which bad data caused the error is easier with our new
print_exc_plus function to help us:

data = ["1", "2", 3, "4"]     # Typo: we 'forget' the quotes on data[2]
def pad4(seq):
""
Pad each string in seq with zeros up to four places. Note that there
is no reason to actually write this function; Python already
does this sort of thing much better. It's just an example!
""
return_value = [ ]
for thing in seq:
return_value.append("0" * (4 - len(thing)) + thing)
return return_value

Here's the (limited) information we get from a
normal traceback.print_exc:

>>> try:
... pad4(data)
... except:
... traceback.print_exc( )
...
Traceback (most recent call last):
File "<stdin>", line 2, in ?
File "<stdin>", line 9, in pad4
TypeError: len( ) of unsized object

Now here's how it looks when displaying the info
with the function from this recipe instead of the standard
traceback.print_exc:

>>> try:
... pad4(data)
... except:
... print_exc_plus( )
...
Traceback (most recent call last):
File "<stdin>", line 2, in ?
File "<stdin>", line 9, in pad4
TypeError: len( ) of unsized object
Locals by frame, innermost last
Frame ? in <stdin> at line 4
sys = <module 'sys' (built-in)>
pad4 = <function pad4 at 0x007C6210>
_ _builtins_ _ = <module '_ _builtin_ _' (built-in)>
_ _name_ _ = _ _main_ _
data = ['1', '2', 3, '4']
_ _doc_ _ = None
print_exc_plus = <function print_exc_plus at 0x00802038>
Frame pad4 in <stdin> at line 9
thing = 3
return_value = ['0001', '0002']
seq = ['1', '2', 3, '4']

Note how easy it is to see the bad data that caused the problem. The
thing variable has a value of 3,
so we know why we got the TypeError. A quick look
at the value for data shows that we simply forgot
the quotes on that item. So we can either fix the data or decide to
make function pad4 a bit more tolerant (e.g., by
changing the loop to for thing in map(str, seq)).
These kind of design choices are important, but the point of this
recipe is to save you time in understanding what's
going on, so you can make your design choices with all the available
information.

The recipe relies on the fact that each traceback object refers to
the next traceback object in the stack through the
tb_next field, forming a linked list. Each
traceback object also refers to a corresponding frame object through
the tb_frame field, and each frame refers to the
previous frame through the f_back field (a linked
list going the other way around from that of the traceback objects).

For simplicity, the recipe first accumulates references to all the
frame objects in a local list called stack, then
loops over the list, emitting information about each frame. For each
frame, it first emits some basic information (e.g., function name,
filename, line number, etc.) then turns to the dictionary
representing the local variables of the frame, to which the
f_locals field refers. Just like for the
dictionaries built and returned by the locals and
globals built-in functions, each key is a variable
name, and the corresponding value is the variable's
value. Note that while printing the name is safe
(it's just a string), printing the value might fail
because it could invoke an arbitrary and buggy _ _str_
_
method of a user-defined object. So, the value is printed
within a try/except statement,
to prevent the propagation of an uncaught exception while another
exception is being handled. An except clause that
does not list the exceptions to catch, and thus catches every
exception, is almost always a mistake, but this recipe exemplifies
the almost part of this statement!

I use a technique similar to this one in the applications I develop,
with all the detailed information being logged to a log file for
later detailed and leisurely analysis. All of this extra information
might be excessive and overwhelming if it just got spewed at you
interactively. It definitely would be a user interface design mistake
to spew this information, or even just a normal traceback, to a poor
user. Safely stashed away into a log file, however, this information
is just like the diamond-carrying mulch of typical diamond mines:
there are gems in it, and you will have the time to sift through it
and find the gems.


See Also


Recipe 8.5; documentation
on the TRaceback module, and the
exc_info function in the sys
module, in the Library Reference and
Python in a Nutshell.


/ 394