Закрытие цикла событий asyncio в Python вызывает исключение в конце

Для запросов https с использованием asyncio и aiohttp в Python 3.4 в Windows мне нужно использовать 2 цикла событий. 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()

Спасибо, гриндж

3 ответа

Решение

Обновление: похоже, проблема исправлена ​​в версии github (0.7.2). Это не производит ошибку. Как сказал @danj.py, это исправлено "Избавиться от __del__ в разъем "коммит.


Это не 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://stackru.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://stackru.com/q/23283502'))
print(len(body), type(body), body[:200])
loop.close()

Примечание: примеры не полностью эквивалентны, например, последний не следует перенаправлениям.

JF Себастьян прав, что это ошибка в aiohttp.

Похоже, что эта проблема была решена с помощью следующего коммита.

https://github.com/KeepSafe/aiohttp/commit/a229110539e93c21ae0198f6f883ae3e40ea866b

На момент написания статьи не похоже, чтобы это превратилось в pypi.

На самом деле это исправленная ошибка в самом 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).

Другие вопросы по тегам