我正在爲Trac創建一個宏,它所做的一件事就是呈現一些wiki文本,它可以反過來使用同一個宏。檢測無限遞歸
如果使用相同的參數調用內部宏(即呈現相同位置的維基文本),這可以引起無限遞歸。我想通過檢查調用堆棧和中斷遞歸來阻止用戶像這樣拍攝自己的腳,如果擴展宏的函數已經用完全相同的一組參數調用。
我一直在尋找的inspect module,這無疑好像要走的路,但還是無法弄清楚如何發現堆棧中的前一個函數的參數值。我怎樣才能做到這一點?
我正在爲Trac創建一個宏,它所做的一件事就是呈現一些wiki文本,它可以反過來使用同一個宏。檢測無限遞歸
如果使用相同的參數調用內部宏(即呈現相同位置的維基文本),這可以引起無限遞歸。我想通過檢查調用堆棧和中斷遞歸來阻止用戶像這樣拍攝自己的腳,如果擴展宏的函數已經用完全相同的一組參數調用。
我一直在尋找的inspect module,這無疑好像要走的路,但還是無法弄清楚如何發現堆棧中的前一個函數的參數值。我怎樣才能做到這一點?
捕獲遞歸的例外是更好的方法,但你也可以在你想「保護」的功能添加裝飾:
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
當發生遞歸錯誤時,比在運行時發現遞歸錯誤更容易。
如果這不是一個選項,那麼在渲染之前分析模板也可能是一種前進的方法。
提供的代碼並不需要太長的時間來進行堆棧溢出。如果是這樣,這可能是不切實際的。 (+1) – NPE 2013-04-11 17:58:13
@NPE:當然,如果這是一個非常緩慢的過程。但是渲染一個模板不應該。 – 2013-04-11 17:58:49
這是我第一次嘗試,但由於某些原因,我仍然無法理解,發生這種情況時不會出現異常。當我通過{{tracd}}運行trac時會引發這個問題,這個過程就會停止。沒有調試信息。沒有例外。沒有。 – 2013-04-11 18:00:06
同樣簡單的是通過字典來跟蹤使用的參數,並在開始時檢查參數是否已經嘗試過。
並不是那麼簡單,因爲我使用的是trac的API。爲了添加宏,我需要覆蓋''IWikiMacroProvider''提供的方法''expand_macro(self,formatter,name,content)''。這不是直接調用自己的方法,而是在我調用Chrome瀏覽器(self.env).render_template(...)的時候的某個地方。 – 2013-04-11 18:04:04
我想@ MartijnPieters的答案接近你的建議。 – 2013-04-11 18:18:12
太好了,謝謝!對於未來的讀者,[本文](http://stackoverflow.com/a/1894371/684253)提供了一個很好的解釋和「threading.local」工作方式的例子。 – 2013-04-12 10:13:38