Закрытие цикла событий 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).