1

我正在尝试实现一个系统来帮助用户调用函数/方法。

我知道用户可以只help(function)获取我提供的某种文档,但我想重新实现TypeError它也会打印该文档(如果可用)。


例如:
假设我有:

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

并且用户决定调用foo()(不带参数)
它将引发这样的 TypeError:

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

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

我希望它也可以打印来自的信息help(foo)。IE:

foo(bar)
    Adds 1 to 'bar' and prints output

关于如何做到这一点的任何想法?我意识到我需要

  1. 检测引发 TypeError 的函数
  2. 获取该函数的帮助文本
  3. 将其添加到引发的 TypeError 中。

1)这似乎有效:

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]

对于 2) 和 3) 并且不知道如何进行... =/


非常感谢!


编辑3) 我正在尝试覆盖 TypeError 的默认行为,但到目前为止没有成功。我创建了一个新的 MyError 类只是为了测试它并做了:

import exceptions
exception.TypeError = MyError

但是每当引发 TypeError 时,就会出现原始版本而不是 MyError


编辑 2好的,发现我实际上需要重写 sys.excepthook 方法。

作为测试,我创建了:

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

sys.excepthook = handler

但是,无论何时发生错误,它仍然会带来原始错误,而不是“糟糕”消息。此外,sys.excepthook仍然返回原始消息:

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

我还尝试覆盖 IPython.terminal.interactiveshell.TerminalInteractiveShell.excepthook 没有成功。

关于如何继续前进的任何想法?

4

2 回答 2

0

对于第二个:

>>> def foo(bar):
...     '''Adds "1" to bar and prints output'''
...     return 1 + bar
...
>>> print foo.__doc__
Adds "1" to bar and prints output

对于第三个,您可能希望使用raise关键字来引发错误。您可能可以在您的第一个解决方案中使用它,但我从未使用过 traceback 模块,所以我无法帮助您,抱歉。

于 2013-09-18T12:29:53.970 回答
0

好的,我终于明白了!

这个答案对python和ipython都有效!(我只用 python 2.7 测试过,python 3 可能需要做一些小改动)

import sys
import traceback
import inspect


def get_args(func):
    """Get function arguments, varargs, kwargs and defaults.

    This function will be called whenever a exception is raised."""
    args, varargs, kwargs, defs = inspect.getargspec(func)

    if defs is not None:
        # If there are defaults, include them in the main argument list
        for i in range(-len(defs), 0):
            args[i] += ' = {}'.format(defs[i])

    if varargs is not None:
        # If the function accept extra arguments, include them at the end of the list.
        args.append('*' + varargs)

    if kwargs is not None:
        # If the function accept extra keyworded arguments, include them at the end of the list.
        args.append('**' + kwargs)

    return args  # args contain information about all the function arguments.


def value_handler(exc, value, tb):
    """Changes the default message to include additional information.

    This handler will be called whenever an exception happens."""
    args = list(value)  # Value typically contains the error message.

    if exc == TypeError and '()' in str(value):
        # I want to change only TypeError from function calls.
        func_name = str(value).split('()')[0]  # Obtain the function name from the error message.
        func = globals()[func_name]  # Get function object from globals
        func_doc = func.__doc__      # Function docstring
        func_args = get_args(func)   # Get function arguments
        if func_doc is not None:     # Add docstring to the message
            args[0] += '\nDoc: \t{}'.format(func_doc)
        args[0] += '\nArgs: \t' + '\n\t'.join(func_args)  # Finally, add all the arguments to the message.

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

def custom_exc(shell, exc, value, tb, tb_offset=None):
    """Add aditional information and call original excepthook

    This version works with iPython"""
    value = value_handler(exc, value, tb)  # Changes the error message
    shell.showtraceback((exc, value, tb), tb_offset=tb_offset)  # Raise the error with the new message (keeping traceback and other information).

def custom_python_exc(exc, value, tb):
    """Add aditional information and call original excepthook

    This version works with regular python"""
    value = value_handler(exc, value, tb)  # Changes the error message
    sys.__excepthook__(exc, value, tb)     # Raise the error with the new message (keeping traceback and other information).

try:
    __IPYTHON__  # Check if we're running iPython
except NameError:
    # Not iPython enviroment, override excepthook
    sys.excepthook = custom_python_exc
else:
    # iPython enviroment, need to set custom excepthook
    get_ipython().set_custom_exc((Exception,), custom_exc)


有了这个,人们应该能够向任何引发的错误添加更多信息。

于 2016-06-04T15:48:58.940 回答