4

私はTrac用のマクロを作成しています。それが行うことの 1 つは、同じマクロを順番に使用できる、少しの wiki テキストをレンダリングすることです。

これにより、内部マクロが同じ引数で呼び出された場合 (つまり、同じウィキ テキストをレンダリングする場合)、無限再帰が発生する可能性があります。コール スタックを調べて、マクロを展開する関数がまったく同じ引数セットで既に呼び出されている場合は再帰を中断することで、ユーザーがこのように自分の足を撃たないようにすることを考えました。

これは間違いなく進むべき道のように思えますが、スタック上の前の関数の引数値を発見する方法をまだ理解できませんでしたこれどうやってするの?

4

3 に答える 3

7

再帰例外をキャッチするのがより良いアプローチですが、「保護」したい関数にデコレーターを追加することもできます。

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
于 2013-04-11T18:02:18.960 に答える