检测无限递归

Fil*_*eia 2 python recursion trac metaprogramming

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

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

我一直在看看检查模块,这看起来似乎是要走的路,但仍然无法弄清楚如何在堆栈上发现前一个函数的参数值.我怎样才能做到这一点?

Mar*_*ers 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
Run Code Online (Sandbox Code Playgroud)

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

一个简单的演示:

>>> @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
Run Code Online (Sandbox Code Playgroud)