10

我正在编写一个需要几分钟才能运行的脚本,并希望向用户提供一些关于其进度的输出。不幸的是,我非常懒惰。我想做的是编写一个没有日志记录的函数,然后对其应用一个装饰器,该装饰器逐步执行该函数并在执行该行之前打印每一行。基本上我正在寻找的是loggingdecorator这样的:

>>> @loggingdecorator
... def myfunction():
...     foo()
...     bar()
...     baz()
>>> myfunction()
Starting myfunction
foo() ... [OK]
bar() ... [OK]
baz() ... [OK]
myfunction Done!

这是我到目前为止所尝试的:

import sys


def logging_tracer(frame, event, arg):
    def local_tracer(local_frame, event, arg):
        if frame is local_frame:
            print frame.f_code.co_name, event, arg

    print frame.f_code.co_name, event, arg
    return local_tracer


def loggingdecorator(func):
    def _wrapper():
        old_trace_function = sys.gettrace()
        sys.settrace(logging_tracer)
        try:
            result = func()
        except:
            raise
        else:
            return result
        finally:
            sys.settrace(old_trace_function)
    return _wrapper

不幸的是,这打印得太多了。它遵循函数调用并将它们逐行打印出来(嗯,这实际上并没有打印源代码行,使用检查的现有答案,结合跟踪函数中框架对象上的东西会做到这一点),但我对于logging_tracer除非有问题的功能实际上是如何装饰的,我有点难过。

4

3 回答 3

4

所以这就是我想出的。 @Corley Brigman的评论让我朝着正确的方向开始。这有点骇人听闻,sys.gettrace/settrace被大量记录为“CPython 实现细节”,因此不应期望该解决方案在其他实现中起作用。也就是说,它似乎工作得很好。cpython 中的跟踪功能不提供“行已完成执行”的任何通知,所以[ok]我的问题没有任何意义。

修复递归跟踪问题只是一个简单的问题,即保留被修饰的函数的缓存,然后仅在被跟踪的帧来自被修饰的函数时才产生输出。

import inspect
import sys


def logging_tracer(frame, event, arg):
    lines, firstline = inspect.getsourcelines(frame)

    def local_tracer(local_frame, event, arg):
        if event == 'line' and frame is local_frame:
            print event, frame.f_lineno,'\t', lines[frame.f_lineno - firstline]
            #print event, lines[frame.f_lineno - firstline]
            #print frame.f_code.co_name, frame.f_lineno, event, arg

    if frame.f_code in LOG_THESE_FUNCTIONS:
        print event, frame.f_lineno,'\t', lines[frame.f_lineno - firstline + (event == 'call')]
        #print frame.f_code.co_name, event, arg
        return local_tracer
    else:
        return None


LOG_THESE_FUNCTIONS = set()


def loggingdecorator(func):
    LOG_THESE_FUNCTIONS.add(func.func_code)

    def _wrapper():
        old_trace_function = sys.gettrace()
        sys.settrace(logging_tracer)
        try:
            result = func()
        except:
            raise
        else:
            return result
        finally:
            sys.settrace(old_trace_function)
    return _wrapper
于 2013-10-07T20:39:14.437 回答
1

如果只有一个缩进级别,这是一个丑陋的例子:

import inspect


def myDec(func):
    temp = list(inspect.getsourcelines(func)[0])

    temp.append('    print("{} Done!")\n'.format(func.__name__))
    for index in range(len(temp)-2, 0, -1):
        temp.insert(index+1, "    print('''{}...[OK]''')\n".format(temp[index].strip().rstrip("\n")))
    temp.insert(1, '    print("Starting {}")\n'.format(func.__name__))
    temp = "".join(temp)
    exec(temp)
    return locals()[func.__name__]

def foo():
    a = 4+5
    list_int = range(100)


foo = myDec(foo)
foo()

虽然真的很丑...

于 2013-10-07T16:12:51.307 回答
0

你可以这样做:

>>> class loggingdecorator:
...     def __init__(self, func):
...             self.func = func
...     def __call__(self):
...             print "Entering", self.func.__name__
...             self.func()
...             print "Exited", self.func.__name__
...

然后使用您的每个功能:

>>> @loggingdecorator
... def func1():
...     print "Hello from func1(), how are you doing?"
... 
>>> @loggingdecorator
... def func2():
...     print "Hello from func2(), Whaddup Dawg...?"
... 
>>> func1()
Entering func1
Hello from func1(), how are you doing?
Exited func1
>>> func2()
Entering func2
Hello from func2(), Whaddup Dawg...?
Exited func2

从此页面找到示例

于 2013-10-07T15:02:42.323 回答