【问题标题】:Closing asyncio event loop in Python causes exception at end在 Python 中关闭 asyncio 事件循环会导致异常结束
【发布时间】:2014-06-10 14:24:28
【问题描述】:

对于在 Windows 上的 Python 3.4 中使用 asyncio 和 aiohttp 的 https 请求,我需要使用 2 个事件循环。用于运行 shell 命令的 ProactorEventLoop,以及用于 HTTPS 请求的默认事件循环。不幸的是,ProactorEventLoop 不适用于 HTTPS 命令。

下面的代码显示了当我在 Windows 上使用新创建的默认事件循环并尝试在最后关闭它时会发生什么。如果我在最后调用loop.close,我会在最后得到异常,如下所示:

> Traceback (most recent call last):
>  File "C:\BuildUtilities\p3.4env0\lib\site-packages\aiohttp\connector.py", line 56, in __del__
>    self.close()
>  File "C:\BuildUtilities\p3.4env0\lib\site-packages\aiohttp\connector.py", line 97, in close
>    transport.close()
>  File "C:\Python34\Lib\asyncio\selector_events.py", line 375, in close
>    self._loop.remove_reader(self._sock_fd)
>  File "C:\Python34\Lib\asyncio\selector_events.py", line 155, in remove_reader
>    key = self._selector.get_key(fd)
> AttributeError: 'NoneType' object has no attribute 'get_key'

注释掉它会删除异常,我不知道为什么。唯一的一个

import asyncio
import aiohttp

@asyncio.coroutine
def get_body(url):
    response = yield from aiohttp.request('GET', url)
    return (yield from response.read_and_close())

#loop = asyncio.ProactorEventLoop()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

f = asyncio.async( get_body('https://www.google.com') )
try:
    loop.run_until_complete(f)
except Exception as e:
    print(e)

if f.result():
    print(f.result())

loop.close()

谢谢, 格林杰

【问题讨论】:

    标签: python-3.x python-asyncio aiohttp


    【解决方案1】:

    更新:问题似乎已在 github 版本 (0.7.2) 中得到修复。它不会产生错误。如@danj.py said,固定为"Get rid of __del__ in connector" commit


    它不是 ProactorEventLoop 或 Windows 特定的。我可以使用默认事件循环在 Ubuntu 上重现错误:

    #!/usr/bin/env python3
    import asyncio
    import aiohttp # $ pip install aiohttp
    
    @asyncio.coroutine
    def get_body(url):
        response = yield from aiohttp.request('GET', url)
        return (yield from response.read_and_close())
    
    loop = asyncio.get_event_loop()
    body = loop.run_until_complete(get_body('https://stackoverflow.com/q/23283502'))
    print(len(body), type(body), body[:200])
    loop.close()
    

    这可能是 aiohttp 中的一个错误,因为用法似乎正确。

    如果在没有aiohttp的情况下发出请求,则不会出错:

    #!/usr/bin/env python3
    import asyncio
    from contextlib import closing
    from urllib.parse import urlsplit
    
    @asyncio.coroutine
    def get_body(url):
        # parse url
        url = urlsplit(url)
        path = '/' * (not url.path) + url.path + '?' * bool(url.query) + url.query
        # open connection
        reader, writer = yield from asyncio.open_connection(
            host=url.hostname,
            port=url.port or (443 if url.scheme == 'https' else 80),
            ssl=(url.scheme == 'https'))
        with closing(writer):
            # send request
            writer.write(b'GET ' + path.encode('ascii') + b' HTTP/1.1\r\n'
                         b'Host: ' + url.netloc.encode('ascii') + b'\r\n'
                         b'Connection: close\r\n\r\n')
            # read headers
            while True:
                line = yield from reader.readline()
                line = line.rstrip(b'\n\r')
                print(line.decode('latin-1'))
                if not line:
                    break
            # read body
            body = yield from reader.read()
        return body
    
    loop = asyncio.get_event_loop()
    body = loop.run_until_complete(get_body('https://stackoverflow.com/q/23283502'))
    print(len(body), type(body), body[:200])
    loop.close()
    

    注意:示例不完全等效,例如,后者不遵循重定向。

    【讨论】:

    • 我已经验证关闭 SelectorEventLoop 在 Windows 7 64 位中不再崩溃。 pip install aiohttp --upgrade 解决了这个问题,将 aiohttp 从 0.7.0 升级到 0.7.2。谢谢。
    【解决方案2】:

    J.F. Sebastian 是正确的,这是 aiohttp 的一个错误。

    看起来这个问题已经通过以下提交解决了。

    https://github.com/KeepSafe/aiohttp/commit/a229110539e93c21ae0198f6f883ae3e40ea866b#diff-7f25afde79f309e2f8722c26cf1f10ad

    在撰写本文时,这似乎还没有进入 pypi。

    【讨论】:

    • 你是对的。我无法重现以a229110 commit 开头的错误。
    【解决方案3】:

    实际上这是 Python 3.4 本身的一个(已修复)错误:http://bugs.python.org/issue21435

    作为临时解决方案(为了 Python 3.3 支持)我提出了拉取请求 https://github.com/KeepSafe/aiohttp/pull/48

    但是,再说一遍,问题的根源是 Python 3.4.0 中的垃圾收集器(由即将发布的 Python 3.4.1 错误修复版本修复)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多