Python 中的异步异常处理 [英] Asynchronous exception handling in Python

查看:146
本文介绍了Python 中的异步异常处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码使用 asyncioaiohttp 来发出异步 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_exceptionsTrue,则异常任务被视为成功的结果,并聚集在结果清单;否则,第一个引发的异常将立即传播到返回的未来.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