检测无限递归 [英] Detecting infinite recursion

查看:99
本文介绍了检测无限递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为 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

这篇关于检测无限递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