Корутины торнадо не работают в Ситоне
Этот код работает в Python 3.4.3 с использованием Tornado 4.1-он спит в течение 1 секунды, а затем печатает "Hello World! 123". Но при компиляции с использованием Cython (я пробовал версии 0.20. 1post0 и 0.23 dev), он ничего не делает.
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'
1 ответ:
Обновление: сопрограммы Cython поддерживаются изначально, начиная с Tornado 4.3. Описанный ниже способ решения проблемы применим только к старым версиям Tornado.
Корутины Торнадо в настоящее время не поддерживаются с помощью Cython. Основная проблема заключается в том, что генератор, скомпилированный Cython, не передает
isinstance(types.GeneratorType)
(и в прошлый раз, когда я смотрел, не было другого класса, который можно было бы использовать вместо него).Лучшим решением для этого было бы изменение в Cython, чтобы добавить общий базовый класс для генераторов, но в качестве быстрого hack i've had some success with this patch to
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)