1

ブロック内で例外を発生except:させようとしていますが、インタープリターは役に立ち、「強制的に」スタック トレースを出力しようとします。これを回避することは可能ですか?

少しの背景情報: Python のTUI ライブラリである urwid をいじっています。ユーザー インターフェイスは、 を呼び出して開始し、を上げurwid.MainLoop.run()て終了します。これまでのところ問題なく動作していますが、別の例外が発生するとどうなりますか? たとえば、キャッチしているとき(urwid MainLoop はそうではありません)、適切な例外を発生させて、クリーンアップを行い、ユーザー インターフェイスを終了したいと考えています。ただし、これにより、画面がスタック トレースでいっぱいになります。 urwid.ExitMainLoop()KeyboardInterrupt

いくつかの小さな調査では、python3 がチェーンされた例外を記憶しており、'cause': を使用して明示的に例外を発生させることができることが示されましたraise B() from A()。発生した例外に関するデータを変更または追加する方法をいくつか学びましたが、この機能を「無効にする」方法は見つかりませんでした。スタック トレースや行の出力を避けたいのですが、ブロックの外側で行うようにThe above exception was the direct cause of...、ブロック内でインターフェイス終了例外を発生させます。except:

これは可能ですか、それとも根本的に間違ったことをしていますか?

編集:これは私の現在のアーキテクチャに似た例で、同じ問題が発生します:

#!/usr/bin/env python3
import time

class Exit_Main_Loop(Exception):
    pass

# UI main loop
def main_loop():
    try:
        while True:
            time.sleep(0.1)
    except Exit_Main_Loop as e:
        print('Exit_Main_Loop')
        # do some UI-related clean up

# my main script
try:
    main_loop()
except KeyboardInterrupt as e:
    print('KeyboardInterrupt')
    # do some clean up
    raise Exit_Main_Loop()      # signal the UI to terminate

残念ながら、私も同様main_loopに変更できません。KeyboardInterruptこれを解決するパターンはありますか?

4

1 に答える 1

1

私はまだあなたの説明をよく理解していませんが、コードから:

try:
    main_loop()
except KeyboardInterrupt as e:
    print('KeyboardInterrupt')
    # do some clean up
    raise Exit_Main_Loop()      # signal the UI to terminate

例外main_loopを見ることができる方法はありません。ハンドルにExit_Main_Loop()到達するまでに、は既に終了していることが保証されているため (この場合は unhandled のため)、その例外ハンドラーはアクティブではなくなります。KeyboardInterruptmain_loopKeyboardInterrupt

したがって、誰もキャッチしない新しい例外が発生します。また、例外が処理されずにコードの先頭に到達すると、Python はトレースバックを出力して終了することで自動的に処理します。

あるタイプの例外を別のタイプに変換して処理できるようにする場合は、ブロックmain_loopどこかでそれを行う必要があります。try

あなたは言う:

残念ながら、main_loop を KeyboardInterrupt 以外に変更することもできません。

それが本当なら、あなたの問題に対する本当の答えはありません… しかし、あなたが作成した問題以外に、そもそも問題があるかどうかはわかりません。コードから を削除するだけExit_Main_Loop()で、すでに望んでいたことを実行していませんか? Python がトレースバックを出力して終了するのを防ごうとしているだけなら、これが面倒を見てくれます。


実際に問題がある場合(たとえば、main_loopコードにクリーンアップ コードがあり、どうしても実行する必要があり、処理できないために実行されない場合)、KeyboardInterruptこれを回避する方法が 2 つあります。


まず、signalドキュメントで説明されているように:

このsignal.signal()関数を使用すると、シグナルを受信したときに実行されるカスタム ハンドラーを定義できます。少数のデフォルト ハンドラーがインストールされています: …SIGINTは例外に変換されKeyboardInterruptます。

したがって、デフォルトのハンドラーを別のハンドラーに置き換えるだけです。

def handle_sigint(signum, frame):
    raise ExitMainLoop()
signal.signal(signal.SIGINT, handle_sigint)

開始する前にこれを行うだけで問題main_loopありません。スレッド化されたプログラムと Windows にはいくつかの制限があることに注意してください。ctrl-C は のExitMainLoop代わりに例外をトリガーするKeyboardInterruptため、メインループがそれを処理します。( の外部でexcept ExitMainLoop:例外が発生した場合に備えて、ラッパー コードにブロックを追加することもできます。ただし、への呼び出しの前後で信号を設定および復元する を簡単に作成できるため、上げる可能性があります。) main_loopcontextmanagermain_loop


または、ソース コードを編集できない場合でもmain_loop、実行時にいつでもモンキー パッチを適用できます。コードがどのように見えるかを知らずに、これを行う方法を正確に説明することは不可能ですが、ほとんどの場合、それを行う方法があります。

于 2013-08-28T23:57:45.050 に答える