私はグリーンレットでツイストを使用しようとしているので、inlineCallbacks を使用せずにツイストで同期のように見えるコードを記述できます。
これが私のコードです:
import time, functools
from twisted.internet import reactor, threads
from twisted.internet.defer import Deferred
from functools import wraps
import greenlet
def make_async(func):
@wraps(func)
def wrapper(*pos, **kwds):
d = Deferred()
def greenlet_func():
try:
rc = func(*pos, **kwds)
d.callback(rc)
except Exception, ex:
print ex
d.errback(ex)
g = greenlet.greenlet(greenlet_func)
g.switch()
return d
return wrapper
def sleep(t):
print "sleep(): greenelet:", greenlet.getcurrent()
g = greenlet.getcurrent()
reactor.callLater(t, g.switch)
g.parent.switch()
def wait_one(d):
print "wait_one(): greenelet:", greenlet.getcurrent()
g = greenlet.getcurrent()
active = True
def callback(result):
if not active:
g.switch(result)
else:
reactor.callLater(0, g.switch, result)
def errback(failure):
if not active:
g.throw(failure)
else:
reactor.callLater(0, g.throw, failure)
d.addCallback(callback)
d.addErrback(errback)
active = False
rc = g.parent.switch()
return rc
@make_async
def inner():
print "inner(): greenelet:", greenlet.getcurrent()
import random, time
interval = random.random()
print "Sleeping for %s seconds..." % interval
sleep(interval)
print "done"
return interval
@make_async
def outer():
print "outer(): greenelet:", greenlet.getcurrent()
print wait_one(inner())
print "Here"
reactor.callLater(0, outer)
reactor.run()
5 つの主要部分があります。
- タイマーを開始し、親 greenlet に戻るスリープ関数。タイマーが切れると、スリープ中の greenlet に戻ります。
- make_async デコレーター。これは、いくつかの同期コードを取得し、greenlet で実行します。IT は deferred も返すため、コードの完了時に呼び出し元がコールバックを登録できます。
- 待機中の遅延が解決されるまで greenlet をブロックする wait_one 関数。
- (ラップされたときに) deferred を返す内部関数は、ランダムな時間スリープし、スリープした時間を deferred に渡します。
- inner() を呼び出す外部関数は、それが戻るのを待ってから、戻り値を出力します。
このコードを実行すると、次の出力が得られます (最後の 2 行のエラーに注意してください)。
outer(): greenelet: <greenlet.greenlet object at 0xb729cc5c>
inner(): greenelet: <greenlet.greenlet object at 0xb729ce3c>
Sleeping for 0.545666723422 seconds...
sleep(): greenelet: <greenlet.greenlet object at 0xb729ce3c>
wait_one(): greenelet: <greenlet.greenlet object at 0xb729cc5c>
done
0.545666723422
Here
Exception twisted.python.failure.Failure: <twisted.python.failure.Failure <class 'greenlet.GreenletExit'>> in <greenlet.greenlet object at 0xb729ce3c> ignored
GreenletExit did not kill <greenlet.greenlet object at 0xb729ce3c>
少し調べてみると、次のことがわかりました。
- 最後の行は greenlet.c によって記録されます
- 前の行は、 delメソッドで発生した例外を無視しているため、python 自体によってログに記録されます。
GreenletExit
またはtwisted.python.failure.Failure
例外にアクセスしてスタックトレースを取得できないため、これをデバッグするのに本当に問題があります。
私が間違っていること、またはスローされている例外をデバッグする方法を知っている人はいますか?
もう1つのデータポイント:wait_one()をハックしてすぐに返すと(そして渡された遅延に何も登録しないように)、エラーはなくなります。:-/