Pythonでの非同期例外処理 質問する

Pythonでの非同期例外処理 質問する

非同期 HTTP リクエストを作成するためにasyncio、 とを使用して次のコードを作成しました。aiohttp

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)

何らかの理由で、get関数内で発生した例外はキャッチされません。

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'

では、コルーチンによって発生した例外を処理する正しい方法は何でしょうか?

ベストアンサー1

asyncio.wait渡されたものを実際に消費するのではなくFutures、完了するまで待機し、Futureオブジェクトを返します。

コルーチン asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

シーケンス futures によって指定された Futures およびコルーチン オブジェクトが完了するまで待機します。コルーチンは Tasks にラップされます。2 つのセットFuture(完了、保留中) を返します。

リスト内の項目を実際にyield from/するまで、それらは消費されないままになります。プログラムは先物を消費せずに終了するため、「例外は取得されませんでした」というメッセージが表示されます。awaitdone

あなたのユースケースでは、おそらくasyncio.gatherは、実際に各 を消費しFuture、すべての結果を集約した単一の を返します(または、入力リスト内の future によってFuture最初にスローされた を発生させます)。Exception

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'",)

実際には、いずれかの futures が例外を発生させたときの動作をカスタマイズできることに注意してくださいasyncio.gather。デフォルトの動作では、最初に発生した例外を発生させますが、出力リスト内の各例外オブジェクトを返すこともできます。

asyncio.gather(*coros_or_futures, loop=None, return_exceptions=False)

指定されたコルーチン オブジェクトまたは Future からの結果を集約した Future を返します。

すべての future は同じイベント ループを共有する必要があります。すべてのタスクが正常に完了した場合、返される future の結果は結果のリストです (元のシーケンスの順序であり、必ずしも結果の到着順序ではありません)。return_exceptionsの場合True、タスク内の例外は成功した結果と同じように扱われ、結果リストに集められます。それ以外の場合、最初に発生した例外は返された future に直ちに伝播されます。

おすすめ記事