Description
Long story short
As described in #877, attempting to use aiohttp.Timeout in a Tornado-based app results in a RuntimeError. In that issue, @bdarnell offers a solution that uses Tornado's own timeout utility. Through version 0.22.5, aiohttp's client request plumbing does not use aiohttp.Timeout internally; users were expected to apply it externally when desired.
The 1.0.0 release of aiohttp includes dc79e14, which forces all requests to use a Timeout. This seems a bit heavy-handed as it makes it impossible to use aiohttp's client with Tornado, even with Ben's suggested workaround. If the timeout kwarg to aiohttp.ClientSession::request is None, it should work the way it used to.
Steps to reproduce
The following test server can be used to consistently reproduce a RuntimeError as in #877 (trace follows):
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from asyncio import get_event_loop
from aiohttp import ClientSession
from tornado.httpserver import HTTPServer
from tornado.platform.asyncio import AsyncIOMainLoop
from tornado.web import Application
from tornado.web import RequestHandler
class Handler(RequestHandler):
async def get(self):
async with ClientSession() as client:
async with client.get('https://www.google.com') as response:
self.write(await response.text())
if __name__ == '__main__':
AsyncIOMainLoop().install()
app = Application([
(r'/', Handler),
])
server = HTTPServer(app)
server.listen(9999)
get_event_loop().run_forever()And the resulting error:
Uncaught exception GET / (127.0.0.1)
HTTPServerRequest(protocol='http', host='localhost:1337', method='GET', uri='/', version='HTTP/1.1', remote_ip='127.0.0.1', headers={'Host': 'localhost:1337', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'HTTPie/0.9.6', 'Accept': '*/*'})
Traceback (most recent call last):
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/tornado/web.py", line 1469, in _execute
result = yield result
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/tornado/gen.py", line 1015, in run
value = future.result()
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/tornado/concurrent.py", line 237, in result
raise_exc_info(self._exc_info)
File "<string>", line 3, in raise_exc_info
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/tornado/gen.py", line 285, in wrapper
yielded = next(result)
File "<string>", line 6, in _wrap_awaitable
File "./test.py", line 16, in get
async with client.get('https://www.google.com') as response:
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/aiohttp/client.py", line 565, in __aenter__
self._resp = yield from self._coro
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/aiohttp/client.py", line 197, in _request
with Timeout(timeout, loop=self._loop):
File "/Users/adam/.pyenv/versions/3.5.2/lib/python3.5/site-packages/async_timeout/__init__.py", line 33, in __enter__
raise RuntimeError('Timeout context manager should be used '
RuntimeError: Timeout context manager should be used inside a task
500 GET / (127.0.0.1) 3.57ms
Environment
- macOS 10.11.6
- Python 3.5.2
aiohttp1.0.0tornado4.4.1
Thoughts
aiohttp relies on @asvetlov's async_timeout library internally to handle timeouts. async_timeout is incompatible with coroutine runners that are not asyncio, as it relies on calling asyncio.Task.current_task. Perhaps it would be possible to make async_timeout compatible with other runners? Or maybe it's not possible. That's out of the scope of what I'm proposing here. I see the value of making it possible to manage the timeout internally, but it would be nice if there was a way to disable this behavior when desired.