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

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

このコミットは、Go言語のコードレビューシステムの一部である lib/codereview/codereview.py スクリプト内の gofmt 関数におけるクラッシュバグを修正するものです。具体的には、hg gofmt コマンドが処理すべきファイルを見つけられなかった場合に発生する問題に対処しています。

コミット

commit 50231fa19f7c88834edd95836dca95267ef8bb4f
Author: Rob Pike <r@golang.org>
Date:   Tue Mar 26 17:32:22 2013 -0700

    lib/codereview/codereview.py: fix crash when hg gofmt has no files
    The gofmt function was returning a string, which isn't the right type.
    Three cheers for dynamic typing.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7917044

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

https://github.com/golang/go/commit/50231fa19f7c88834edd95836dca95267ef8bb4f

元コミット内容

lib/codereview/codereview.py: fix crash when hg gofmt has no files The gofmt function was returning a string, which isn't the right type. Three cheers for dynamic typing.

このコミットは、lib/codereview/codereview.py ファイル内の gofmt 関数が、hg gofmt コマンド実行時に処理すべきファイルが存在しない場合にクラッシュする問題を修正します。原因は、gofmt 関数が本来期待される型ではない文字列を返していたことにありました。コミットメッセージは、この問題が動的型付けの言語(Python)で発生しやすい典型的な問題であることを示唆しています。

変更の背景

Go言語の開発初期段階では、バージョン管理システムとしてMercurial (hg) が使用されており、コードレビュープロセスも独自のツール (codereview.py) を介して行われていました。gofmt はGo言語のコードを自動的に整形するツールであり、開発ワークフローにおいて非常に重要な役割を担っています。

このコミットが修正している問題は、codereview.py 内の gofmt 関数が、変更されたGoファイルが存在しない状況で呼び出された際に発生していました。本来、この関数は何も返さないか、特定の型の値を返すことを期待されていたにもかかわらず、エラーメッセージとして文字列を返していました。Pythonのような動的型付け言語では、関数の戻り値の型が厳密にチェックされないため、このような予期せぬ型の値が返されると、呼び出し元でその値を処理しようとした際に型エラーやクラッシュを引き起こす可能性があります。

このバグは、開発者がコードレビューのために hg gofmt コマンドを実行した際に、対象となるGoファイルがない場合にツールが正常に動作せず、開発体験を損なうものでした。安定した開発ワークフローを維持するためには、このようなエッジケースでのクラッシュを修正することが不可欠でした。

前提知識の解説

  • gofmt: Go言語の公式なコード整形ツールです。Goのソースコードを標準的なスタイルに自動的に整形し、コードの一貫性を保ち、可読性を向上させる役割を担います。Go開発において非常に広く使われており、多くの開発環境やCI/CDパイプラインに組み込まれています。
  • Mercurial (hg): 分散型バージョン管理システムの一つで、Gitと同様の機能を提供します。Go言語プロジェクトは初期にMercurialを使用していましたが、後にGitに移行しました。このコミットはMercurialがまだ使用されていた時期のものであるため、hg コマンドが言及されています。
  • codereview.py: Go言語の初期のコードレビューシステムで使われていたPythonスクリプトです。Mercurialリポジトリと連携し、変更点の取得、パッチの生成、gofmt の実行など、コードレビュープロセスを支援する機能を提供していました。
  • 動的型付け (Dynamic Typing): プログラムの実行時に変数の型が決定されるプログラミング言語の特性です(例: Python, JavaScript)。静的型付け言語(例: Go, Java)とは異なり、コンパイル時に型のチェックが行われないため、実行時まで型に関するエラーが検出されないことがあります。このコミットのメッセージにある「Three cheers for dynamic typing.」は、動的型付けがこのような実行時エラーを引き起こしやすいという皮肉を込めた表現です。
  • ui.status: codereview.py のようなCLIツールにおいて、ユーザーインターフェース (UI) を介してステータスメッセージや情報を表示するための一般的なパターンです。標準出力にメッセージを出力するのと似ていますが、ツール固有のロギングや表示メカニズムの一部である場合があります。

技術的詳細

このコミットの技術的詳細は、Pythonにおける関数の戻り値の扱いと、それが呼び出し元に与える影響に集約されます。

修正前の gofmt 関数は、処理すべきGoファイルがない場合に以下のように実装されていました。

if not files:
    return "no modified go files"

