pekapa pekapa - 4 months ago 5x
Python Question

Python TypeError to give more information about arguments

I'm trying to implement a system to help the user when calling functions/methods.

I know the user can just

to get some kind of a documentation provided by me but I wanted to reimplement the
do it would also print that documentation if available.

For example:

Suppose I have:

def foo(bar):
''' Adds 1 to 'bar' and prints output '''
print 1+bar

And the user decide to call
(with no arguments)

It will raise a TypeError like this:

TypeError Traceback (most recent call last)
<ipython-input-9-624891b0d01a> in <module>()
----> 1 foo()

TypeError: foo() takes exactly 1 argument (0 given)

I would like it to also print the information from the
as well. i.e.:

Adds 1 to 'bar' and prints output

Any ideas on how to do that? I realise I need

  1. to detect the function that raised the TypeError

  2. get the help text for that function

  3. add it to the raised TypeError.

for 1) this seems to work:

import sys, traceback
# Get latest traceback information
tb = sys.exc_info()[-1]
stk = traceback.extract_tb(tb, 1)
# Extract called function and remove '()' - This actually limits functionality as the user might had inputed some extra arguments for example
fname = stk[0][-1]
fname = str(fname).split('()')[0]

for 2) and 3) and have no ideas on how to proceed... =/

Very much appreciated!

Edit for 3) I'm trying to override the default behaviour of TypeError, so far with no success.
I created a new MyError class just to test it and made:

import exceptions
exception.TypeError = MyError

but whenever the TypeError is raised, the original version comes up and not MyError

Edit 2 Ok, found out that I actually need to override the sys.excepthook method.

As a test, I created:

import sys
def handler(exc, value, tb):
print 'Oops'

sys.excepthook = handler

However, whenever a error occurs it still brings the original error and not the 'Oops' message. Also,
still returns the original message:

<bound method TerminalInteractiveShell.excepthook of <IPython.terminal.interactiveshell.TerminalInteractiveShell object at 0x10f4320d0>>

I also tried overriding the IPython.terminal.interactiveshell.TerminalInteractiveShell.excepthook with no success.

Any ideas on how to keep going?


Ok, I finally got it!

This answer is valid for both python and ipython! (I only tested with python 2.7, minor changes might be needed for python 3)

import sys
import traceback
import inspect

def value_handler(exc, value, tb):
    '''Changes the default message to include additional information.'''
    args = list(value)
    if exc == TypeError and '(' in str(value):
        # I want to change only TypeError from function calls.
        func_name = str(value).split('(')[0]
        func = globals()[func_name]
        func_doc = func.func_doc
        func_args = inspect.getargspec(func)[0]
        if func_doc is not None:
            args[0] += '\nDoc: \t{}'.format(func_doc)
        args[0] += '\nArgs: \t' + '\n\t'.join(func_args)

    # Apply changes to the original error
    value.args = args
    return value

def custom_ipython_exc(shell, exc, value, tb, tb_offset=None):
    '''Add aditional information and call original excepthook'''
    value = value_handler(exc, value, tb)
    shell.showtraceback((exc, value, tb), tb_offset=tb_offset)

def custom_python_exc(exc, value, tb):
    '''Add aditional information and call original excepthook'''
    value = value_handler(exc, value, tb)
    sys.__excepthook__(exc, value, tb)

except NameError:
    sys.excepthook = custom_python_exc
    get_ipython().set_custom_exc((Exception,), custom_exc)

With this, one should be able to add more information to any Error being raised.