テクノロジー · 実用ガイド

Python の BrokenPipeError?読み手の終了、flush、終了契約を証明する

トレースバックを隠したり SIGPIPE を変更したりする前に、早期終了した読み手、バッファ、インタープリター終了処理を切り分けます。

Python の書き手、パイプバッファ、早期終了する読み手のオリジナル図。
Watch at Ease オリジナルイラスト:全読み手終了後の次の書き込みが SIGPIPE または EPIPE となり、バッファが報告時点を左右します。 Watch at Ease

パイプ中の Python BrokenPipeError は、通常、書き手が出力を終える前に下流の読み手が停止したことを示します。難しいのは、閉じた場所を証明し、終了時の二度目の失敗を防ぐことです。

この監査はカーネルの規則、Python のバッファ、明示 flush、CLI の終了方針を分離し、想定された head の打ち切りだけを静かにします。

パイプと各要素の状態を固定する

シグナル処理を変える前に、完全なコマンド、トレースバック、stderr、シェルを保存します。stdout の接続先と各要素の状態を取得し、Bash では再現直後に PIPESTATUS、pipefail 使用時は合成状態も残します。head の成功は書き手の完了を保証しません。

失敗を保つ最小の書き手と早期終了する読み手へ縮小し、cat と head -n 1 を比較します。全量消費時は成功し、読み手の早期終了時だけ失敗するなら、下流パイプ閉鎖を示す証拠です。

読み手の close と書き手の flush を証明する

パイプには独立した読み取り端と書き込み端があります。全読み取り記述子が閉じると書き込みに SIGPIPE が発生し、無視されていれば EPIPE で失敗します。Python は通常これを BrokenPipeError に変換します。

出力と sys.stdout.flush() を同じ try 内に置き、早期終了する読み手で試します。実書き込みはバッファ満杯、明示 flush、終了処理まで遅れるため、例外行が元の出力処理より後になることがあります。

断続的な再現をスケジューリングとバッファの証拠にする

小さい出力で一度トレースバックが出なかっただけで安全と判断しません。書き手と読み手は独立に実行され、パイプ容量は有限です。head が閉じる前に flush が終わる場合も、その後に書く場合もあります。

複数の出力量で反復し、明示的な最終 flush を加えます。診断時だけ通常、非バッファ、行バッファを比較し、読み手上限、バイト数、各状態を記録します。安全な行数ではなく close と flush の競合を特定します。

二度目の終了トレースバックを出さずに処理する

早期終了する読み手を仕様として支える CLI では、出力と sys.stdout.flush() を try で囲みます。BrokenPipeError 時は終了前に stdout の記述子を null デバイスへ向け、自動終了 flush が閉じたパイプを再度触るのを防ぎます。

stderr は stdout と分離します。他の I/O 障害を分類できないなら OSError 全体を捕捉しません。ソケット切断、ディスク障害、閉じたシェルパイプは同じ復旧契約ではありません。

CLI の終了契約を選びテストする

早い読み手の終了を正常完了とするか報告すべき失敗とするか決めます。パイプ用フィルターはトレースバックを抑えても、自動化は非ゼロ状態を必要とする場合があります。pipefail と要素別状態を含めて文書化します。

全量読み手、head 型早期終了、下流自身の失敗、通常ファイルへの stdout を行列で試し、stdout、stderr、各状態を別々に確認します。見た目の修正が下流障害や既存スクリプトの意味を隠すのを防ぎます。

SIGPIPE の既定動作を万能な修正にしない

Python 公式文書は、表示を消す目的で SIGPIPE を SIG_DFL にしないよう警告します。グローバル変更はソケット中断時にプロセスを突然終了させ得ます。stdout の局所処理なら他の書き込みの例外モデルを保てます。

フレームワークがシグナル方針を持つなら変更前に追跡します。ローカルパイプと、必要なら制御したソケット切断で検証し、想定された早期終了だけが静かで他の I/O エラーは観測可能であることを確認します。

証拠とロールバックを残す

最小再現、Python 版、OS、シェル、バッファ方式、状態行列を保存し、修正が一つの CLI 入口か共有ライブラリかを記録します。API が約束しない限り、ライブラリ関数は BrokenPipeError を呼び出し側へ伝えるべきです。

局所修正なら捕捉とリダイレクトを外し以前の終了意味へ戻せます。基礎のパイプ規則が同じでも終了処理、ラッパー、シェル設定で見え方が変わるため、更新後も同じ行列を再実行します。

局所的な BrokenPipeError 処理と検証行列のオリジナル図。
Watch at Ease オリジナルイラスト:stdout のみを処理し、終了契約を検証してプロセス全体の SIGPIPE 変更を避けます。 Watch at Ease