8.6. Iznimke¶
Iznimke unutar asyncio skripte ponašaju se gotovo jednako kao u običnom Pythonu – propagiraju se uz lanac poziva sve dok ih nešto ne uhvati. Gotovo jer se zadaci izvršavaju paralelno, pa put „prema gore” nije put koji je stvorio zadatak. Ova stranica pokriva kamo iznimke odlaze u svakom od uobičajenih oblika.
8.6.1. Unutar jedne korutine¶
try/except unutar korutine hvata iznimke koje izazove bilo što što awaita, na uobičajeni način:
async def fetch_with_retry(url):
for attempt in range(3):
try:
return await fetch(url)
except OSError as e:
last_error = e
raise last_error
Ovdje nema ničeg specifičnog za asyncio – klauzula except vidi iznimke izazvane unutar fetch kao da je riječ o običnom pozivu funkcije.
8.6.2. U zadatku koji aplikacija čeka¶
Kad korutina koja se izvršava kao Task izazove iznimku, iznimka se pohranjuje u zadatak. Sljedeći put kad nešto awaita taj zadatak, iznimka se ponovno izaziva na await
task = asyncio.create_task(may_fail())
try:
result = await task
except OSError:
log("may_fail failed")
Isto vrijedi i za asyncio.gather(). Zadano ponašanje – jedno dijete izazove iznimku, ostala se otkažu, iznimka se propagira izvan gathera – proizlazi iz ovog mehanizma.
8.6.3. U zadatku koji nitko ne čeka¶
Zadatak koji nitko nikada ne awaita slučaj je koji zahtijeva pažnju. Iznimka se i dalje događa; petlja primjećuje da je zadatak završio s neobrađenom iznimkom; ali nema await na kojem bi se ona pojavila. Zadano ponašanje je ispisati trag praćenja preko sys.stderr i nastaviti s radom – što je u redu za nenadzirano dijagnosticiranje, ali slabo odgovara aplikaciji koja je htjela znati.
Pravo rješenje obično je pričekati zadatak. Bilo izravno, pamćenjem ručke i čekanjem na nju tijekom gašenja, bilo implicitno preko gather() ili wait_for(). Obrazac „gašenje aplikacije” sa stranice Istek vremena i otkazivanje hvata ovaj slučaj za dugotrajne pozadinske zadatke koje tipična skripta pokreće.
8.6.4. Prilagođeni rukovatelj iznimkama¶
Kad tihi trag-pa-nastavi nije dovoljan, petlja izlaže udicu – Loop.set_exception_handler – koju aplikacija može nadjačati kako bi učinila nešto drugo:
def handler(loop, context):
print("asyncio:", context.get("message"))
if "exception" in context:
sys.print_exception(context["exception"])
loop = asyncio.get_event_loop()
loop.set_exception_handler(handler)
Argument context je rječnik s ključevima 'message', 'exception' i 'future'. Iznimka može nedostajati kod određenih događaja u stilu upozorenja, zbog čega primjer koristi .get().
Tipične upotrebe su bilježenje neuspjeha u flash memoriju, treptanje LED-icom za pogrešku ili eskaliranje do ponovnog pokretanja putem watchdoga. Stranica upravljanje petljom pokriva cijelu površinu udica petlje.
8.6.5. KeyboardInterrupt¶
Kad se skripta zaustavi izvana – obično tako da je IDE zatraži da prestane – zahtjev stiže unutar skripte kao KeyboardInterrupt. Unutar asyncio.run() propagira se kao i bilo koja druga neobrađena iznimka: main se otkazuje, svaki zadatak koji petlja prati također se otkazuje, a KeyboardInterrupt se ponovno izaziva izvan asyncio.run(). Klauzule finally izvršavaju se na izlasku, pa isti obrazac čišćenja sa stranice o otkazivanju upravlja njime.