web-dev-qa-db-ja.com

Ctrl-Cでasyncioスクリプトを適切に終了する方法は?

Ctrl-Cで終了するasyncioイベントループでスクリプトを適切に処理する方法について見つけることができるすべての投稿を読みました。そうする。答えはほぼどこにでもあり、私はそれらのどれもこの小さなスクリプトに実装できませんでした:

import asyncio
import datetime
import functools
import signal


async def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)


def stopper(signame, loop):
    print("Got %s, stopping..." % signame)
    loop.stop()


loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame), functools.partial(stopper, signame, loop))

loop.run_until_complete(display_date(loop))
loop.close()

Ctrl-C(またはkillを介して送信されたSIGTERM/SIGINT)に続いてトレースバックを出力せずにスクリプトが終了するようにします。このコードはRuntimeError: Event loop stopped before Future completed.以前の回答に基づいて私が試した他の多くの形式では、他のタイプの例外クラスとエラーメッセージが大量にあり、それらを修正する方法がわかりません。上記のコードは現時点では最小限のものですが、以前に行った試みの一部はまったく別のものであり、どれも正しくありませんでした。

正常に終了するようにスクリプトを変更できる場合は、その方法がrightである理由の説明をいただければ幸いです。

10
JK Laiho

実行中のイベントループの停止は有効になりません。

ここでは、デフォルトのスタックトレースを表示する代わりに自分で処理したいことをPythonに示すために、Ctrl-Cをキャッチする必要があります。これは、従来のtry/exceptで実行できます。

coro = display_date(loop)
try:
    loop.run_until_complete(coro)
except KeyboardInterrupt:
    print("Received exit, exiting")

そして、あなたのユースケースについては、それだけです!より現実的なプログラムでは、おそらくいくつかのリソースをクリーンアップする必要があります。参照 asyncioコルーチンの正常なシャットダウン

8
Arthur

signal ハンドラーを使用します。

import asyncio
from signal import SIGINT, SIGTERM

async def main_coro():
    try:
        await awaitable()
    except asyncio.CancelledError:
        do_cleanup()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    main_task = asyncio.ensure_future(main_coro())
    loop.add_signal_handler(SIGINT, main_task.cancel)
    loop.add_signal_handler(SIGTERM, main_task.cancel)
    try:
        loop.run_until_complete(main_task)
    finally:
        loop.close()
5
ManuelSchneid3r