8.6. İstisnalar

Bir asyncio betiği içindeki istisnalar, normal Python’dakiyle neredeyse aynı şekilde davranır – bir şey onları yakalayana kadar çağrı zincirinde yukarı doğru yayılırlar. Neredeyse aynıdır çünkü görevler paralel olarak çalışır, bu nedenle “yukarı” yolu, görevi oluşturan yol değildir. Bu sayfa, yaygın yapıların her birinde istisnaların nereye gittiğini ele alır.

8.6.1. Tek bir eşyordam içinde

Bir eşyordam içindeki bir try/except bloğu, await ettiği herhangi bir şeyin oluşturduğu istisnaları her zamanki şekilde yakalar:

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

Burada asyncio’ya özgü bir şey yoktur – except yan tümcesi, fetch içinde oluşan istisnaları, sanki normal bir işlev çağrısıymış gibi görür.

8.6.2. Uygulamanın beklediği bir görevde

Task olarak çalışan bir eşyordam bir istisna oluşturduğunda, istisna görevde saklanır. Bir sonraki sefer bir şey o görevi await ettiğinde, istisna await noktasında yeniden oluşturulur:

task = asyncio.create_task(may_fail())
try:
    result = await task
except OSError:
    log("may_fail failed")

Aynısı asyncio.gather() için de geçerlidir. Varsayılan davranış – bir alt öğe istisna oluşturur, diğerleri iptal edilir, istisna gather’dan dışarı yayılır – bu mekanizmadan gelir.

8.6.3. Kimsenin beklemediği bir görevde

Hiç kimsenin await etmediği bir görev, dikkat gerektiren durumdur. İstisna yine de oluşur; döngü, görevin işlenmemiş bir istisnayla bittiğini fark eder; ancak bunun ortaya çıkacağı bir await yoktur. Varsayılan davranış, sys.stderr üzerinden bir geri izleme yazdırmak ve çalışmaya devam etmektir – bu, gözetimsiz bir tanılama için sorun değildir, ancak bilmek isteyen bir uygulama için zayıf bir uyumdur.

Doğru çözüm genellikle görevi beklemektir. Ya doğrudan, tutamacı hatırlayıp kapatma sırasında onu bekleyerek, ya da gather() veya wait_for() aracılığıyla dolaylı olarak. Zaman aşımları ve iptal sayfasındaki “bir uygulamayı kapatma” deseni, tipik bir betiğin oluşturduğu uzun ömürlü arka plan görevleri için bu durumu yakalar.

8.6.4. Özel istisna işleyicisi

Sessiz geri-izleme-ve-devam-et yeterli olmadığında, döngü bir kanca sunar – Loop.set_exception_handler – uygulama bunu başka bir şey yapmak için geçersiz kılabilir:

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)

context argümanı, 'message', 'exception' ve 'future' anahtarlarına sahip bir sözlüktür. İstisna, belirli uyarı tarzı olaylarda bulunmayabilir, bu nedenle örnek .get() kullanır.

Tipik kullanımlar, hatayı flash belleğe günlüğe kaydetmek, bir hata LED’ini yakıp söndürmek veya bir bekçi köpeği yeniden başlatmasına yükseltmektir. döngü denetimi sayfası, döngü kancalarının tüm yüzeyini ele alır.

8.6.5. KeyboardInterrupt

Bir betik dışarıdan durdurulduğunda – genellikle IDE’nin onu durdurmasını istemesiyle – istek, betiğin içine bir KeyboardInterrupt olarak gelir. asyncio.run() içinde, işlenmemiş herhangi bir istisnanın yapacağı şekilde yayılır: main iptal edilir, döngünün izlediği her görev de iptal edilir ve KeyboardInterrupt asyncio.run()‘dan dışarı yeniden oluşturulur. Çıkış sırasında finally yan tümceleri çalışır, bu nedenle iptal sayfasındaki aynı temizleme deseni bunu işleyen şeydir.