Skip to content
This repository has been archived by the owner on Jun 2, 2019. It is now read-only.

Timeout context manager error using sanic and telepot same time #359

Open
xen opened this issue Jan 19, 2018 · 3 comments
Open

Timeout context manager error using sanic and telepot same time #359

xen opened this issue Jan 19, 2018 · 3 comments

Comments

@xen
Copy link

xen commented Jan 19, 2018

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.

@xen
Copy link
Author

xen commented Jan 20, 2018

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.

@das7pad
Copy link
Contributor

das7pad commented Jan 20, 2018

@xen Could you share a diff?

$ git diff > issue359

And then drop the file into the comment area or edit the issue.

@xen
Copy link
Author

xen commented Jan 21, 2018

File attach didn't work well. So I made the pull request #362. I don't know how to attach it to this issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants