Function call arguments in stack trace?

D

Dun Peal

Hi,

In a stack trace, is it possible to somehow get the arguments with
which each function was called?

So for example, if function `foo` in module `bar` was called with
arguments `(1, [2])` when it raised an exception, then instead of:

Traceback (most recent call last):
File "bar.py", line 123, in foo
build_rpms()

The stack trace would read:

Traceback (most recent call last):
File "bar.py", line 123, in foo(1, [2])
build_rpms()

This would save a lot of debugging time!

Thanks, D.
 
N

Neil Cerutti

Hi,

In a stack trace, is it possible to somehow get the arguments with
which each function was called?

So for example, if function `foo` in module `bar` was called with
arguments `(1, [2])` when it raised an exception, then instead of:

Traceback (most recent call last):
File "bar.py", line 123, in foo
build_rpms()

The stack trace would read:

Traceback (most recent call last):
File "bar.py", line 123, in foo(1, [2])
build_rpms()

This would save a lot of debugging time!

Use pdb.
 
D

Dun Peal


Neil, thanks for the tip; `pdb` is indeed a great debugging tool.

Still, it doesn't obviate the need for arguments in the stack trace.
For example:

1) Arguments in stack trace can expedite a debugging session, and even
obviate it completely: "Why did `foo()` fail? Oh, because it got `-1`
as its first argument, while I only coded for positive integers!".
2) In some environments, it's very hard to recreate a rare exception
and analyze it with `pdb`. For instance, on a web application that
emails the stack traces of unhandled exceptions, it's very important
for that stack trace to be as informative as possible, since often
that's the only debugging feedback you will get.

Hope that makes sense, D.
 
I

Ian Kelly

Neil, thanks for the tip; `pdb` is indeed a great debugging tool.

Still, it doesn't obviate the need for arguments in the stack trace.

Your program could use sys.excepthook to generate a custom stack trace
for unhandled exceptions. All the stack frames are available from the
traceback, but extracting the arguments would be tricky, and getting
the original arguments would be impossible if they've been reassigned
prior to the exception being raised. It would be simpler just to dump
all the locals in the frame.
 
I

Irmen de Jong

Neil, thanks for the tip; `pdb` is indeed a great debugging tool.

Still, it doesn't obviate the need for arguments in the stack trace.

If you can't use pdb perhaps you can use the following:

Pyro has always had a feature that prints detailed stacktraces. It is mainly meant to
clarify stacktraces that occur on a different machine (where you don't have the option
of using pdb), but can very well be used for normal code too:


import sys
import Pyro4.util
Pyro4.config.DETAILED_TRACEBACK=True
sys.excepthook=Pyro4.util.excepthook

def divide(a,b):
return a//b

def dividebysomething(a):
return divide(a,0)

print dividebysomething(10)


When you run this, this will be printed:

[E:\projects]python trace.py
--------------------------------------------------
<<type 'exceptions.ZeroDivisionError'>> RAISED : integer division or modulo by zero
Extended stacktrace follows (most recent call last)
--------------------------------------------------
File "trace.py", line (13), in <module>
Source code:
print dividebysomething(10)
File "trace.py", line (11), in dividebysomething
Source code:
return divide(a,0)
Local values:
a = 10
--------------------------------------------------
File "trace.py", line (8), in divide
Source code:
return a//b
Local values:
a = 10
b = 0
--------------------------------------------------
<<type 'exceptions.ZeroDivisionError'>> RAISED : integer division or modulo by zero
--------------------------------------------------


You can find the relevant code that produces these kinds of tracebacks in the util.py
source file of Pyro. You can get that from Pypi:
http://pypi.python.org/pypi/Pyro4/
or the file directly from subversion:
$ svn export svn://svn.razorvine.net/Pyro/Pyro4/trunk/src/Pyro4/util.py

Perhaps you can use this or adapt it to suit your needs.


Irmen de Jong
 
N

Neil Cerutti

Neil, thanks for the tip; `pdb` is indeed a great debugging
tool.

Still, it doesn't obviate the need for arguments in the stack
trace. For example:

1) Arguments in stack trace can expedite a debugging session, and even
obviate it completely: "Why did `foo()` fail? Oh, because it got `-1`
as its first argument, while I only coded for positive integers!".
2) In some environments, it's very hard to recreate a rare exception
and analyze it with `pdb`. For instance, on a web application that
emails the stack traces of unhandled exceptions, it's very important
for that stack trace to be as informative as possible, since often
that's the only debugging feedback you will get.

Hope that makes sense, D.

The locals should be in the frame object of the traceback. Here's
a sketch of a decorator to print them out before your program
bombs:

import sys

def report_arg_info(fn):
def wrapper(*arg, **kw):
try:
return fn(*arg, **kw)
except:
frame = sys.exc_info()[2].tb_next.tb_frame
print(frame.f_locals)
raise
return wrapper

Use it as usual:

@report_arg_info
def my_func(bombs)
raise ValueError

You could log the local arguments instead.
 
G

Gabriel Genellina

In a stack trace, is it possible to somehow get the arguments with
which each function was called?

So for example, if function `foo` in module `bar` was called with
arguments `(1, [2])` when it raised an exception, then instead of:

Traceback (most recent call last):
File "bar.py", line 123, in foo
build_rpms()

The stack trace would read:

Traceback (most recent call last):
File "bar.py", line 123, in foo(1, [2])
build_rpms()

This would save a lot of debugging time!

The cgitb module does exactly that; some third-party modules offer similar
functionality, but I don't remember any names.
Despite its name, cgitb works with any script.

Given this test script:

# begin test_traceback.py
import cgitb
cgitb.enable(format="text")

spam = []

def a(x, y):
"This is function a"
z = x+y
return b(z)


def b(z, n=3):
"""This is function b.

Its docstring is longer."""

if n!=3:
just(to_consume_space)

w = c(foo=z*n)

return w


def c(foo=0, bar=1):
"This is function c"
baz = foo+bar
spam.somenamethatdoesnotexist(foo+bar)
anotherglobal("thatdoesnotexisteither")

a(10, 20)
# end test_traceback.py

the output is:


AttributeError
Python 3.2: d:\apps\Python32\python.exe
Tue Jun 7 23:36:36 2011

A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they occurred.

D:\TEMP\test_traceback.py in <module>()
27 baz = foo+bar
28 spam.somenamethatdoesnotexist(foo+bar)
29 anotherglobal("thatdoesnotexisteither")
30
31 a(10, 20)
a = <function a>

D:\TEMP\test_traceback.py in a(x=10, y=20)
7 "This is function a"
8 z = x+y
9 return b(z)
10
11
global b = <function b>
z = 30

D:\TEMP\test_traceback.py in b(z=30, n=3)
18 just(to_consume_space)
19
20 w = c(foo=z*n)
21
22 return w
w undefined
global c = <function c>
foo undefined
z = 30
n = 3

D:\TEMP\test_traceback.py in c(foo=90, bar=1)
26 "This is function c"
27 baz = foo+bar
28 spam.somenamethatdoesnotexist(foo+bar)
29 anotherglobal("thatdoesnotexisteither")
30
global spam = []
spam.somenamethatdoesnotexist undefined
foo = 90
bar = 1
AttributeError: 'list' object has no attribute 'somenamethatdoesnotexist'
[... exception attributes ...]
[... original traceback ...]
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top