如何在 ContextDecorator 中引发 Tornado 超时异常?
How can I raise a Tornado timeout exception within a ContextDecorator?
我正在寻找:
- 制作一个自定义计时器包装器,用于logging/other计时目的
- 如果包装操作超过预定义的时间长度,则包括抢先退出
这是我目前所拥有的:
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.
龙卷风超时 而不是 "bubbled"(因为缺少更好的词)。
我想让 __exit__
捕获异常,以便我可以处理它并适当地登录,同时重新引发为不同的异常类型。
我不确定是否需要:
- 根本不使用 ContextDecorator
- 做一些不同的事情 how/where 我有龙卷风电话
- ????
我知道在这个例子中,我可以将所有调用代码包装到协程中,并在计时记录器包装周围的 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)。
您可以使用 signal to interrupt and trigger this exception (though alarm only allows integer, second inputs):
而不是使用龙卷风超时
def timeout_handler(signum, frame):
raise gen.TimeoutError()
self.signal = signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(1 + self.allowed_ms // 1000)
这将适当地引发异常,从而产生一个完整的 ContextDecorator,如下所示:
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()
def timeout_handler(signum, frame):
raise gen.TimeoutError() # could be any type of exception
self.signal = signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(1 + self.allowed_ms // 1000)
return self
def __exit__(self, exc_type, exc_val, traceback):
signal.alarm(0)
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
请注意,您需要在 __exit__
中重置警报,否则稍后将在您的代码中触发。
我正在寻找:
- 制作一个自定义计时器包装器,用于logging/other计时目的
- 如果包装操作超过预定义的时间长度,则包括抢先退出
这是我目前所拥有的:
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.
龙卷风超时 而不是 "bubbled"(因为缺少更好的词)。
我想让 __exit__
捕获异常,以便我可以处理它并适当地登录,同时重新引发为不同的异常类型。
我不确定是否需要:
- 根本不使用 ContextDecorator
- 做一些不同的事情 how/where 我有龙卷风电话
- ????
我知道在这个例子中,我可以将所有调用代码包装到协程中,并在计时记录器包装周围的 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)。
您可以使用 signal to interrupt and trigger this exception (though alarm only allows integer, second inputs):
而不是使用龙卷风超时 def timeout_handler(signum, frame):
raise gen.TimeoutError()
self.signal = signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(1 + self.allowed_ms // 1000)
这将适当地引发异常,从而产生一个完整的 ContextDecorator,如下所示:
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()
def timeout_handler(signum, frame):
raise gen.TimeoutError() # could be any type of exception
self.signal = signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(1 + self.allowed_ms // 1000)
return self
def __exit__(self, exc_type, exc_val, traceback):
signal.alarm(0)
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
请注意,您需要在 __exit__
中重置警报,否则稍后将在您的代码中触发。