简体   繁体   English

Python TypeError提供有关参数的更多信息

[英]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 help(function) to get some kind of a documentation provided by me but I wanted to reimplement the TypeError do it would also print that documentation if available. 我知道用户可以只是通过help(function)来获得我提供的某种文档,但是我想重新实现TypeError ,它还会打印该文档(如果有)。


For example: 例如:
Suppose I have: 假设我有:

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

And the user decide to call foo() (with no arguments) 然后用户决定调用foo() (不带参数)
It will raise a TypeError like this: 它将引发一个TypeError:

---------------------------------------------------------------------------
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 help(foo) as well. 我也希望它也可以打印来自help(foo)的信息。 ie: 即:

foo(bar)
    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 检测引发TypeError的函数
  2. get the help text for that function 获取该功能的帮助文本
  3. add it to the raised TypeError. 将其添加到引发的TypeError中。

for 1) this seems to work: 对于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]

for 2) and 3) and have no ideas on how to proceed... =/ 对于2)和3),并且不知道如何进行... = /


Very much appreciated! 非常感谢!


Edit for 3) I'm trying to override the default behaviour of TypeError, so far with no success. 编辑 3)我试图覆盖TypeError的默认行为,到目前为止没有成功。 I created a new MyError class just to test it and made: 我创建了一个新的MyError类来进行测试并进行了以下操作:

import exceptions
exception.TypeError = MyError

but whenever the TypeError is raised, the original version comes up and not MyError 但是每当引发TypeError时,就会出现原始版本,而不是MyError


Edit 2 Ok, found out that I actually need to override the sys.excepthook method. 编辑2好的,发现我实际上需要重写sys.excepthook方法。

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, sys.excepthook still returns the original message: 同样, sys.excepthook仍返回原始消息:

<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. 我也尝试覆盖IPython.terminal.interactiveshell.TerminalInteractiveShell.excepthook,但没有成功。

Any ideas on how to keep going? 关于如何继续前进的任何想法?

For number two: 对于第二个:

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

For number three, you may want to use the raise keyword to raise an error. 对于第三个,您可能要使用raise关键字来引发错误。 You could probably use this with your first solution, but I've never used the traceback module so I can't help you there, sorry. 您可能会在第一个解决方案中使用它,但是对我而言,我从未使用过追溯模块,因此我无法为您提供帮助。

Ok, I finally got it! 好吧,我终于明白了!

This answer is valid for both python and ipython! 此答案对python和ipython均有效! (I only tested with python 2.7, minor changes might be needed for python 3) (我仅使用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)


With this, one should be able to add more information to any Error being raised. 有了它,人们应该能够向正在引发的任何错误中添加更多信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM