3

我可以获取回溯中调用的最后一个函数的参数吗?如何?

我想为标准错误制作一个捕捉器以制作可读的代码,同时向用户提供详细信息。

在下面的示例中,我希望 GET_PARAMS 返回提供给 os.chown 的参数元组。检查inspectAlex Martelli 建议的模块,我找不到。

def catch_errors(fn):
    def decorator(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except (IOError, OSError):
            msg = sys.exc_info()[2].tb_frame.f_locals['error_message']
            quit(msg.format(SEQUENCE_OF_PARAMETERS_OF_THE_LAST_FUNCTION_CALLED)\
            + '\nError #{0[0]}: {0[1]}'.format(sys.exc_info()[1].args), 1)
    return decorator

@catch_errors
def do_your_job():
    error_message = 'Can\'t change folder ownership \'{0}\' (uid:{1}, gid:{2})'
    os.chown('/root', 1000, 1000) # note that params aren't named vars.

if __name == '__main__' and os.getenv('USERNAME') != 'root':
    do_your_job()

(感谢Jim Robert的装饰)

4

3 回答 3

5

对于这样的检查任务,总是首先想到inspect标准库中的模块。在这里,inspect.getargvalues为您提供给定框架的参数值,而inspect.getinnerframes为您提供来自回溯对象的感兴趣的框架。

于 2009-10-30T16:09:46.470 回答
3

这是此类功能的示例以及您无法解决的一些问题:

import sys

def get_params(tb):
    while tb.tb_next:
        tb = tb.tb_next
    frame = tb.tb_frame
    code = frame.f_code
    argcount = code.co_argcount
    if code.co_flags & 4: # *args
        argcount += 1
    if code.co_flags & 8: # **kwargs
        argcount += 1
    names = code.co_varnames[:argcount]
    params = {}
    for name in names:
        params[name] = frame.f_locals.get(name, '<deleted>')
    return params


def f(a, b=2, c=3, *d, **e):
    del c
    c = 4
    e['g'] = 6
    assert False

try:
    f(1, f=5)
except:
    print get_params(sys.exc_info()[2])

输出是:

{'a': 1, 'c': 4, 'b': 2, 'e': {'g': 6, 'f': 5}, 'd': ()}

我没有用inspect.getinnerframes()另一种方法来获得所需的框架。虽然它简化了一点,但它也做了一些你不需要的额外工作,但速度相对较慢(inspect.getinnerframes()为回溯中的每个模块读取源文件;这对于一个调试调用并不重要,但在其他情况下可能是一个问题) .

于 2009-10-30T16:43:07.510 回答
0

使用装饰器来实现您想要实现的问题是异常处理程序获取的帧是do_your_job()s,而不是os.listdir()s、os.makedirs()s 或os.chown()s。因此,您将打印出的信息是do_your_job(). 为了获得我认为您想要的行为,您必须装饰您正在调用的所有库函数。

于 2009-10-30T16:38:06.180 回答