Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13962] ファイルの概要

このコミットは、Go言語のコードレビューツール codereview において、外部コマンド実行に os.spawnvp() の代わりに subprocess.call() を使用するように変更したものです。これにより、ツールの移植性が向上し、特定の環境での問題(Issue #4121)が修正されました。

コミット

  • コミットハッシュ: b7331f9b3a1a5ece290c1a19cd68c58642fe26fb
  • 作者: Shivakumar GN shivakumar.gn@gmail.com
  • コミット日時: 2012年9月27日 木曜日 01:50:59 +0800

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/b7331f9b3a1a5ece290c1a19cd68c58642fe26fb

元コミット内容

codereview: use subprocess.call() instead of os.spawnvp() for portability

Fixes #4121.

R=golang-dev, minux.ma, dave, r
CC=golang-dev
https://golang.org/cl/6555049

変更の背景

この変更の主な背景は、Go言語のコードレビューシステム codereview が使用する外部コマンド実行方法の移植性に関する問題でした。具体的には、os.spawnvp() 関数が一部のオペレーティングシステム(特にWindows)で期待通りに動作しない、または利用できないケースがあったため、よりクロスプラットフォーム互換性の高い subprocess.call() へと移行する必要がありました。

コミットメッセージに Fixes #4121 とあるように、この変更はGoプロジェクトのIssue 4121を解決するために行われました。Issue #4121は、os.spawnvp() がWindows環境で gofmt コマンドを正しく実行できないという問題に起因しています。gofmt はGoコードのフォーマットを行うための重要なツールであり、コードレビュープロセスにおいてその実行は不可欠です。この問題により、Windowsユーザーが codereview ツールを円滑に利用できないという状況が発生していました。

subprocess モジュールは、Pythonで外部プロセスを生成、管理、および通信するための推奨される方法であり、os.spawn* ファミリーの関数よりも柔軟性と移植性に優れています。この変更は、codereview ツールがより多くの環境で安定して動作するようにするための重要な改善でした。

前提知識の解説

os.spawnvp()

os.spawnvp() はPythonの os モジュールに含まれる関数の一つで、新しいプロセスを生成し、指定されたプログラムを実行します。

  • os.P_WAIT: 子プロセスが終了するまで親プロセスが待機することを示します。
  • program: 実行するプログラムの名前(例: "gofmt")。
  • args: プログラムに渡す引数のリスト。最初の要素はプログラム名自体である必要があります。

os.spawnvp() は、Unix系のシステムでは execvp() システムコールを内部的に使用することが多く、Windowsでは異なる実装が用いられます。この関数の問題点として、プラットフォーム間の挙動の差異や、標準入出力のリダイレクトなどの高度な制御が難しい点が挙げられます。特に、Windows環境ではパスの解決やシェルとの連携において問題が発生しやすい傾向がありました。

subprocess.call()

subprocess.call() はPythonの subprocess モジュールに含まれる関数で、外部コマンドを実行し、その終了コードを返します。

  • args: 実行するコマンドと引数のリスト(例: ["gofmt", "-l", "file1.go"])。
  • shell=True を指定しない限り、シェルを介さずに直接コマンドを実行します。これにより、シェルに依存する挙動の違いを避けることができます。

subprocess モジュールは、os.spawn*os.system() といった古い関数群に代わる、より強力で柔軟な外部プロセス管理機能を提供します。subprocess.call() は、コマンドの実行、標準入出力のリダイレクト、エラー処理、タイムアウト設定など、様々なシナリオに対応できます。特に、クロスプラットフォームでの互換性が高く、Windows、Linux、macOSなど、異なるOS環境で一貫した挙動を期待できます。

gofmt

gofmt はGo言語のソースコードを標準的なスタイルに自動的にフォーマットするツールです。Go言語のプロジェクトでは、コードの一貫性を保つために gofmt の使用が強く推奨されています。gofmt は、インデント、スペース、改行などを自動的に調整し、Goコミュニティ全体で統一されたコードスタイルを維持するのに役立ちます。

codereview ツール

codereview は、Goプロジェクトで利用されていたコードレビューシステムの一部を構成するツールです。このツールは、変更されたコードの差分を表示したり、gofmt のような補助ツールを実行してコードスタイルをチェックしたりする機能を持っていました。開発者が変更を提出する前に、このツールを使って自動的なチェックを行うことで、コード品質の維持に貢献していました。

技術的詳細

このコミットの技術的な核心は、Pythonにおける外部プロセス実行のベストプラクティスへの移行です。

os.spawnvp() は、Pythonがまだ subprocess モジュールを持っていなかった時代に、外部プログラムを実行するための主要な手段の一つでした。しかし、この関数は低レベルなインターフェースを提供し、プラットフォーム固有の挙動に依存する傾向がありました。特に、Windows環境では、PATH 環境変数の解釈や、実行可能ファイルの検索方法に違いがあり、Unix系システムと同じように動作しないことが頻繁にありました。また、os.spawnvp() は、子プロセスの標準入出力の制御や、エラーハンドリングの柔軟性に欠けていました。

一方、subprocess モジュールはPython 2.4で導入され、外部プロセスとの対話をより安全かつ柔軟に行うための包括的なAPIを提供します。subprocess.call() は、その中でも最もシンプルな使用例の一つで、コマンドを実行し、その終了コードを待つだけです。subprocess.call() は、内部的に Popen クラスを使用しており、プラットフォーム間の差異を吸収するためのロジックが組み込まれています。これにより、開発者はOSの違いを意識することなく、一貫した方法で外部コマンドを実行できるようになります。

この変更により、codereview.py スクリプトは、gofmt コマンドの実行において、より堅牢で移植性の高い方法を採用しました。subprocess.call() を使用することで、Windowsを含む様々なオペレーティングシステム上で gofmt が正しく呼び出され、コードレビュープロセスがスムーズに進行するようになりました。これは、Goプロジェクトがクロスプラットフォーム開発を重視していることと合致する重要な改善でした。

コアとなるコードの変更箇所

--- a/lib/codereview/codereview.py
+++ b/lib/codereview/codereview.py
@@ -1772,7 +1772,7 @@ def gofmt(ui, repo, *pats, **opts):
 		cmd = ["gofmt", "-l"]
 		if not opts["list"]:\n 			cmd += ["-w"]
-\t\tif os.spawnvp(os.P_WAIT, "gofmt", cmd + files) != 0:
+\t\tif subprocess.call(cmd + files) != 0:
 			raise hg_util.Abort("gofmt did not exit cleanly")
 		except hg_error.Abort, e:
 			raise

コアとなるコードの解説

変更は lib/codereview/codereview.py ファイルの gofmt 関数内で行われています。

元のコード:

if os.spawnvp(os.P_WAIT, "gofmt", cmd + files) != 0:

この行では、os.spawnvp() を使用して gofmt コマンドを実行していました。

  • os.P_WAIT: gofmt プロセスが終了するまで待機します。
  • "gofmt": 実行するプログラムの名前。
  • cmd + files: gofmt に渡される引数のリスト。cmd には ["gofmt", "-l"] または ["gofmt", "-w"] が含まれ、files には処理対象のファイルリストが含まれます。

変更後のコード:

if subprocess.call(cmd + files) != 0:

この行では、subprocess.call() を使用して gofmt コマンドを実行しています。

  • cmd + files: subprocess.call() に渡されるコマンドと引数のリスト。subprocess.call() はこのリストを直接実行します。

両方の関数とも、外部コマンドの終了コードが 0 でない場合(つまり、エラーが発生した場合)に hg_util.Abort 例外を発生させるロジックは変更されていません。

この変更により、gofmt コマンドの実行が os.spawnvp() のプラットフォーム依存性から解放され、subprocess.call() の提供するより堅牢で移植性の高いメカニズムに置き換えられました。これにより、特にWindows環境での gofmt 実行に関する問題が解決され、codereview ツールの全体的な信頼性と互換性が向上しました。

関連リンク

参考にした情報源リンク