Python 3.10 asyncio.wait_for TimeoutError 未捕获
在 Python 3.10 里,asyncio.wait_for() 超时抛出的是 asyncio.TimeoutError,它和内置的 TimeoutError 是两个不同的类。你写的 except TimeoutError: 捕获的是内置异常,所以 asyncio.wait_for() 抛出的 asyncio.TimeoutError 没被接住,调试器就把它当成未处理异常显示出来。直到 Python 3.11,asyncio.TimeoutError 才和内置 TimeoutError 统一成同一个类,这也是为什么有些示例在 3.11+ 上看起来直接就能跑。
改法是把捕获的类型写成 asyncio.TimeoutError,同时确认代码缩进没有问题:
import asyncio
async def eternity():
await asyncio.sleep(3600)
print('yay!')
async def main():
print('wait for at most 1 second...')
try:
await asyncio.wait_for(eternity(), timeout=1.0)
except asyncio.TimeoutError:
print('timeout!')
asyncio.run(main())
要同时兼容 3.10 和 3.11+,也可以写成 except (asyncio.TimeoutError, TimeoutError):,不过在 3.10 上真正需要捕获的是 asyncio.TimeoutError。你提到的 3.10 移除 loop 参数,和这个超时捕获的问题是两回事。
在 VS Code 或 PyCharm 里调试时如果还是看到 Exception has occurred: TimeoutError exception: no description,看一下是不是开了 Raised Exceptions,或者设置了在抛出异常时中断。调试器会在异常刚抛出的那一刻暂停,哪怕后面有 except 也一样,继续运行或者关掉这个断点就行。
asyncio.wait_for() 超时的时候会取消正在等待的任务。如果这个任务不能被取消,可以用 asyncio.shield() 包一层。不过 shield 保护的只是内层任务,外层 wait_for 到点照样会超时:
await asyncio.wait_for(asyncio.shield(eternity()), timeout=1.0)
还有,你贴出来的代码块缩进像是丢了。Python 对缩进敏感,函数体、try 块和 except 块都得写对,否则会先报语法或缩进错误。上面的示例已经把缩进补好了。
备注:内容仅供参考。
