telepot
telepot copied to clipboard
Timeout context manager error using sanic and telepot same time
I'm trying to create a web app that communicates with Telegram. And trying to use Sanic web framework with Telepot. Both are asyncio based. Now I'm getting a very weird error.
This is my code:
import datetime
import telepot.aio
from sanic import Sanic
app = Sanic(__name__, load_env=False)
app.config.LOGO = ''
@app.listener('before_server_start')
async def server_init(app, loop):
app.bot = telepot.aio.Bot('anything', loop=loop)
# here we fall
await app.bot.sendMessage(
"@test",
"Wao! {}".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),)
)
if __name__ == "__main__":
app.run(
debug=True
)
The error that I'm getting is:
[2018-01-18 22:41:43 +0200] [10996] [ERROR] Experienced exception while trying to serve
Traceback (most recent call last):
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/sanic/app.py", line 646, in run
serve(**server_settings)
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/sanic/server.py", line 588, in serve
trigger_events(before_start, loop)
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/sanic/server.py", line 496, in trigger_events
loop.run_until_complete(result)
File "uvloop/loop.pyx", line 1364, in uvloop.loop.Loop.run_until_complete
File "/home/mk/Dev/project/sanic-telepot.py", line 14, in server_init
"Wao! {}".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 109, in __next__
return self.gen.send(None)
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/telepot/aio/__init__.py", line 100, in sendMessage
return await self._api_request('sendMessage', _rectify(p))
File "/usr/lib/python3.6/asyncio/coroutines.py", line 109, in __next__
return self.gen.send(None)
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/telepot/aio/__init__.py", line 78, in _api_request
return await api.request((self._token, method, params, files), **kwargs)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 109, in __next__
return self.gen.send(None)
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/telepot/aio/api.py", line 139, in request
async with fn(*args, **kwargs) as r:
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/aiohttp/client.py", line 690, in __aenter__
self._resp = yield from self._coro
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/aiohttp/client.py", line 221, in _request
with timer:
File "/home/mk/Dev/project/venv/lib/python3.6/site-packages/aiohttp/helpers.py", line 712, in __enter__
raise RuntimeError('Timeout context manager should be used '
RuntimeError: Timeout context manager should be used inside a task
I'm not sure that it is the issue with the telepot. But probably you have an idea how to deal with it.
As a temporary fix I made this workaround:
import asyncio
import aiohttp
import async_timeout
import telepot.aio
from telepot.aio.api import (
_proxy, _parse, _compose_timeout, _compose_data, _methodurl
)
from telepot.exception import TelegramError
class Bot(telepot.aio.Bot):
async def request(self, req, **user_kw):
fn, args, kwargs, timeout, cleanup = self._transform(req, **user_kw)
if _proxy:
kwargs['proxy'] = _proxy[0]
if len(_proxy) > 1:
kwargs['proxy_auth'] = aiohttp.BasicAuth(*_proxy[1])
try:
if timeout is None:
async with fn(*args, **kwargs) as r:
return await _parse(r)
else:
try:
with async_timeout.timeout(timeout):
async with fn(*args, **kwargs) as r:
return await _parse(r)
except asyncio.TimeoutError:
raise TelegramError('Response timeout', 504, {})
except aiohttp.ClientConnectionError:
raise TelegramError('Connection Error', 400, {})
finally:
if cleanup:
cleanup() # e.g. closing one-time session
async def _api_request(self, method, params=None, files=None, **kwargs):
return await self.request((self._token, method, params, files), **kwargs)
def _transform(self, req, **user_kw):
timeout = _compose_timeout(req, **user_kw)
data = _compose_data(req, **user_kw)
url = _methodurl(req, **user_kw)
session = aiohttp.ClientSession(
connector=aiohttp.TCPConnector(limit=1, force_close=True),
loop=self.loop)
cleanup = session.close
kwargs = {'data': data}
kwargs.update(user_kw)
return session.post, (url,), kwargs, timeout, cleanup
Now Bot doesn't lose loop. I'm not sure that it should be fixed in the code of the telepot, but if you want to make it working inside other asyncio frameworks I may have a reason to make loop linked to the calls made by the aiohttp.
@xen Could you share a diff?
$ git diff > issue359
And then drop the file into the comment area or edit the issue.
File attach didn't work well. So I made the pull request #362. I don't know how to attach it to this issue.