问题描述
我正在为 Trac 创建一个宏,它要做的事情之一就是渲染一点Wiki文本,然后可以使用相同的宏.
I'm creating a macro for Trac, and one of the things it does is to render a bit of wiki text, that can in turn use the same macro.
如果使用相同的参数调用内部宏(即呈现相同的Wiki文本位),则可能导致无限递归.我想通过检查调用堆栈并中断递归(如果已经使用完全相同的一组参数调用扩展宏的函数)来试图阻止用户像这样射击自己的脚.
This can give rise to an infinite recursion if the inner macro is invoked with the same arguments (i.e., renders the same bit of wiki text). I thought of trying to stop the user from shooting his own foot like this by inspecting the call stack and breaking the recursion if the function that expands the macro was already invoked with exactly the same set of arguments.
我一直在查看检查模块,它看起来确实像的路要走,但仍然无法弄清楚如何发现堆栈上前一个函数的参数值.我该怎么办?
I've been looking at the inspect module, which definitely seems like the way to go, but still couldn't figure out how to discover the argument values of the previous function on the stack. How can I do this?
推荐答案
捕获递归异常是更好的方法,但是您也可以在要保护"的函数上添加装饰器:
Catching the recursion exception is the better approach, but you could also add a decorator on the functions you wanted to 'protect':
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
然后将该装饰器应用于宏渲染功能.
then apply that decorator to the macro render function.
一个简单的演示
>>> @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
这篇关于检测无限递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!