Showing the method's class in expection's traceback

A

Agustin Villena

Hi!

is there anyway to show the class of a method in an exception's
traceback?

For example, the next code

class Some(object):
def foo(self,x):
raise Exception(x)

obj = Some()
obj.foo("some arg")

produces the next traceback

Traceback (most recent call last):
File "<string>", line 231, in run_nodebug
File "G:\dev\exceptions\sample.py", line 7, in <module>
obj.foo("some arg")
File "G:\dev\exceptions\sample.py", line 3, in foo
raise Exception(x)
Exception: some arg

I want to improve the line
File "G:\dev\exceptions\sample.py", line 3, in foo

to
File "G:\dev\exceptions\sample.py", line 3, in Some.foo

Is this improvement feasible
 
D

Diez B. Roggisch

Agustin said:
Hi!

is there anyway to show the class of a method in an exception's
traceback?

For example, the next code

class Some(object):
def foo(self,x):
raise Exception(x)

obj = Some()
obj.foo("some arg")

produces the next traceback

Traceback (most recent call last):
File "<string>", line 231, in run_nodebug
File "G:\dev\exceptions\sample.py", line 7, in <module>
obj.foo("some arg")
File "G:\dev\exceptions\sample.py", line 3, in foo
raise Exception(x)
Exception: some arg

I want to improve the line
File "G:\dev\exceptions\sample.py", line 3, in foo

to
File "G:\dev\exceptions\sample.py", line 3, in Some.foo

Is this improvement feasible

It should be. You can get a dictionary of the locals of an exception
stack frame, of which you could extract the self-parameter's class.

Diez
 
G

Gabriel Genellina

Agustin Villena schrieb:

It should be. You can get a dictionary of the locals of an exception
stack frame, of which you could extract the self-parameter's class.

That by itself is not enough, the method could be inherited; one should walk the base classes in the MRO to find the right one. And deal with classmethods and staticmethods. And decorators that don't preserve meta information... Hmmm, I think it isn't so trivial as it seems.
 
D

Diez B. Roggisch

That by itself is not enough, the method could be inherited; one should
walk the base classes in the MRO to find the right one. And deal with
classmethods and staticmethods. And decorators that don't preserve meta
information... Hmmm, I think it isn't so trivial as it seems.

You might even have an instance-unique method. But I think the OP is
satisfied with a 90%-solution.

Diez
 
A

Agustin Villena

Agustin Villena schrieb:













It should be. You can get a dictionary of the locals of an exception
stack frame, of which you could extract the self-parameter's class.

Diez

Hi!

I digged on sys.exc_info() object and the traceback module and I
could't figure how I can get the locals() of the exception stackframe

Any advice?

Thanks
Agustin
 
G

Gabriel Genellina

En Mon, 19 May 2008 10:54:05 -0300, Agustin Villena
I digged on sys.exc_info() object and the traceback module and I
could't figure how I can get the locals() of the exception stackframe

Put this function in traceback.py, and replace the lines
name = co.co_name
with
name = guess_full_method_name(f)
everywhere.
Please read the docstring!!!

def guess_full_method_name(frame):
"""Guess classname.methodname for a given frame.

Only a guess!
Assumes the frame belongs to an instancemethod
whose first argument is "self", or a classmethod
whose first argument is "cls".
Doesn't handle correctly any other situation.
Returns the class name of the object on which
the method was called, not the class where
the method was actually defined.
"""
cls_name = None
fun_name = frame.f_code.co_name
self = frame.f_locals.get('self', None)
if self is None:
cls = frame.f_locals.get('cls', None)
else:
cls = getattr(self, '__class__', None)
if cls is not None:
cls_name = getattr(cls, '__name__', None)
if cls_name is not None:
return '%s.%s' % (cls_name, fun_name)
return fun_name
 
B

Bruno Desthuilliers

Gabriel Genellina a écrit :
En Sun, 18 May 2008 17:31:44 -0300, Diez B. Roggisch


That by itself is not enough, the method could be inherited; one
should walk the base classes in the MRO to find the right one. And
deal with classmethods and staticmethods. And decorators that don't
preserve meta information...

And monkeypatches.
Hmmm, I think it isn't so trivial as it
seems.

And not that useful - why would one care about the function being
defined in class X or Y when one have the exact file and line ?
 
R

Richard G Riley

Bruno Desthuilliers said:
Gabriel Genellina a écrit :

