For https requests using asyncio and aiohttp in Python 3.4 on Windows I'll need to use 2 event loops. A ProactorEventLoop for running shell commands, and the default event loop for HTTPS requests. The ProactorEventLoop does not work for HTTPS commands, unfortunately.
The following code below shows what happens when I use a newly created default event loop and try to close it at the end on Windows. I get exceptions at the end if I call loop.close
at the end as shown below:
> 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'
Commenting it out removes the exception and I don't know why. The one and only
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()
Thanks, greenaj
Update: it looks like the issue is fixed in the github version (0.7.2). It doesn't produce the error. As @danj.py said, it is fixed by "Get rid of __del__
in connector" commit.
It is not ProactorEventLoop or Windows specific. I can reproduce the error on Ubuntu with the default event loop:
#!/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()
It might be a bug in aiohttp because the usage seems correct.
There is no error if the request is made without 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()
Note: the examples are not completely equivalent e.g., the latter doesn't follow redirects.