Асинхронная обработка исключений в Python
у меня есть следующий код, используя asyncio
и aiohttp
для выполнения асинхронных HTTP-запросов.
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)
по какой-то причине исключения, поднятые внутри
3 ответов
asyncio.wait
фактически не потребляет Futures
передано ему, он просто ждет их завершения, а затем возвращает Future
объекты:
coroutine
asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
дождитесь объектов Futures и coroutine задается последовательность фьючерсов для завершения. Coroutines будут обернуты в задачах. Возвращает два набора
Future
: (сделано, в ожидании).
пока вы на самом деле yield from
элементы в done
список, они останутся невостребованные. Поскольку ваша программа выходит, не потребляя фьючерсы, вы видите сообщения "исключение никогда не было получено".
для вашего случая использования, вероятно, имеет смысл использовать asyncio.gather
, который фактически будет потреблять каждый Future
, а затем верните один Future
что объединяет все их результаты (или вызывает "первый"!--12--> брошенный будущим во вход список.)
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
фактически позволяет настроить его поведение, когда один из фьючерсов вызывает исключение; поведение по умолчанию, чтобы поднять первое исключение он попадает, но он также может просто вернуть каждый объект исключения в списке вывода:
asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)
возвращает будущие результаты агрегирования из заданных объектов coroutine или будущее.
все фьючерсы должны иметь один и тот же цикл событий. Если все задачи выполнены успешно, результат возвращенного будущего-это список результатов (в порядок исходной последовательности, не обязательно порядок результаты заезда). если
return_exceptions
isTrue
исключения в задачи рассматриваются так же, как и успешные результаты, и собираются в список результатов; в противном случае первое вызванное исключение будет немедленно распространяется на возвращаемые будущее.
для отладки или" обработки " исключений в обратный звонок:
Coroutine, которые возвращают некоторый результат или вызывают исключения:
@asyncio.coroutine
def async_something_entry_point(self):
try:
return self.real_stuff_which_throw_exceptions()
except:
raise Exception(some_identifier_here + ' ' + traceback.format_exc())
и обратный вызов:
def callback(self, future: asyncio.Future):
exc = future.exception()
if exc:
# Handle wonderful empty TimeoutError exception
if type(exc) == TimeoutError:
self.logger('<Some id here> callback exception TimeoutError')
else:
self.logger("<Some id here> callback exception " + str(exc))
# store your result where you want
self.result.append(
future.result()
)
asyncio предоставляет API для индивидуальной обработки ошибок, пожалуйста, обратитесь к документу https://docs.python.org/3/library/asyncio-eventloop.html#error-handling-api