48

注意: 以下で説明する問題を、Windows または 2.7.3 以外のバージョンの Python で再現しようとはしていません。

問題を引き起こす最も確実な方法は、次のテスト スクリプトの出力を:(下bash)にパイプすることです。

try:
    for n in range(20):
        print n
except:
    pass

すなわち:

% python testscript.py | :
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr

私の質問は:

上記のテスト スクリプトを変更して、スクリプトを (Unix/ の下で) 示されているように実行したときにエラー メッセージが表示されないようにするにはどうすればよいbashですか?

(テスト スクリプトが示すように、エラーは でトラップできませんtry-except。)

上記の例は、確かに非常に人為的ですが、私のスクリプトの出力がサードパーティのソフトウェアを介してパイプされると、同じ問題が発生することがあります。

エラー メッセージは確かに無害ですが、エンド ユーザーを当惑させるので、黙らせたいと思います。

編集: 次のスクリプトは、sys.excepthook を再定義するという点でのみ上記の元のスクリプトと異なりますが、上記のスクリプトとまったく同じように動作します。

import sys
STDERR = sys.stderr
def excepthook(*args):
    print >> STDERR, 'caught'
    print >> STDERR, args

sys.excepthook = excepthook

try:
    for n in range(20):
        print n
except:
    pass
4

4 に答える 4

71

上記のテスト スクリプトを変更して、スクリプトを (Unix/ の下で) 示されているように実行したときにエラー メッセージが表示されないようにするにはどうすればよいbashですか?

スクリプトが標準出力に何も書き込まないようにする必要があります。つまり、すべてのprintステートメントと の使用、およびsys.stdout.writeそれらを呼び出すすべてのコードを削除することを意味します。

これが発生する理由は、Python スクリプトからのゼロ以外の量の出力を、標準入力から決して読み取らないものにパイプしているためです。:これはコマンドに固有のものではありません。次のような標準入力を読み取らないコマンドにパイプすることで、同じ結果を得ることができます。

python testscript.py | cd .

printer.pyまたは、より単純な例として、

print 'abcde'

それで

python printer.py | python printer.py

同じエラーが発生します。

あるプログラムの出力を別のプログラムにパイプすると、書き込みプログラムによって生成された出力がバッファーにバックアップされ、読み取りプログラムがバッファーからそのデータを要求するのを待ちます。バッファーが空でない限り、書き込み中のファイル オブジェクトを閉じようとすると、エラーで失敗するはずです。これが、表示されるメッセージの根本的な原因です。

tryエラーをトリガーする特定のコードは、Python の C 言語実装にあり、 /exceptブロックでキャッチできない理由を説明しています。スクリプトのコンテンツの処理が終了した後に実行されます。基本的に、Python がシャットダウンしている間、Python は を閉じようとしstdoutますが、読み取りを待機しているバッファリングされた出力がまだあるため、失敗します。そのため、Python は通常どおりこのエラーを報告しようとしますsys.excepthookが、ファイナライズ手順の一部として既に削除されているため、失敗します。次に、Python は にメッセージを出力しようとしますがsys.stderr、既に割り当てが解除されているため、失敗します。画面にメッセージが表示されるのは、Python コードに不測の事態が含まれているためです。fprintfPython の出力オブジェクトが存在しない場合でも、出力をファイル ポインタに直接書き出すことができます。

技術的な詳細

この手順の詳細に興味がある方は、 のPy_Finalize関数に実装されている Python インタープリターのシャットダウン シーケンスを見てみましょうpythonrun.c

  1. 終了フックを呼び出してスレッドをシャットダウンした後、ファイナライズ コードは、PyImport_Cleanupインポートされたすべてのモジュールをファイナライズして割り当てを解除するために呼び出します。この関数によって実行される最後から 2 番目のタスクは、 moduleを削除することsysです。これは主に、モジュールのディクショナリ内のすべてのエントリをクリアするための呼び出しで構成されます。特に、や_PyModule_Clearなどの標準ストリーム オブジェクト (Python オブジェクト) が含まれます。stdoutstderr
  2. 値がディクショナリから削除されるか、新しい値に置き換えられると、その参照カウントはマクロを使用しPy_DECREF減分されます。参照カウントがゼロになったオブジェクトは、割り当て解除の対象になります。sysモジュールは標準ストリーム オブジェクトへの最後に残った参照を保持するため、これらの参照が によって設定解除されると_PyModule_Clear、割り当てを解除する準備が整います。1
  3. Python ファイル オブジェクトの割り当て解除は、file_dealloc関数によって実行されfileobject.cます。これはまず、適切な名前のfunctionを使用して Python ファイル オブジェクトのcloseメソッドを呼び出します。close_the_file

    ret = close_the_file(f);
    

    標準のファイル オブジェクトの場合、ファイル ポインタに書き込むデータがまだある場合にエラー状態を設定するC関数にclose_the_file(f) 委譲します。fclosefile_dealloc次に、そのエラー状態をチェックし、表示される最初のメッセージを出力します。

    if (!ret) {
        PySys_WriteStderr("close failed in file object destructor:\n");
        PyErr_Print();
    }
    else {
        Py_DECREF(ret);
    }
    
  4. そのメッセージを出力した後、Python は を使用して例外を表示しようとしますPyErr_Print。これは に委譲しPyErr_PrintEx、その機能の一部として、PyErr_PrintExから Python 例外プリンタへのアクセスを試みますsys.excepthook

    hook = PySys_GetObject("excepthook");
    

    これは、Python プログラムの通常の過程で行われれば問題ありませんが、この状況でsys.excepthookは既にクリアされています。2 Python はこのエラー状態をチェックし、2 番目のメッセージを通知として出力します。

    if (hook && hook != Py_None) {
        ...
    } else {
        PySys_WriteStderr("sys.excepthook is missing\n");
        PyErr_Display(exception, v, tb);
    }
    
  5. が見つからないことを通知した後excepthook、Python は を使用して例外情報を出力するようにフォールバックしPyErr_Displayます。これは、スタック トレースを表示するためのデフォルトの方法です。この関数が最初に行うことは、 へのアクセスを試みることですsys.stderr

    PyObject *f = PySys_GetObject("stderr");
    

    この場合、sys.stderrはすでにクリアされていてアクセスできないため、これは機能しません。3したがって、コードは直接呼び出しfprintfて、3 番目のメッセージを C 標準エラー ストリームに送信します。

    if (f == NULL || f == Py_None)
        fprintf(stderr, "lost sys.stderr\n");
    