And monkeypatches.


And not that useful - why would one care about the function being
defined in class X or Y when one have the exact file and line ?

Very obvious I would think. One can develop ones own interactive class
browser and code navigator. One can not bring up class help from "line 3
in x.py" but one can from "classname : MyClass at line 2 in z.py". With
the class name I can navigate directly to all sorts of class related
information without the need to delve into a physical usage of it e.g
program.py at line 3.
 
B

Bruno Desthuilliers

Richard G Riley a écrit :
(snip)


Very obvious I would think. One can develop ones own interactive class
browser and code navigator.

From the traceback ?
One can not bring up class help from "line 3
in x.py" but one can from "classname : MyClass at line 2 in z.py". With
the class name I can navigate directly to all sorts of class related
information without the need to delve into a physical usage of it e.g
program.py at line 3.

Please bear with me, but I'm afraid I still don't get the point : I
don't need tracebacks to get to some object's info (help etc).
 
R

Richard G Riley

Bruno Desthuilliers said:
Richard G Riley a écrit :

From the traceback ?


Please bear with me, but I'm afraid I still don't get the point : I
don't need tracebacks to get to some object's info (help etc).

This is a view quite common to people when they are happy with what they
have. Some like to improve what they have. It is immediately apparent to
me why having a class name and method name in the traceback would be
immediately useful. Having written a few extensions to IDEs in the past
I know it would help me, and I suspect, the original poster. As a recent
"recruit" to Python I am quite surprised as how poor most of the query
like tools in the python shell are. The first thing I did was to move to
iPython. Still, horses for courses.
 
B

Bruno Desthuilliers

Richard G Riley a écrit :
This is a view quite common to people when they are happy with what they
have. Some like to improve what they have. It is immediately apparent to
me why having a class name and method name in the traceback would be
immediately useful.

Then please explain.
Having written a few extensions to IDEs in the past
I know it would help me,

To do what ?
 
A

Agustin Villena

And not that useful - why would one care about the function being
defined in class X or Y when one have the exact file and line ?

I have 3 reasons:

1) My developing time is expended running unit tests and browsing
tracebacks to find which is the real problem. Knowing the offender
class (instead of the method alone) makes me understand more quickly
which component of my software is failing.
2) There are some ocassions where I only have the traceback (e.g. when
analyzing an app's log) and no inmediate access to teh source code
3) And finally, for completeness: If a function is really a method, if
the traceback show only its name and not the class that defines it,
for me its a bug, because the method name has no sense out of its
class.

Just my two cents

Agustin
 
A

Agustin Villena

En Mon, 19 May 2008 10:54:05 -0300, Agustin Villena



Put this function in traceback.py, and replace the lines
name = co.co_name
with
name = guess_full_method_name(f)
everywhere.
Please read the docstring!!!

def guess_full_method_name(frame):
"""Guess classname.methodname for a given frame.

Only a guess!
Assumes the frame belongs to an instancemethod
whose first argument is "self", or a classmethod
whose first argument is "cls".
Doesn't handle correctly any other situation.
Returns the class name of the object on which
the method was called, not the class where
the method was actually defined.
"""
cls_name = None
fun_name = frame.f_code.co_name
self = frame.f_locals.get('self', None)
if self is None:
cls = frame.f_locals.get('cls', None)
else:
cls = getattr(self, '__class__', None)
if cls is not None:
cls_name = getattr(cls, '__name__', None)
if cls_name is not None:
return '%s.%s' % (cls_name, fun_name)
return fun_name

Thanks! I'll try it

Agustin
 
B

Bruno Desthuilliers

Agustin Villena a écrit :
I have 3 reasons:

1) My developing time is expended running unit tests and browsing
tracebacks to find which is the real problem. Knowing the offender
class (instead of the method alone) makes me understand more quickly
which component of my software is failing.

This is only true when there is an obvious, one-to-one, unambiguous
relationship between the physical location of the error (file, line) and
the class of the object the method has been called on. Which is not
necessarily the case (inheritance, method decoration and monkeypatching
comes to mind here...).

Also, your above statement seems to imply that component==class, which
is not the case in Python.
2) There are some ocassions where I only have the traceback (e.g. when
analyzing an app's log) and no inmediate access to teh source code

Ok. But the above still apply...
3) And finally, for completeness: If a function is really a method, if
the traceback show only its name and not the class that defines it,
for me its a bug, because the method name has no sense out of its
class.

