2017-08-28 11 views
1

私はに探しています:ラップ操作(s)は、時間の所定の長さを超えた場合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()) 

私がここに持っている問題は、私はをもたらすわけではないので、ということである:これは私がこれまで持っているものです0将来、私が見スタックに:

$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を使用しない方法/どこ私は竜巻コール
  • を持っていると別の何かを行います????

    @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) 
    

    しかし、私は:私はこの例では、私はコルーチンにすべての呼び出し元のコードをラップすることができますし、このように、タイミングロガーラップの周りmain機能にタイムアウトを追加することを知っている

上記を私のContextDecoratorに含めると、timing_loggerを使用したいすべてのものをコピーしなければならないので、エラーが発生しやすくなるだけでなく、面倒な作業になります。

ContextDecoratorが機能する方法の一部としてタイムアウトを含めるためには、どのようにして希望の機能を実現できますか?

Python 3.6.1と最新のTornado(4.5.1)を使用しています。

答えて

0

むしろ竜巻のタイムアウトを使用するよりも、あなたが中断し(alarm only allows integer, second inputsが)この例外をトリガするためにsignalを使用することができます。

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__でアラームをリセットする必要があるか、それはあなたのコードの後半で発生します注意してください。

関連する問題