Skip to content Skip to sidebar Skip to footer

Detecting Infinite Recursion

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. This can give rise to an infinite recursion if th

Solution 1:

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

defrecursion_detector(func):
    func._thread_locals = local()

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

        ifnothasattr(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.

A simple demo:

>>>@recursion_detector
... def foo(bar):
...     return foo(not bar)
... 
>>> foo(True)
Traceback (most recent calllast):
  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 functionwith the same arguments
>>> foo(False)
Traceback (most recent calllast):
  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 functionwith the same arguments

Solution 2:

It's easier to just catch the recursion error when it happens, than trying to catch it before it happens, in runtime.

If that's not an option, analyzing the template before rendering could be a way forward as well.

Solution 3:

Equally simple would be to pass dictionary to keep track of used arguments and at the beginning check if argument has already been tried.

Post a Comment for "Detecting Infinite Recursion"