I'm not sure you really grasp what "methods" are in Python. What you
define (using the def statement) within a class is function, not a
method. It only becomes a method when it's looked up on an instance or
class object, and this 'method' is only a thin wrapper around the
instance, class and function objects. And FWIW, you don't need to define
the function within the class to make it a method:

# bar.py
def bar(obj):
print "function bar.bar called on obj %s" % obj


# foo.py

class Foo(object): pass

# baaz.py
from foo import Foo
import bar

Foo.baaz = bar.bar

def guux(obj):
print "function baaz.guux called on obj %s" % obj

# main.py
from baaz import Foo
f = Foo()
f.bar()

f.gnix = baae.guux.__get__(f, type(f))
f.gnix()



Not to say that your concerns are pointless, and that things cannot be
improved somehow, but this is not that trivial, and there may be
ambuiguities in some not so rare cases.
 
A

Agustin Villena

Agustin Villena a écrit :




This is only true when there is an obvious, one-to-one, unambiguous
relationship between the physical location of the error (file, line) and
the class of the object the method has been called on. Which is not
necessarily the case (inheritance, method decoration and monkeypatching
comes to mind here...).

Also, your above statement seems to imply that component==class, which
is not the case in Python.


Ok. But the above still apply...


I'm not sure you really grasp what "methods" are in Python. What you
define (using the def statement) within a class is function, not a
method. It only becomes a method when it's looked up on an instance or
class object, and this 'method' is only a thin wrapper around the
instance, class and function objects. And FWIW, you don't need to define
the function within the class to make it a method:

# bar.py
def bar(obj):
print "function bar.bar called on obj %s" % obj

# foo.py

class Foo(object): pass

# baaz.py
from foo import Foo
import bar

Foo.baaz = bar.bar

def guux(obj):
print "function baaz.guux called on obj %s" % obj

# main.py
from baaz import Foo
f = Foo()
f.bar()

f.gnix = baae.guux.__get__(f, type(f))
f.gnix()

Not to say that your concerns are pointless, and that things cannot be
improved somehow, but this is not that trivial, and there may be
ambuiguities in some not so rare cases.

Well, the solution given in an early response is good enough for me.

I don't see things like you, because I'm accustomed to design my
software
though classes and see the code in an "object = software's functional
atom/component" way
I agree that python's dynamic nature make things complicated here, but
for me it its just
an implementation problem derived of the recent OOP support of python
 
T

Tim Golden

Agustin said:
I don't see things like you, because I'm accustomed to design my
software
though classes and see the code in an "object = software's functional
atom/component" way
I agree that python's dynamic nature make things complicated here, but
for me it its just
an implementation problem derived of the recent OOP support of python

(laughs). I do hope you're either joking or wearing a flameproof suit.
Python's dynamic nature as an implementation problem. Hmmm..
And what's that about "recent" OOP support of Python?

Oh well...

TJG
 
B

Bruno Desthuilliers

Agustin Villena a écrit :
Well, the solution given in an early response is good enough for me.

Not for me.
I don't see things like you, because I'm accustomed to design my
software
though classes and see the code in an "object = software's functional
atom/component" way

I guess you mean 'class = software's functional atom/component' -
because it's perfectly legal, in Python, to have per-instance methods...

Now the fact that *you* see it that way doesn't mean Python (and most
Python users) have to share your views, so labelling the way it works as
"a bug" sounds a bit arrogant to me.
I agree that python's dynamic nature make things complicated here, but
for me it its just
an implementation problem derived of the recent OOP support of python

I beg your pardon ? "recent OOP support in Python" ? Python had classes
and objects years before Oak was renamed as Java and made public, you know.
 
G

Gabriel Genellina

It might be worth considering an alternative approach here: a formatted
exception includes the relevant source lines (where possible).

Just to note that the standard cgitb module already does that, among other things.
 
G

Gabriel Genellina

Anywhere Python prints a traceback already does that. The 'alternative
approach' I was suggesting was the part you snipped, i.e. extracting the
enclosing class name from the source.

Ah, sorry for the misunderstanding!
 
A

Agustin Villena

To this therad, I received 2 kinds of anwsers:
- some that help me in
- and other where some guy thinks that has the right to rule if my
need has some value

Thanksfully, Python is an open platform, and with the help obtained
here, now I can fullfill my needs.
Who is the arrogant?
 

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

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top