我期待:如何在ContextDecorator中引發Tornado超時異常?
- 製作自定義計時器包裝,用於記錄/其他定時目的
- 包括搶先離開如果包裝操作(多個)超過預定的時間長度
這是我到目前爲止有:
from contextlib import ContextDecorator
import datetime
from tornado import gen, ioloop
from tornado.concurrent import Future
class timing_logger(ContextDecorator):
def __init__(self, allowed_ms):
self.allowed_ms = allowed_ms
self.f = Future()
# this exception is not bubbled up by Tornado but fires
gen.with_timeout(datetime.timedelta(seconds=1+allowed_ms/1000), self.f)
def __enter__(self):
self.start_time = datetime.datetime.now()
return self
def __exit__(self, exc_type, exc_val, traceback):
self.f.set_result(True)
elapsed_time_ms = (datetime.datetime.now() - self.start_time).total_seconds() * 1000
if exc_type == gen.TimeoutError:
raise TimeoutError('ruh oh, this is reeeally bad')
if elapsed_time_ms > self.allowed_ms:
raise TimeoutError('took {actual} ms, but was only allowed {allowed}.'.format(
actual=elapsed_time_ms, allowed=self.allowed_ms))
else:
print('worked. nothing to see here...')
return False
@gen.coroutine
def main():
with timing_logger(1000):
# real application may be a variety of coroutines
# and regular function calls, some of which may hang
# for a long time
for i in range(25):
yield gen.sleep(0.1)
if __name__ == "__main__":
ioloop.IOLoop.current().run_sync(
lambda: main())
我這裏的問題是,因爲我不屈服的gen.with_timeout
今後,在堆棧中我看到:
$python test.py
ERROR:tornado.application:Future <tornado.concurrent.Future object at 0x10c7cb668> exception was never retrieved: tornado.gen.TimeoutError: Timeout
Traceback (most recent call last):
File "test.py", line 48, in <module>
lambda: main())
<snip>
yielded = self.gen.send(value)
File "test.py", line 43, in main
yield gen.sleep(0.1)
File "test.py", line 28, in __exit__
actual=elapsed_time_ms, allowed=self.allowed_ms))
TimeoutError: took 2606.2940000000003 ms, but was only allowed 1000.
龍捲風超時不被「冒泡」(因爲缺乏更好的詞)。
我想__exit__
捕獲異常,以便我可以處理它並適當地記錄,同時重新提升爲另一種異常類型。
我不知道如果我需要:
- 不使用ContextDecorator在所有
- 做一些不同的如何/在那裏我有龍捲風呼叫
- ????
我知道,在這個例子中,我可以換全部調用的代碼放到一個協程和超時添加到main
功能周圍的時間記錄器包裝,像這樣:
@gen.coroutine
def main():
@gen.coroutine
def f():
with timing_logger(1000):
# real application may be a variety of coroutines
# and regular function calls, some of which may hang
# for a long time
for i in range(25):
yield gen.sleep(0.1)
future = f()
yield gen.with_timeout(datetime.timedelta(seconds=1), future)
但我希望將上述內容加入到我的ContextDecorator中,因爲不得不在我想使用timing_logger
時複製這些內容變得單調乏味並且容易出錯。
我該如何實現所需的功能,以允許ContextDecorator將超時作爲其功能的一部分?
使用Python 3.6.1和最新的Tornado(4.5.1)。