15

イベントを処理するサーバーを作成し、イベントの処理中にキャッチされない例外がサーバーを終了してはなりません。

サーバーは、スレッド化されていない単一の Python プロセスです。

これらのエラータイプで終了したい:

  • キーボード割り込み
  • メモリエラー
  • ...

組み込みの例外のリストは長い: https://docs.python.org/2/library/exceptions.html

以前に何度か行われたと思うので、この例外処理を再発明したくありません。

どうやって進める?

  1. ホワイトリストを用意する: OK であり、次のイベントを処理することが正しい選択である例外のリスト
  2. ブラックリストを持つ: サーバーを終了することが正しい選択であることを示す例外のリスト。

ヒント: この質問は、UNIX デーモンをバックグラウンドで実行することに関するものではありません。それはダブルフォークではなく、stdin/stdout のリダイレクトでもありません :-)

4

2 に答える 2

4

私はあなたが考えているのと同様の方法でこれを行います.「合格してはならない」ガンダルフ例外ハンドラーを使用して、except Exceptionシステムを終了しないすべての例外をキャッチしset、合格して再終了する必要がある例外のブラックリストを作成します-上げた。

Gandalf ハンドラーを使用すると、コール スタックの上位に他のハンドラーが存在しない場合、(すべてのシステム終了例外)および(すべてのシステム終了例外) が確実GeneratorExitに渡され、プログラムが終了します。ここで、キャッチされた例外が実際にブラックリストの例外のセットに属していることを確認して、再度確認できます。SystemExitKeyboardInterrupttype(e)__class__eraise

小さなデモンストレーションとして:

import exceptions  # Py2.x only

# dictionary holding {exception_name: exception_class}
excptDict = vars(exceptions)

exceptionNames = ['MemoryError', 'OSError', 'SystemError'] # and others

# set containing black-listed exceptions
blackSet = {excptDict[exception] for exception in exceptionNames}

blackSet = {OSError, SystemError, MemoryError}これで、処理したくないシステム外の例外のクラスが保持されます。

ブロックは次のtry-exceptようになります。

try:
    # calls that raise exceptions:
except Exception as e:
    if type(e) in blackSet: raise e # re-raise
    # else just handle it

を使用してすべての例外をキャッチするBaseExceptionは、私の言いたいことを説明するのに役立ちます。(これはデモンストレーションのみを目的として行われ、このレイズが最終的にどのようにプログラムを終了させるかを確認するためです)。注意してください私はあなたが使用することをお勧めしていませんBaseException; どの例外が実際に「パススルー」して終了を引き起こすか (つまり、BaseExceptionキャッチするすべて)を示すために使用しています。

for i, j in excptDict.iteritems():
    if i.startswith('__'): continue  # __doc__ and other dunders
    try:
        try:
            raise j
        except Exception as ex:
            # print "Handler 'Exception' caught " + str(i)
            if type(ex) in blackSet:
                raise ex           
    except BaseException:
        print "Handler 'BaseException' caught " + str(i)

# prints exceptions that would cause the system to exit     
Handler 'BaseException' caught GeneratorExit
Handler 'BaseException' caught OSError
Handler 'BaseException' caught SystemExit
Handler 'BaseException' caught SystemError
Handler 'BaseException' caught KeyboardInterrupt
Handler 'BaseException' caught MemoryError
Handler 'BaseException' caught BaseException

最後に、この Python 2/3 に依存しないようにするために、tryそれが失敗した場合 (Python 3 ではそうです)、すべての;を含むimport exceptionsインポートにフォールバックできます。名前で辞書を検索するので、違いはありません。builtinsExceptions

try:
    import exceptions
    excDict = vars(exceptions)
except ImportError:
    import builtins 
    excDict = vars(builtins)

これを実際に行うためのよりスマートな方法があるかどうかはわかりません。 別の解決策はtry-except、 signleを使用する代わりにexcept、ブラックリストに記載された例外用と一般的なケース用の 2 つのハンドラーを持つことです。

try:
    # calls that raise exceptions:
except tuple(blackSet) as be:  # Must go first, of course.
    raise be
except Exception as e:
    # handle the rest
于 2016-01-25T12:06:18.640 に答える
2

最上位の例外はBaseException. その下に 2 つのグループがあります。

  • Exception派生
  • ほかのすべて

StopiterationValueErrorTypeErrorなどはすべて の例ですException

のようなものはGeneratorExit、の子孫ではSystemExitありません。KeyboardInterruptExecption

したがって、最初のステップは、キャッチExceptionBaseExceptionてプログラムを簡単に終了できるようにすることです。また、キャッチすることをお勧めGeneratorExitします。1)手動で上げない限り、実際には表示されません。2)ログに記録してループを再開できます。3) ジェネレーターが終了し、クリーンアップできることを通知するためのものであり、プログラムを終了する必要があることを示すものではありません。

次のステップは、(後でデバッグを行うときに) 何が問題なのかを突き止めることができるように、各例外を詳細にログに記録することです。

Exception最後に、終了する派生例外がある場合は、自分で決定する必要があります。サーバーループを停止して再起動するだけでそれらを回避できる場合もありますが、RuntimeErrorとをお勧めします。MemoryError

だから、本当に、それはあなた次第です。

他のエラー (IOError設定ファイルを読み込もうとしているときなど) が終了するほど深刻な場合、設定ファイルの読み込みを担当するコードは、それをキャッチして代わりに発生さIOErrorせるのに十分なほどスマートでなければなりませんSystemExit

Exceptionホワイトリスト/ブラックリストに関しては、サーバーを実際に終了する必要があるベースの例外は、あるとしても少数であるため、ブラックリストを使用してください。

于 2016-01-28T05:57:12.993 に答える