Python 具有一个超时的两个异步操作
基本上我想要:Python 具有一个超时的两个异步操作,python,python-asyncio,Python,Python Asyncio,基本上我想要: await action1() await action2() return result 这两个操作都有一个超时,而且-这很重要-有一条错误消息告诉您哪个操作超时 作为比较,只需一个操作: try: await asyncio.wait_for(action(), timeout=1.0) except asyncio.TimeoutError: raise RuntimeError("Problem") 现在我有两个动作,但我不喜欢 import asyn
await action1()
await action2()
return result
这两个操作都有一个超时,而且-这很重要-有一条错误消息告诉您哪个操作超时
作为比较,只需一个操作:
try:
await asyncio.wait_for(action(), timeout=1.0)
except asyncio.TimeoutError:
raise RuntimeError("Problem")
现在我有两个动作,但我不喜欢
import asyncio
async def a2():
try:
await asyncio.sleep(1.0)
except asyncio.CancelledError:
raise RuntimeError("Problem 1") from None
try:
await asyncio.sleep(1.0)
except asyncio.CancelledError:
raise RuntimeError("Problem 2") from None
return True
async def test():
loop = asyncio.get_event_loop()
action_task = loop.create_task(a2())
# timeouts: 0.5 -> Problem1 exc; 1.5 -> Problem2 exc; 2.5 -> OK
try:
await asyncio.wait_for(action_task, timeout=0.5)
except asyncio.TimeoutError:
pass
result = await action_task
asyncio.get_event_loop().run_until_complete(test())
我发现有以下几点确实违反直觉:
except asyncio.TimeoutError:
pass
其中超时处理是主要功能。你能建议一个更好的方法吗
你能建议一个更好的方法吗
您的代码是正确的,但如果您正在寻找更优雅的东西,也许上下文管理器适合您的使用:
class Timeout:
def __init__(self, tmout):
self.tmout = tmout
self._timed_out = False
self._section = None
async def __aenter__(self):
loop = asyncio.get_event_loop()
self._timer = loop.call_later(self.tmout, self._cancel_task,
asyncio.current_task())
return self
def set_section(self, section):
self._section = section
def _cancel_task(self, task):
self._timed_out = True
task.cancel()
async def __aexit__(self, t, v, tb):
if self._timed_out:
assert t is asyncio.CancelledError
raise RuntimeError(self._section) from None
else:
self._timer.cancel()
人们将按如下方式使用它:
async def main():
# raises RuntimeError("second sleep")
async with Timeout(1) as tmout:
tmout.set_section("first sleep")
# increase above 1.0 and "first sleep" is raised
await asyncio.sleep(0.8)
tmout.set_section("second sleep")
await asyncio.sleep(0.5)
asyncio.get_event_loop().run_until_complete(main())
最初为aiohttp
开发的模块可能正是您所需要的。它的回溯包含导致超时的行
安装:
import asyncio
from async_timeout import timeout
async def main():
with timeout(1.5) as t:
await asyncio.sleep(1) # first
await asyncio.sleep(1) # second
await asyncio.sleep(1) # third
print('not timeout')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Traceback (most recent call last):
File "C:\Users\gmn\main.py", line 74, in main
await asyncio.sleep(1) # second
File "C:\Users\gmn\AppData\Local\Programs\Python\Python37\lib\asyncio\tasks.py", line 564, in sleep
return await future
concurrent.futures._base.CancelledError
During handling of the above exception, another exception occurred:
...
pip安装异步\u超时
用法:
import asyncio
from async_timeout import timeout
async def main():
with timeout(1.5) as t:
await asyncio.sleep(1) # first
await asyncio.sleep(1) # second
await asyncio.sleep(1) # third
print('not timeout')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Traceback (most recent call last):
File "C:\Users\gmn\main.py", line 74, in main
await asyncio.sleep(1) # second
File "C:\Users\gmn\AppData\Local\Programs\Python\Python37\lib\asyncio\tasks.py", line 564, in sleep
return await future
concurrent.futures._base.CancelledError
During handling of the above exception, another exception occurred:
...
输出:
import asyncio
from async_timeout import timeout
async def main():
with timeout(1.5) as t:
await asyncio.sleep(1) # first
await asyncio.sleep(1) # second
await asyncio.sleep(1) # third
print('not timeout')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Traceback (most recent call last):
File "C:\Users\gmn\main.py", line 74, in main
await asyncio.sleep(1) # second
File "C:\Users\gmn\AppData\Local\Programs\Python\Python37\lib\asyncio\tasks.py", line 564, in sleep
return await future
concurrent.futures._base.CancelledError
During handling of the above exception, another exception occurred:
...
这里的第二行和第三行告诉您超时发生的位置:
File "C:\Users\gmn\main.py", line 74, in main
await asyncio.sleep(1) # second
可能重复@bobah这不是重复,我想先运行操作1,然后再运行操作2。公平点,我已经收回了关闭voteI。在过去几周里,我从您那里得到了一些有用的asyncio答案。谢谢大家!@VPfB,感谢您提出有趣的问题
async_timeout
是一个不错的模块,但它没有解决OP的问题。问题清楚地指出,“两个操作都有一个超时,这一点很重要,并带有一条错误消息,告知哪个操作超时。”async\u timeout
引发完全相同的错误,而不管哪个操作超时。@user4815162342不,错误不是“完全相同的”。错误具有相同的类型和消息,但不同的回溯“告诉哪个操作超时”。它允许清楚地识别超时发生的位置。如果我理解正确,它完全解决了OP的问题。OP指定他们需要一条“错误消息,告诉哪个操作超时”。一般来说,在Python中,特别是在OP的代码中,错误消息来自exception对象,不必从回溯中猜测。回溯是一个很好的调试功能,但是当行恰好相同时,或者当程序没有附带.py
源时,回溯就没有帮助了。另外,要提取实际的消息,必须解析回溯。@user4815162342好吧,我想我的答案的实际情况取决于OP到底需要什么:控制台中的用户消息或开发人员的调试信息(我稍后假设)。我还想指出的是,如果行恰好相同,回溯仍然会有帮助:它包含行号。作为OP,首先我要感谢您提供了有关async_timeout库的信息。我学到了一些新东西。关于我的需求,我更喜欢一个清晰易读的日志信息,这是我可以自己表述的。这就是为什么我认为这个库不是适合我编程的工具。