Smart help again

B

bearophileHUGS

Hello, here I extend the idea of the smart help I've discussed
recently.
When I receive an error like:

TypeError: fun() takes exactly 2 arguments (1 given)

I'd also like to see that method/function parameters (or the first line
of its docstring).
From a discussion with gentle programmers in another newsgroup, I've
see that this problem can probably be solved with something like:

.. import sys, exceptions, difflib, inspect
..
.. def excepthook(type, value, tb):
.. extra = ""
.. if sys.last_type == exceptions.TypeError:
.. # extra = string of function/method +
.. # parameters inspect.getargspec(function or method)
.. import traceback
.. tblines = traceback.format_exception(type, value, tb)
.. tblines.append(extra)
.. print "".join( map(str, tblines) )
.. sys.exit(1)
..
.. sys.excepthook = excepthook


inspect.getargspec is useful, but how can I find there the class+method
or (function name) of the raised error? I can find the function name
with something like this, but it doesn't look like a nice solution, and
it cannot be used to find the class of the method:

extra = str(sys.last_value)
extra = extra[:extra.find("()")]

Thank you,
Bearophile
 
W

wittempj

You can create your own Exception class, based on thisrecipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52215, it will
look like

-import sys, traceback
-
-class Error:
- def __init__(self, arg):
- self._arg = arg
- tb = sys.exc_info()[2]
- while 1:
- if not tb.tb_next:
- break
- 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 have to be careful not to cause a new error in our
-error
- #printer! Calling str() on an unknown object could
cause -an
- #error we don't want.
- try:
- print value
- except:
- print "<ERROR WHILE PRINTING VALUE>"-
-
- def __repr__(self):
- return repr(self._arg)
-

suppose this class is in a module named test.py, then your code can be
like:

-#!/usr/bin/env python
-import test, sys

-try:
- try:
- class Spam:
- def eggs(self):
- return 1/0
- foo = Spam()
- foo.eggs()
- except:
- raise test.Error('xxx')
-except test.Error, e:
- print >> sys.stderr, e.__class__.__name__, e


The output will be:
-Locals by frame, innermost last
-
-Frame ? in ./test2.py at line 12
- Spam = __main__.Spam
- __builtins__ = <module '__builtin__' (built-in)>
- __file__ = ./test2.py
- sys = <module 'sys' (built-in)>
- test = <module 'test' from
'/home/martin/test.pyc'>
- __name__ = __main__
- foo = <__main__.Spam instance at 0xb7e0720c>
- __doc__ = None

-Frame eggs in ./test2.py at line 8
- self = <__main__.Spam instance at 0xb7e0720c>
-Error 'xxx'
 
B

bearophileHUGS

You can create your own Exception class, based on this recipe:<

Thank you for your answer, the recipe you have suggested gives a very
big output, but it doesn't contain what I've asked for... :) I was
asking to see that faulty method/function parameters (or the first line
of its docstring).
Probably the use of sys.excepthook is better, because you don't need
that
except: raise test.Error('xxx')
I've studied that solution of yours, and I think I still have to learn
many things about Python :)

Bye, thank you,
Bearophile
 
W

wittempj

I hope a rewrite makes it a bit more clear for you.

The test module is defined below, it contains a simplified Error
object, it resemble the one I use a lot in my own scripting. The test
file generates an error ( A ZeroDivisionError in method eggs of class
Spam).

-#!/usr/bin/env python

-import test, sys
-
-try:
- try:
- class Spam:
- def eggs(self):
- return 1/0
- foo = Spam()
- foo.eggs()
- except Exception, e:
- raise test.Error('%s: %s' % (e.__class__.__name__, e))
-except test.Error, e:
- print >> sys.stderr, e.__class__.__name__, e

The Error class takes the last exception, and tries to find the
originating method of that exception, by unwinding the stacktrace


-import sys, traceback
-class Error:
- def __init__(self, arg):
- tb = sys.exc_info()[2]
- while 1:
- if not tb.tb_next:
- break
- tb = tb.tb_next
- stack = []
- f = tb.tb_frame
- while f:
- stack.append(f)
- f = f.f_back
-
- error_originator = stack[0]
-
- (obj, instance) = error_originator.f_locals.items()[0]
-
- self._str = 'class instance: %s, method: %s raises %s' % \
- (instance, \
- error_originator.f_code.co_name, \
- arg)
-
- def __repr__(self):
- return repr(self._str)

Now the output is:


Error 'class instance: <__main__.Spam instance at 0xb7e0444c>, method:
eggs raises ZeroDivisionError: integer division or modulo by zero'

Now you see the class and methos where the exception was raised,
although I admit that it is quite complicated with all the exception
handling
 

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,773
Messages
2,569,594
Members
45,120
Latest member
ShelaWalli
Top