7

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

>>> {'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是在此处创建的,这表明显然没有对附加的原始对象的引用。

这里的代码具有相同的占位符功能:

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
4

2 回答 2

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 来获取所有已知类的名称及其方法。您可以使用它对回溯字符串进行逆向工程以获取类和不正确的方法,然后运行模糊匹配以查找建议。

于 2014-10-25T02:57:22.750 回答
1

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

这里的诀窍是,错误消息包含足够的信息来推断您的实际意思几乎是一种情况。事实上,这里真正重要的是你试图调用gte一个dict对象:你需要的是类型,而不是对象本身。

如果您写{'foo': 'bar'}.get('foob')了这种情况,处理起来会更加棘手,我很高兴知道是否有人有解决方案。

第一步

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

第二步

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

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

第三步

获取与类型对应的类型对​​象(在您的情况下为“dict”),以便您可以调用dir()它。一个肮脏的解决方案只是使用eval(type),但您可以通过重用跟踪中的信息(钩子的第三个参数)做得更好和更清洁:跟踪的最后一个元素包含发生异常的帧以及该帧中的类型被正确定义(作为本地类型、全局类型或内置类型)。

一旦你有了 type 对象,你只需要调用dir()它并提取你最喜欢的建议。

如果您需要有关我所做工作的更多详细信息,请告诉我。

于 2014-11-04T12:13:32.977 回答