You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同时使用Sanic与Telepot时出现Timeout上下文管理器错误

问题:Sanic + Telepot.aio 启动时触发 RuntimeError 错误

我正在开发一个和Telegram通信的Web应用,尝试结合基于asyncio的Sanic框架和Telepot库,但启动时出现了异常。以下是我的代码:

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)
    # 此处出现错误
    await app.bot.sendMessage(
        "@test",
        "Wao! {}".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),)
    )

if __name__ == "__main__":
    app.run( debug=True )

错误信息:

[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

Telepot内部依赖aiohttp进行HTTP请求,用aiohttp.web实现类似功能时代码能正常运行,而且用同样方式连接的Redis、数据库等其他依赖都工作正常,请问该怎么解决这个问题?


解决方案

这个问题的核心原因是:Sanic默认使用uvloop作为事件循环,在before_server_start监听器里直接await Telepot的API调用时,这个协程是被loop.run_until_complete直接执行的,并没有被包装成一个正式的Task对象。而aiohttp的超时上下文管理器要求必须运行在Task内部,所以就触发了这个RuntimeError。

解决方法很简单,把发送消息的协程包装成一个Task再执行,具体修改你的server_init函数:

@app.listener('before_server_start')
async def server_init(app, loop):
    app.bot = telepot.aio.Bot('你的真实Telegram Bot Token', loop=loop)
    # 用loop.create_task把发送消息的操作包装成Task
    loop.create_task(
        app.bot.sendMessage(
            "@test",  # 注意:建议使用数字ID代替用户名,避免找不到聊天对象
            "Wao! {}".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        )
    )

为什么这样能解决问题?

loop.create_task()会把你的协程包装成一个Task对象,这个Task会被事件循环调度执行,此时aiohttp的超时上下文管理器就能识别到有效的Task上下文,不会再抛出错误。

另外补充两个注意点:

  • 请把代码里的'anything'替换成你从BotFather那里获取的真实有效的Bot Token,否则API调用还是会失败。
  • 发送消息的目标@test如果是用户名,可能存在无法找到的情况,建议改用聊天的数字ID(可以通过Telegram的一些Bot工具获取,比如@userinfobot),这样更可靠。

内容的提问来源于stack exchange,提问作者Mikhail Kashkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:53:05