检测无限递归

时间:2013-04-11 17:53:48

标签: python recursion metaprogramming trac

我正在为Trac创建一个宏,它所做的一件事就是渲染一些wiki文本,然后可以使用相同的宏。

如果使用相同的参数调用内部宏(即,呈现相同的维基文本位),这可能会导致无限递归。我想通过检查调用堆栈并在扩展宏的函数已经使用完全相同的参数集调用时,试图阻止用户像这样射击自己的脚。

我一直在关注inspect module,这看起来似乎是要走的路,但仍然无法弄清楚如何在堆栈上发现前一个函数的参数值。我怎么能这样做?

3 个答案:

答案 0 :(得分:6)

捕获递归异常是更好的方法,但您也可以在要保护的函数上添加装饰器:

from functools import wraps
from threading import local

def recursion_detector(func):
    func._thread_locals = local()

    @wraps(func)
    def wrapper(*args, **kwargs):
        params = tuple(args) + tuple(kwargs.items())

        if not hasattr(func._thread_locals, 'seen'):
            func._thread_locals.seen = set()
        if params in func._thread_locals.seen:
            raise RuntimeError('Already called this function with the same arguments')

        func._thread_locals.seen.add(params)
        try:
            res = func(*args, **kwargs)
        finally:
            func._thread_locals.seen.remove(params)

        return res

    return wrapper

然后将该装饰器应用于宏渲染功能。

一个简单的演示:

>>> @recursion_detector
... def foo(bar):
...     return foo(not bar)
... 
>>> foo(True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 10, in wrapper
  File "<stdin>", line 3, in foo
  File "<stdin>", line 10, in wrapper
  File "<stdin>", line 3, in foo
  File "<stdin>", line 7, in wrapper
RuntimeError: Already called this function with the same arguments
>>> foo(False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 10, in wrapper
  File "<stdin>", line 3, in foo
  File "<stdin>", line 10, in wrapper
  File "<stdin>", line 3, in foo
  File "<stdin>", line 7, in wrapper
RuntimeError: Already called this function with the same arguments

答案 1 :(得分:3)

当发生递归错误时,比在运行时尝试捕获它更容易。

如果这不是一个选项,那么在渲染之前分析模板也可能是一种方法。

答案 2 :(得分:0)

同样简单的是传递字典以跟踪使用的参数,并在开始时检查参数是否已经被尝试过。