ここで、files リストが空の場合、関数は文字列 "no modified go files" を返していました。しかし、この gofmt 関数を呼び出す側のコードは、ファイルが存在しない場合に特定の型の戻り値を期待していなかったか、あるいは None (Pythonにおける「何もない」ことを示す値) を期待していました。文字列が返されたことで、呼び出し元がその文字列を予期しない方法で処理しようとし、結果としてクラッシュが発生したと考えられます。

修正後のコードは以下のようになります。

if not files:
    ui.status("no modified go files\\n")
    return

この変更により、以下の点が改善されました。

  1. メッセージの表示: ui.status("no modified go files\\n") を使用して、ユーザーに対して「変更されたGoファイルがない」という情報を適切に表示するようになりました。これは、単に値を返すのではなく、ユーザーインターフェースを通じて状況を伝えるためのより適切な方法です。
  2. 戻り値の変更: return ステートメントのみを使用することで、関数は明示的に None を返すようになります。Pythonでは、return の後に何も指定しない場合、関数は None を返します。これにより、呼び出し元が予期しない型の値を受け取ることがなくなり、クラッシュが回避されます。

この修正は、動的型付け言語における堅牢なプログラミングの重要性を示しています。関数の戻り値の型が厳密に定義されていない場合でも、呼び出し元がどのような値を期待しているかを考慮し、予期せぬ型の値を返さないようにすることが重要です。特に、エラーや特定の条件を示すために文字列を返すのではなく、例外を発生させるか、None のような特別な値を返すことが、より安全な設計とされます。

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

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

--- a/lib/codereview/codereview.py
+++ b/lib/codereview/codereview.py
@@ -1762,7 +1762,8 @@ def gofmt(ui, repo, *pats, **opts):
 	files = ChangedExistingFiles(ui, repo, pats, opts)
 	files = gofmt_required(files)
 	if not files:
-		return "no modified go files"
+		ui.status("no modified go files\\n")
+		return
 	cwd = os.getcwd()
 	files = [RelativePath(repo.root + '/' + f, cwd) for f in files]
 	try:

コアとなるコードの解説

gofmt 関数は、まず ChangedExistingFilesgofmt_required を呼び出して、整形が必要な変更済みGoファイルのリストを取得します。

変更前のコードでは、if not files: の条件が真(つまり、files リストが空で、整形すべきファイルがない)の場合に、return "no modified go files" という行が実行されていました。これは、関数が文字列を返すことを意味します。

変更後のコードでは、同じ条件が真の場合に、以下の2行が実行されます。

  1. ui.status("no modified go files\\n"): これは、codereview.py のユーザーインターフェースを通じて、"no modified go files" というメッセージを標準出力(またはそれに相当する場所)に表示します。\n は改行コードです。
  2. return: この return ステートメントは、明示的に値を指定していません。Pythonでは、このように値を指定せずに return すると、関数は None を返します。

この修正により、gofmt 関数は、整形すべきファイルがない場合に、ユーザーに状況を通知しつつ、呼び出し元に対して予期しない文字列ではなく None を返すようになりました。これにより、呼び出し元での型に関する不整合が解消され、クラッシュが防止されます。

関連リンク

  • Go言語の公式ウェブサイト: https://go.dev/
  • gofmt のドキュメント (Goコマンドの一部): https://pkg.go.dev/cmd/gofmt
  • Mercurial 公式サイト: https://www.mercurial-scm.org/
  • Go言語のコードレビュープロセスに関する古い情報 (Mercurial時代): Go言語の初期のコードレビューシステムは、Googleの内部ツールであるMondrianをベースにしており、codereview.py はそのクライアントサイドツールでした。現在ではGerritに移行しています。

参考にした情報源リンク

  • コミットハッシュ: 50231fa19f7c88834edd95836dca95267ef8bb4f
  • Go言語の公式リポジトリ (GitHub): https://github.com/golang/go
  • Go言語のコードレビューシステムに関する歴史的背景 (GoのIssueトラッカーやメーリングリストのアーカイブから情報を収集)
  • Pythonの関数の戻り値に関する一般的なドキュメント (Python公式ドキュメントなど)
  • Mercurialの基本的な使用法に関する情報 (Mercurial公式ドキュメントなど)
  • gofmt の機能とGo開発における役割に関する情報 (Go公式ブログやドキュメント)