Python 中的异步异常处理 [英] Asynchronous exception handling in Python
问题描述
我有以下代码使用 asyncio
和 aiohttp
来发出异步 HTTP 请求.
I've the following code using asyncio
and aiohttp
to make asynchronous HTTP requests.
import sys
import asyncio
import aiohttp
@asyncio.coroutine
def get(url):
try:
print('GET %s' % url)
resp = yield from aiohttp.request('GET', url)
except Exception as e:
raise Exception("%s has error '%s'" % (url, e))
else:
if resp.status >= 400:
raise Exception("%s has error '%s: %s'" % (url, resp.status, resp.reason))
return (yield from resp.text())
@asyncio.coroutine
def fill_data(run):
url = 'http://www.google.com/%s' % run['name']
run['data'] = yield from get(url)
def get_runs():
runs = [ {'name': 'one'}, {'name': 'two'} ]
loop = asyncio.get_event_loop()
task = asyncio.wait([fill_data(r) for r in runs])
loop.run_until_complete(task)
return runs
try:
get_runs()
except Exception as e:
print(repr(e))
sys.exit(1)
由于某种原因,在 get
函数中引发的异常没有被捕获:
For some reason, exceptions raised inside the get
function are not caught:
Future/Task exception was never retrieved
Traceback (most recent call last):
File "site-packages/asyncio/tasks.py", line 236, in _step
result = coro.send(value)
File "mwe.py", line 25, in fill_data
run['data'] = yield from get(url)
File "mwe.py", line 17, in get
raise Exception("%s has error '%s: %s'" % (url, resp.status, resp.reason))
Exception: http://www.google.com/two has error '404: Not Found'
那么,处理协程引发的异常的正确方法是什么?
So, what is correct way to handle exceptions raised by coroutines?
推荐答案
asyncio.wait
实际上并不消耗传递给它的 Futures
,它只是等待它们完成,然后返回 Future
对象:
asyncio.wait
doesn't actually consume the Futures
passed to it, it just waits for them to complete, and then returns the Future
objects:
coroutine asyncio.wait(futures, *, loop=None, timeout=None,return_when=ALL_COMPLETED)
等待 Futures 和协程对象由给定的序列期货来完成.协程将被包装在任务中.返回两组Future
:(完成,待定).
Wait for the Futures and coroutine objects
given by the sequence futures to complete. Coroutines will be wrapped
in Tasks. Returns two sets of Future
: (done, pending).
在您实际yield from
done
列表中的项目之前,它们将保持未消耗状态.由于您的程序在不消耗期货的情况下退出,您会看到从未检索到异常"消息.
Until you actually yield from
the items in the done
list, they'll remain unconsumed. Since your program exits without consuming the futures, you see the "exception was never retrieved" messages.
对于您的用例,使用 asyncio.gather
,它实际上会消耗每个 Future
,然后返回一个聚合所有结果的 Future
(或引发输入列表中未来抛出的第一个 Exception
).
For your use-case, it probably makes more sense to use asyncio.gather
, which will actually consume each Future
, and then return a single Future
that aggregates all their results (or raises the first Exception
thrown by a future in the input list).
def get_runs():
runs = [ {'name': 'one'}, {'name': 'two'} ]
loop = asyncio.get_event_loop()
tasks = asyncio.gather(*[fill_data(r) for r in runs])
loop.run_until_complete(tasks)
return runs
输出:
GET http://www.google.com/two
GET http://www.google.com/one
Exception("http://www.google.com/one has error '404: Not Found'",)
请注意,asyncio.gather
实际上允许您在其中一个期货引发异常时自定义其行为;默认行为是引发它遇到的第一个异常,但它也可以只返回输出列表中的每个异常对象:
Note that asyncio.gather
actually lets you customize its behavior when one of the futures raises an exception; the default behavior is to raise the first exception it hits, but it can also just return each exception object in the output list:
asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)
从给定的协程对象返回一个未来的聚合结果或期货.
Return a future aggregating results from the given coroutine objects or futures.
所有期货必须共享相同的事件循环.如果所有的任务都完成了成功,返回的未来结果是结果列表(在原始序列的顺序,不一定是结果到达).如果 return_exceptions
为 True
,则异常任务被视为成功的结果,并聚集在结果清单;否则,第一个引发的异常将立即传播到返回的未来.
All futures must share the same event loop. If all the tasks are done
successfully, the returned future’s result is the list of results (in
the order of the original sequence, not necessarily the order of
results arrival). If return_exceptions
is True
, exceptions in the
tasks are treated the same as successful results, and gathered in the
result list; otherwise, the first raised exception will be immediately
propagated to the returned future.
这篇关于Python 中的异步异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!