Error handling in async code
try/except around await works exactly as you'd expect
Nothing new is needed here —
try/except from the Python setup lesson
applies to await-ed calls exactly the same way it applies to a regular
function call:
The exception propagates out of call_calculator_api(), through the
await, and is caught exactly where you’d expect — await doesn’t
change how exceptions travel, it just marks where the pause happens.
gather()'s default behavior: one failure stops the whole thing
asyncio.gather() needs its own explanation, though, since it’s
combining multiple coroutines at once. By default, if any one of them
raises, gather() itself raises that same exception — even though the
other coroutines might have succeeded, or might still be running:
The whole gather() call raises, results never gets assigned, and
call_search_api()’s successful "3 results found" is simply
discarded — even though that call genuinely did succeed. This matters
because it’s easy to assume gather() behaves like a list of independent
attempts, each one reported on its own — by default, it doesn’t; one
failure looks, from the caller’s side, exactly like the whole batch
failed.
return_exceptions=True — collect failures instead of raising
Passing return_exceptions=True changes this: instead of raising on the
first failure, gather() waits for every coroutine to finish (success or
failure) and returns a list where each position is either that
coroutine’s actual result, or the exception it raised:
Both positions are filled — call_search_api()’s real result at index
0, and the actual ValueError object (not raised — just sitting there
as a value) at index 1, in the same order the coroutines were passed in,
exactly as gather()’s ordering already worked without return_exceptions.
Distinguishing a real result from a failure means checking each item’s
type:
isinstance(result, Exception) works here because every built-in
exception — and every
custom exception you’d define yourself
— is a subclass of Exception,
the same isinstance/inheritance relationship covered back in the OOP lesson,
applying here to tell an exception object apart from a normal return
value sitting in the same list.
Does wrapping await some_coroutine() in a try/except work the same way it would for a regular function call?
Use asyncio.gather(..., return_exceptions=True) to fetch every id concurrently, then split the results into two lists — successful result strings, and str(e) for each exception — preserving each item's relative order within its own list.