如何获取缺少属性的对象

时间:2014-10-24 12:41:17

标签: python exception inspection

假设我们尝试访问不存在的属性:

>>> {'foo': 'bar'}.gte('foo')  # well, I meant “get”!

Python的AttributeError只有属性args,其中包含一个包含已完成错误消息的字符串:'dict' object has no attribute 'gte'

inspect和/或traceback模块与sys.last_traceback一起使用,是否有办法获取实际的dict对象?

>>> offending_object = get_attributeerror_obj(sys.last_traceback)
>>> dir(offending_object)
[...
 'clear',
 'copy',
 'fromkeys',
 'get',       # ah, here it is!
 'items',
 ...]

编辑:既然猫已经不在了,我会分享我的发现和代码(请不要解决这个并提交给PyPI,请;)

创建AttributeError here,表明显然没有对附加的原始对象的引用。

这里的代码具有相同的占位符函数:

import sys
import re
import difflib

AE_MSG_RE = re.compile(r"'(\w+)' object has no attribute '(\w+)'")

def get_attributeerror_obj(tb):
    ???

old_hook = sys.excepthook

def did_you_mean_hook(type, exc, tb):
    old_hook(type, exc, tb)

    if type is AttributeError:
        match = AE_MSG_RE.match(exc.args[0])
        sook = match.group(2)

        raising_obj = get_attributeerror_obj(tb)

        matches = difflib.get_close_matches(sook, dir(raising_obj))
        if matches:
            print('\n\nDid you mean?', matches[0], file=sys.stderr)

sys.excepthook = did_you_mean_hook

2 个答案:

答案 0 :(得分:1)

这不是你想要的答案,但我很确定你不能......至少不是sys.excepthook。这是因为引用计数在框架展开时递减,因此在调用sys.excepthook之前对对象进行垃圾回收是完全有效的。事实上,这就是CPython中发生的事情:

import sys

class X:
    def __del__(self):
        print("deleting")

def error():
    X().wrong

old_hook = sys.excepthook
def did_you_mean_hook(type, exc, tb):
    print("Error!")
sys.excepthook = did_you_mean_hook

error()
#>>> deleting
#>>> Error!

尽管如此,情况并非总是如此。因为异常对象指向框架,如果您的代码如下所示:

def error():
    x = X()
    x.wrong

x尚无法收集。 x由框架拥有,并且框架是活动的。但是,既然我已经证明没有明确的参考这个对象,那么做什么并不明显。例如,

def error():
    foo().wrong

可能有也可能没有幸存下来的对象,唯一可行的方法就是运行foo ......但即便如此,你也有副作用问题。

所以不,这是不可能的。如果你不介意任何长度,你可能最终不得不在加载时重写AST(类似于FuckIt.py)。但是你不想这样做。


我的建议是尝试使用linter来获取所有已知类及其方法的名称。您可以使用它来反向设计回溯字符串以获取类和不正确的方法,然后运行模糊匹配以查找建议。

答案 1 :(得分:1)

我成功地加入了2美分(到目前为止)尝试为DidYouMean-Python做类似的事情。

这里的诀窍在于,它几乎就是错误消息包含足够信息来推断您实际意味着什么的情况。实际上,这里真正重要的是你试图在gte对象上调用dict:你需要的是类型,而不是对象本身。

如果你写过{'foo': 'bar'}.get('foob'),那么处理的情况会更加棘手,我很乐意知道是否有人有解决方案。

第一步

检查您是否正在处理AttributeError(使用挂钩的第一个参数)。

第二步

从消息中检索相关信息(使用第二个参数)。我用regexp做到了这一点。请注意,此异常可以采用多种形式,具体取决于Python的版本,您调用方法的对象等。

到目前为止,我的正则表达式为:"^'?(\w+)'? (?:object|instance) has no attribute '(\w+)'$"

第三步

获取与该类型相对应的类型对​​象(在您的情况下为' dict'),以便您可以在其上调用dir()。一个肮脏的解决方案就是使用eval(type),但是你可以通过重用trace中的信息(钩子的第三个参数)来做得更好更干净:跟踪的最后一个元素包含发生异常的框架和该框架,类型已正确定义(作为本地类型,全局类型或内置)。

获得类型对象后,您只需要在其上调用dir()并提取您最喜欢的建议。

如果您需要有关我所做的更多详细信息,请与我们联系。