2013-04-11 2 views
1

Trac 매크로를 만들고 있는데, 매크로 중 하나는 같은 매크로를 사용할 수있는 위키 텍스트를 렌더링하는 것입니다.무한 재귀 검색

내부 매크로가 동일한 인수로 호출되는 경우 (즉, 동일한 비트의 wiki 텍스트를 렌더링하는 경우) 무한 재귀가 발생할 수 있습니다. 매크로를 확장하는 함수가 이미 동일한 인수 집합을 사용하여 호출 된 경우 호출 스택을 검사하고 재귀를 중단하여 사용자가 자신의 발을 직접 촬영하지 못하도록 막으려 고 생각했습니다.

나는 확실히 이동하는 것처럼 보이지만 여전히 스택에있는 이전 함수의 인수 값을 찾는 방법을 파악할 수 없었던 inspect module을보고있었습니다. 어떻게해야합니까?

답변

5

는 더 나은 방법입니다,하지만 당신은 당신이 '보호'하고 싶은 기능에 장식을 추가 할 수 있습니다

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 
+0

위대한, 고마워! 앞으로의 독자들에게 [this post] (http://stackoverflow.com/a/1894371/684253)는''threading.local''이 어떻게 작동하는지에 대한 좋은 설명과 예제를 제공합니다. –

4

재귀 오류가 발생하면 잡기보다는 런타임에 잡기가 쉽습니다.

옵션이 아닌 경우 렌더링 전에 템플릿을 분석하는 것도 좋은 방법 일 수 있습니다.

+2

코드가 스택 오버 플로우를 얻을 너무 오래 걸릴하지 않습니다 제공. 그렇다면 이것은 비실용적 일 수 있습니다. (+1) – NPE

+0

@NPE : 물론 매우 느린 과정 인 경우. 그러나 템플릿을 렌더링하면 안됩니다. –

+0

그건 내가 처음 시도한 것이지만, 여전히 이해할 수없는 몇 가지 이유로, 예외가 발생했을 때 예외가 발생하지 않습니다. {{tracd}}를 통해 trac를 실행할 때 이것을 자극하면 프로세스가 중지됩니다. 디버그 메시지가 없습니다. 예외 없음. 아무것도. –

0

마찬가지로 사용 된 인수를 추적하기 위해 사전을 전달하고 인수가 이미 시도되었는지 확인합니다. 재귀 예외를 잡기

+0

trac의 API를 사용하기 때문에 그렇게 간단하지 않습니다. 매크로를 추가하려면''IWikiMacroProvider''가 제공하는''expand_macro (self, formatter, name, content)''메소드를 오버라이드해야합니다. 이 메소드는 직접 호출하는 것이 아니라''Chrome (self.env) .render_template (...)' '과 같은 호출 내부에서 호출합니다. –

+0

나는 @ MartijnPieters의 답변이 당신이 제안한 것과 비슷하다고 생각합니다. –