興味深いことに、Python 3.4+ では動作が少し異なります。これは、組み込みモジュールがクリアされる前に、ファイナライズ プロシージャが標準出力とエラー ストリームを明示的にフラッシュするようになったためです。このようにして、書き込みを待機しているデータがある場合、通常のファイナライズ手順での「偶発的な」失敗ではなく、その状態を明示的に示すエラーが発生します。また、実行すると

python printer.py | python printer.py

Python 3.4 を使用すると (printもちろんステートメントに括弧を付けた後)、エラーはまったく発生しません。Python の 2 回目の呼び出しは、何らかの理由で標準入力を消費している可能性があると思いますが、それはまったく別の問題です。


1実は嘘です。Python のインポート メカニズムは、インポートされた各モジュールのディクショナリのコピーをキャッシュします。これは、実行されるまでリリースされず_PyImport_Fini後で の実装にPy_Finalizeなります。標準ストリーム オブジェクトへの最後の参照が消えるのはそのときです参照カウントがゼロになると、すぐPy_DECREFにオブジェクトの割り当てを解除します。しかし、主な答えにとって重要なのは、参照がモジュールの辞書から削除され、後で割り当てが解除されることだけです。sys

2繰り返しになりますが、これはsys、属性キャッシュ メカニズムのおかげで、モジュールのディクショナリが実際に割り当てが解除される前に完全にクリアされるためです。-vvファイル ポインターを閉じることに関するエラー メッセージが表示される前に、すべてのモジュールの属性が設定解除されていることを確認するオプションを使用して Python を実行できます。

3この特定の動作は、前の脚注で説明した属性キャッシュ メカニズムについて知らなければ意味をなさない唯一の部分です。

于 2012-10-09T19:42:15.010 に答える
11

今日、私はこの種の問題に遭遇し、答えを探しに行きました。ここでの簡単な回避策は、まず stdio を確実にフラッシュすることだと思います。これにより、スクリプトのシャットダウン中に失敗するのではなく、python がブロックされます。例えば:

--- a/testscript.py
+++ b/testscript.py
@@ -9,5 +9,6 @@ sys.excepthook = excepthook
 try:
     for n in range(20):
         print n
+    sys.stdout.flush()
 except:
     pass

このスクリプトでは、例外 (IOError: [Errno 32] Broken pipe) が try...except によって抑制されるため、何も起こりません。

$ python testscript.py  | :
$
于 2014-07-25T17:05:40.903 に答える
2

プログラムで、try/exceptブロックを使用してキャッチできない例外をスローします。彼を捕まえるには、関数をオーバーライドしますsys.excepthook

import sys
sys.excepthook = lambda *args: None

ドキュメントから:

sys.excepthook(type、value、traceback)

例外が発生してキャッチされない場合、インタープリターは、例外クラス、例外インスタンス、およびトレースバックオブジェクトの3つの引数を使用してsys.excepthookを呼び出します。インタラクティブセッションでは、これは制御がプロンプトに戻る直前に発生します。Pythonプログラムでは、これはプログラムが終了する直前に発生します。このようなトップレベルの例外の処理は、別の3つの引数関数をsys.excepthookに割り当てることでカスタマイズできます。

実例:

import sys
import logging

def log_uncaught_exceptions(exception_type, exception, tb):

    logging.critical(''.join(traceback.format_tb(tb)))
    logging.critical('{0}: {1}'.format(exception_type, exception))

sys.excepthook = log_uncaught_exceptions
于 2012-10-08T22:31:16.020 に答える
-6

これは古い質問だと思いますが、エラーの Google 検索で見つけました。私の場合、それはコーディングエラーでした。私の最後の声明の1つは次のとおりです。

print "Good Bye"

解決策は、構文を次のように修正するだけでした。

print ("Good Bye")

[ラズベリーパイ ゼロ、パイソン 2.7.9]

于 2017-03-18T02:29:13.303 に答える