Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous exception handling in Python

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) 

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?

like image 311
Yury Bayda Avatar asked May 20 '15 23:05

Yury Bayda


People also ask

What are asynchronous exceptions?

An exception is described as asynchronous if either of the following applies: — the exception is not generated as a result of direct execution or attempted execution of the instruction. stream. — the return address presented to the exception handler is not guaranteed to indicate the instruction that. caused the ...

Are exceptions handled asynchronously?

Learn the exception handling semantics for asynchronous methods in C# Exception handling is the technique of handling runtime errors in an application. Asynchronous programming allows us to perform resource-intensive operations without the need for blocking on the main or executing thread of the application.

Does Python support asynchronous?

Python's asyncio package (introduced in Python 3.4) and its two keywords, async and await , serve different purposes but come together to help you declare, build, execute, and manage asynchronous code.

What is asynchronous task in Python?

In Python, these tasks automatically suspend themselves and allow other operations to run by yielding control to the event loop. On completion of the I/O operation, the task is resumed back to its original state (before it was paused) by the event loop. We can achieve this behavior through a coroutine object.


1 Answers

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)

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).

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.

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 

Output:

GET http://www.google.com/two GET http://www.google.com/one Exception("http://www.google.com/one has error '404: Not Found'",) 

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.

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.

like image 176
dano Avatar answered Sep 20 '22 08:09

dano