龙卷风的 IOLoop.call_later 行为异常

Tornado's IOLoop.call_later is behaving inexplicably

来自口译员session:

>>>io_loop.call_later(comments_page_delay, functools.partial(http_client.fetch, "google.com", lambda x: print('kek')))
<tornado.ioloop._Timeout object at 0x7fe9a2427b08>
>>> io_loop.start()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/site-packages/tornado/ioloop.py", line 755, in start
    raise RuntimeError("IOLoop is already running")
RuntimeError: IOLoop is already running
>>> io_loop.call_later(comments_page_delay, functools.partial(http_client.fetch, "google.com", lambda x: print('kek')))
<tornado.ioloop._Timeout object at 0x7fe9a0267808>
>>> io_loop.stop()
>>> io_loop.start()
>>> io_loop.start()
kek
kek
kek

这里:

io_loop = tornado.ioloop.IOLoop.current()
comments_page_delay = 0.1

编辑:

AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
http_client = AsyncHTTPClient()
http_client.max_clients = max_clients
http_client.request_timeout = request_timeout

问题:

ioloop 似乎没有 运行 添加了 call_later 的调用。它是 运行ning,正如我尝试执行 io_loop.start() 时出现的错误所证明的那样。为了让它真正执行,我必须先停止循环,然后再启动它两次...非常混乱。

我承认我已经有一段时间没有使用 Tornado 了,但这基本上等同于我知道有效的其他代码。

编辑:代码在第一个 运行 上正常工作,但在连续的 运行 上不能正常工作。此时需要运行io_loop.start()两次returns

这是完整代码(略有删减):

"""...docstring..."""
import functools

import tornado
from tornado.httpclient import AsyncHTTPClient
from lxml import html as lh

from config import comments_page_delay, max_clients, request_timeout
from debug import log

AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
http_client = AsyncHTTPClient()
http_client.max_clients = max_clients
http_client.request_timeout = request_timeout

io_loop = tornado.ioloop.IOLoop.current()

def x(y, proxy):
    """...docstring..."""
    def handle_response(response):
        if response.error:
            log('...message...'
                .format(uid, response.error), 1)
        else:
            print('Worked')
            data = response.body

    comments_url = "...valid url..."

    io_loop.call_later(comments_page_delay, functools.partial(http_client.fetch, comments_url, handle_response,
                                                              proxy_host=proxy['host'], proxy_port=proxy['port']))

x(1, {'host': None, 'port': None})

io_loop.start()
print('ok')
io_loop.stop()

我在 Emacs python 解释器 session 中 运行 编写代码,如果这在某种程度上是相关的。我会在一秒钟内尝试正常 python session。

EDIT2:如果我 运行 在普通 python 解释器中编写该代码,会发生以下情况:

$ python -i fetch.py
Worked


Traceback (most recent call last):
  File "fetch.py", line 34, in <module>
    io_loop.start()
  File "/usr/lib/python3.6/site-packages/tornado/ioloop.py", line 863, in start
    event_pairs = self._impl.poll(poll_timeout)
KeyboardInterrupt
>>> >>> >>> get_user_profile_link(1, {'host': None, 'port': None})
>>> 
>>> 
>>> io_loop.start()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/site-packages/tornado/ioloop.py", line 755, in start
    raise RuntimeError("IOLoop is already running")
RuntimeError: IOLoop is already running
>>> io_loop.stop()
>>> io_loop.start()
>>> io_loop.start()
Worked

似乎 io_loop.start() 正在阻塞?我认为不是,这可能是问题所在。我现在必须重新考虑我整个程序的架构。

未捕获的异常(在本例中为 KeyboardInterrupt)使 IOLoop 处于未定义状态。在发生此类异常后不可能重新启动 IOLoop(因为它可能随时出现并使 IOLoop 的内部结构不一致)。一般来说,假定进程将在 KeyboardInterrupt 后简单地退出。可以创建一个新的 IOLoop 并从头开始,但是您必须小心地重新创建依赖于它的 all 对象。

不幸的是,这意味着实际上很难将 Tornado 与交互式解释器一起使用。我通常开发 Tornado 应用程序的工作流程是 运行 一个带有 python -m tornado.autoreload 的脚本,每当我编辑它时让脚本重新 运行 从头开始​​。