Tornado 协程在 Cython 中不起作用
Tornado coroutines don’t work in Cython
此代码在使用 Tornado 4.1 的 Python 3.4.3 中运行 — 它休眠 1 秒然后打印 "Hello World! 123"。但是当使用 Cython 编译时(我试过版本 0.20.1post0 和 0.23dev),它什么都不做。
import tornado.ioloop
import datetime
from tornado import gen
@gen.coroutine
def test():
yield gen.Task(ioloop.add_timeout, datetime.timedelta(seconds=1))
return 123
@gen.coroutine
def hello_world():
print('Hello World! {}'.format((yield test())))
ioloop = tornado.ioloop.IOLoop().instance()
ioloop.run_sync(hello_world)
我用来构建和 运行 Cython 版本的命令:
cython --embed -o hello.c hello.py
gcc -shared -fPIC -O0 -Wall -I/usr/include/python3.4 -o hello.so hello.c
python -c 'import hello'
更新:从 Tornado 4.3 开始原生支持 Cython 协程。以下解决方法仅适用于旧版本的 Tornado。
Cython 目前不支持 Tornado 协程。主要问题是 Cython 编译的生成器没有通过 isinstance(types.GeneratorType)
(上次我看没有其他 class 可以代替)。
解决此问题的最佳方法是更改 Cython,为生成器添加一个通用基础 class,但作为快速破解,我已经通过此 tornado/gen.py
补丁取得了一些成功:
diff --git a/tornado/gen.py b/tornado/gen.py
index aa931b4..b348f21 100644
--- a/tornado/gen.py
+++ b/tornado/gen.py
@@ -91,6 +91,12 @@ from tornado.concurrent import Future, TracebackFuture
from tornado.ioloop import IOLoop
from tornado.stack_context import ExceptionStackContext, wrap
+def _is_generator(obj):
+ # cython generates a new generator type for each module without a
+ # common base class :(
+ return (isinstance(obj, types.GeneratorType) or
+ str(type(obj)) == "<type 'generator'>")
+
class KeyReuseError(Exception):
pass
@@ -147,7 +153,7 @@ def engine(func):
except (Return, StopIteration) as e:
result = getattr(e, 'value', None)
else:
- if isinstance(result, types.GeneratorType):
+ if _is_generator(result):
def final_callback(value):
if value is not None:
raise ReturnValueIgnoredError(
@@ -219,7 +225,7 @@ def coroutine(func):
future.set_exc_info(sys.exc_info())
return future
else:
- if isinstance(result, types.GeneratorType):
+ if _is_generator(result):
def final_callback(value):
deactivate()
future.set_result(value)
此代码在使用 Tornado 4.1 的 Python 3.4.3 中运行 — 它休眠 1 秒然后打印 "Hello World! 123"。但是当使用 Cython 编译时(我试过版本 0.20.1post0 和 0.23dev),它什么都不做。
import tornado.ioloop
import datetime
from tornado import gen
@gen.coroutine
def test():
yield gen.Task(ioloop.add_timeout, datetime.timedelta(seconds=1))
return 123
@gen.coroutine
def hello_world():
print('Hello World! {}'.format((yield test())))
ioloop = tornado.ioloop.IOLoop().instance()
ioloop.run_sync(hello_world)
我用来构建和 运行 Cython 版本的命令:
cython --embed -o hello.c hello.py
gcc -shared -fPIC -O0 -Wall -I/usr/include/python3.4 -o hello.so hello.c
python -c 'import hello'
更新:从 Tornado 4.3 开始原生支持 Cython 协程。以下解决方法仅适用于旧版本的 Tornado。
Cython 目前不支持 Tornado 协程。主要问题是 Cython 编译的生成器没有通过 isinstance(types.GeneratorType)
(上次我看没有其他 class 可以代替)。
解决此问题的最佳方法是更改 Cython,为生成器添加一个通用基础 class,但作为快速破解,我已经通过此 tornado/gen.py
补丁取得了一些成功:
diff --git a/tornado/gen.py b/tornado/gen.py
index aa931b4..b348f21 100644
--- a/tornado/gen.py
+++ b/tornado/gen.py
@@ -91,6 +91,12 @@ from tornado.concurrent import Future, TracebackFuture
from tornado.ioloop import IOLoop
from tornado.stack_context import ExceptionStackContext, wrap
+def _is_generator(obj):
+ # cython generates a new generator type for each module without a
+ # common base class :(
+ return (isinstance(obj, types.GeneratorType) or
+ str(type(obj)) == "<type 'generator'>")
+
class KeyReuseError(Exception):
pass
@@ -147,7 +153,7 @@ def engine(func):
except (Return, StopIteration) as e:
result = getattr(e, 'value', None)
else:
- if isinstance(result, types.GeneratorType):
+ if _is_generator(result):
def final_callback(value):
if value is not None:
raise ReturnValueIgnoredError(
@@ -219,7 +225,7 @@ def coroutine(func):
future.set_exc_info(sys.exc_info())
return future
else:
- if isinstance(result, types.GeneratorType):
+ if _is_generator(result):
def final_callback(value):
deactivate()
future.set_result(value)