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

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

このコミットは、Mercurial バージョン 2.1 で導入された変更に対応するため、Go プロジェクトのコードレビューツール (lib/codereview/codereview.py) を修正するものです。Mercurial 2.1 以降、コマンドの終了コードが整数である必要があるという新しい要件が課せられたため、既存のコマンドが正しく動作するように、Python のデコレータを使用してこれらのコマンドの戻り値を調整しています。

コミット

  • コミットハッシュ: bd71072eee41cdf7966805a33e509831bfccc7d9
  • 作者: Russ Cox rsc@golang.org
  • 日付: Mon Mar 12 14:39:44 2012 -0400

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

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

元コミット内容

codereview: fix for Mercurial 2.1

Mercurial: the Python of version control systems.
Python: the Mercurial of programming languages.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5777066

変更の背景

この変更は、バージョン管理システムである Mercurial がバージョン 2.1 にアップデートされたことに起因します。Mercurial の以前のバージョンでは、拡張機能によって定義されたコマンドがエラーを示すために任意の文字列を返すことが許容されていました。しかし、Mercurial 2.1 からは、コマンドは必ず整数の終了コードを返すことが義務付けられるようになりました。

Go プロジェクトのコードレビューシステムは、Mercurial の拡張機能として実装されており、そのコマンドは以前のバージョンの Mercurial の仕様に合わせて文字列を返す可能性がありました。Mercurial 2.1 にアップグレードすると、これらのコマンドが文字列を返した場合に TypeError が発生し、Mercurial がクラッシュするという問題が発生しました。このコミットは、この互換性の問題を解決し、Mercurial 2.1 以降でもコードレビューツールが正常に機能するようにするために行われました。

コミットメッセージにある「Mercurial: the Python of version control systems. Python: the Mercurial of programming languages.」という記述は、Mercurial が Python で書かれていること、そして両者が柔軟性と拡張性において似た特性を持つことを示唆するユーモラスな表現です。

前提知識の解説

Mercurial (Hg)

Mercurial は、分散型バージョン管理システム(DVCS)の一つで、Git と同様にソースコードの変更履歴を管理するために使用されます。Python で書かれており、拡張性が高いことが特徴です。ユーザーは独自のコマンドや機能を Python で記述し、Mercurial の動作をカスタマイズできます。

Mercurial 拡張機能

Mercurial は、~/.hgrc ファイルなどで設定を記述することで、Python で書かれたカスタムコマンドやフックを追加できます。これらの拡張機能は、Mercurial の内部 API を利用して、リポジトリの操作や情報の取得などを行います。

コマンドの終了コード (Exit Code)

Unix/Linux システムでは、プログラムやコマンドが実行を終了する際に、その結果を示す整数値を返します。これを終了コード(または終了ステータス)と呼びます。

  • 0: 通常、成功を示します。
  • 0以外: エラーや異常終了を示します。具体的な値はエラーの種類によって異なります。 Mercurial 2.1 では、拡張機能のコマンドもこの標準的な終了コードの規約に従うことが求められるようになりました。

Python デコレータ (Decorator)

Python のデコレータは、関数やメソッドの定義をラップし、その動作を変更または拡張するための構文です。@decorator_name の形式で関数定義の直前に記述します。デコレータは、元の関数を引数として受け取り、新しい関数(または元の関数をラップした関数)を返します。これにより、既存のコードを変更せずに機能を追加したり、共通の処理を適用したりすることができます。

このコミットでは、Mercurial コマンドの戻り値を正規化するためにデコレータが使用されています。

技術的詳細

Mercurial 2.1 での変更は、Mercurial のコマンドディスパッチメカニズムが、拡張機能から返される値の型チェックを厳格化したことにあります。以前は、コマンド関数がエラーメッセージとして文字列を返しても許容されていましたが、2.1 以降は、コマンド関数が整数(終了コード)を返すことを期待するようになりました。文字列が返された場合、Mercurial の内部で型エラー(TypeError)が発生し、プログラムが異常終了する原因となっていました。

この問題を解決するために、hgcommand という新しい Python デコレータが導入されました。このデコレータの主な役割は以下の通りです。

  1. 戻り値の正規化: デコレータは、元の Mercurial コマンド関数が返す値をインターセプトします。
  2. 型チェックと変換:
    • もし元の関数が整数を返した場合、それはそのまま終了コードとして返されます。
    • もし元の関数が None または空の文字列("")を返した場合、これは成功と見なされ、終了コード 0 が返されます。
    • もし元の関数が None でも空文字列でもない文字列を返した場合、これはエラーメッセージと見なされ、hg_util.Abort 例外を発生させます。hg_util.Abort は Mercurial のユーティリティ関数で、指定されたメッセージとともに Mercurial の実行を中断するために使用されます。これにより、Mercurial はエラーメッセージを適切に表示し、非ゼロの終了コードで終了できます。

このデコレータを既存のすべての Mercurial コマンド関数に適用することで、各コマンド関数が個別に終了コードの規約に対応する必要がなくなり、コードの重複を避け、保守性を高めています。

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

変更は lib/codereview/codereview.py ファイルに集中しています。

  1. hgcommand デコレータの追加:

    def hgcommand(f):
    	def wrapped(ui, repo, *pats, **opts):
    		err = f(ui, repo, *pats, **opts)
    		if type(err) is int:
    			return err
    		if not err:
    			return 0
    		raise hg_util.Abort(err)
    	return wrapped
    

    このデコレータは、元の関数 f をラップし、その戻り値 err の型をチェックします。整数であればそのまま返し、None や空文字列であれば 0 を返し、それ以外の文字列であれば hg_util.Abort 例外を発生させます。

  2. 既存のコマンド関数へのデコレータの適用: 以下の Mercurial コマンド関数に @hgcommand デコレータが追加されました。

    • change
    • code_login
    • clpatch
    • undo
    • release_apply
    • download
    • file
    • gofmt
    • mail
    • ps
    • pq
    • pending
    • submit
    • sync
    • upload

    例:

    @hgcommand
    def change(ui, repo, *pats, **opts):
    	"""create, edit or delete a change list
    	...
    

コアとなるコードの解説

hgcommand デコレータは、Mercurial のコマンド関数が呼び出される際に、その戻り値を統一的な形式(整数の終了コード)に変換する役割を担っています。

  • def hgcommand(f): これはデコレータの定義です。引数 f はデコレートされる元の関数(Mercurial コマンド関数)です。

  • def wrapped(ui, repo, *pats, **opts): これはデコレータが返す「ラッパー関数」です。Mercurial コマンド関数は通常、ui (ユーザーインターフェースオブジェクト)、repo (リポジトリオブジェクト)、可変長の引数 *pats、およびキーワード引数 **opts を受け取ります。ラッパー関数はこれらの引数をそのまま元の関数 f に渡します。

  • err = f(ui, repo, *pats, **opts) ここで元の Mercurial コマンド関数が実行され、その戻り値が err 変数に格納されます。

  • if type(err) is int: もし err が整数型であれば、それはすでに適切な終了コードであるため、そのまま return err で返されます。

  • if not err: もし errNone または空の文字列(Python ではこれらは False と評価されます)であれば、これは成功と見なされます。したがって、標準的な成功を示す終了コード 0 が返されます。

  • raise hg_util.Abort(err) 上記のどの条件にも当てはまらず、err が文字列(かつ空ではない)である場合、これはエラーメッセージと見なされます。この場合、hg_util.Abort(err) 例外が送出されます。hg_util.Abort は Mercurial の内部で定義されている例外クラスで、この例外が捕捉されると、Mercurial は err の内容をエラーメッセージとして表示し、非ゼロの終了コードで終了します。これにより、Mercurial 2.1 の新しい要件を満たしつつ、以前のバージョンで文字列を返していたコマンドの動作をエミュレートしています。

このデコレータを各コマンド関数に適用することで、Mercurial 2.1 の厳格な終了コード要件に、最小限のコード変更で対応しています。

関連リンク

参考にした情報源リンク

このコミットは、Mercurial バージョン 2.1 で導入された変更に対応するため、Go プロジェクトのコードレビューツール (lib/codereview/codereview.py) を修正するものです。Mercurial 2.1 以降、コマンドの終了コードが整数である必要があるという新しい要件が課せられたため、既存のコマンドが正しく動作するように、Python のデコレータを使用してこれらのコマンドの戻り値を調整しています。

コミット

  • コミットハッシュ: bd71072eee41cdf7966805a33e509831bfccc7d9
  • 作者: Russ Cox rsc@golang.org
  • 日付: Mon Mar 12 14:39:44 2012 -0400

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

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

元コミット内容

codereview: fix for Mercurial 2.1

Mercurial: the Python of version control systems.
Python: the Mercurial of programming languages.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5777066

変更の背景

この変更は、バージョン管理システムである Mercurial がバージョン 2.1 にアップデートされたことに起因します。Mercurial の以前のバージョンでは、拡張機能によって定義されたコマンドがエラーを示すために任意の文字列を返すことが許容されていました。しかし、Mercurial 2.1 からは、コマンドは必ず整数の終了コードを返すことが義務付けられるようになりました。

Go プロジェクトのコードレビューシステムは、Mercurial の拡張機能として実装されており、そのコマンドは以前のバージョンの Mercurial の仕様に合わせて文字列を返す可能性がありました。Mercurial 2.1 にアップグレードすると、これらのコマンドが文字列を返した場合に TypeError が発生し、Mercurial がクラッシュするという問題が発生しました。このコミットは、この互換性の問題を解決し、Mercurial 2.1 以降でもコードレビューツールが正常に機能するようにするために行われました。

コミットメッセージにある「Mercurial: the Python of version control systems. Python: the Mercurial of programming languages.」という記述は、Mercurial が Python で書かれていること、そして両者が柔軟性と拡張性において似た特性を持つことを示唆するユーモラスな表現です。

前提知識の解説

Mercurial (Hg)

Mercurial は、分散型バージョン管理システム(DVCS)の一つで、Git と同様にソースコードの変更履歴を管理するために使用されます。Python で書かれており、拡張性が高いことが特徴です。ユーザーは独自のコマンドや機能を Python で記述し、Mercurial の動作をカスタマイズできます。

Mercurial 拡張機能

Mercurial は、~/.hgrc ファイルなどで設定を記述することで、Python で書かれたカスタムコマンドやフックを追加できます。これらの拡張機能は、Mercurial の内部 API を利用して、リポジトリの操作や情報の取得などを行います。

コマンドの終了コード (Exit Code)

Unix/Linux システムでは、プログラムやコマンドが実行を終了する際に、その結果を示す整数値を返します。これを終了コード(または終了ステータス)と呼びます。

  • 0: 通常、成功を示します。
  • 0以外: エラーや異常終了を示します。具体的な値はエラーの種類によって異なります。 Mercurial 2.1 では、拡張機能のコマンドもこの標準的な終了コードの規約に従うことが求められるようになりました。

Python デコレータ (Decorator)

Python のデコレータは、関数やメソッドの定義をラップし、その動作を変更または拡張するための構文です。@decorator_name の形式で関数定義の直前に記述します。デコレータは、元の関数を引数として受け取り、新しい関数(または元の関数をラップした関数)を返します。これにより、既存のコードを変更せずに機能を追加したり、共通の処理を適用したりすることができます。

このコミットでは、Mercurial コマンドの戻り値を正規化するためにデコレータが使用されています。

技術的詳細

Mercurial 2.1 での変更は、Mercurial のコマンドディスパッチメカニズムが、拡張機能から返される値の型チェックを厳格化したことにあります。以前は、コマンド関数がエラーメッセージとして文字列を返しても許容されていましたが、2.1 以降は、コマンド関数が整数(終了コード)を返すことを期待するようになりました。文字列が返された場合、Mercurial の内部で型エラー(TypeError)が発生し、プログラムが異常終了する原因となっていました。

この問題を解決するために、hgcommand という新しい Python デコレータが導入されました。このデコレータの主な役割は以下の通りです。

  1. 戻り値の正規化: デコレータは、元の Mercurial コマンド関数が返す値をインターセプトします。
  2. 型チェックと変換:
    • もし元の関数が整数を返した場合、それはそのまま終了コードとして返されます。
    • もし元の関数が None または空の文字列("")を返した場合、これは成功と見なされ、終了コード 0 が返されます。
    • もし元の関数が None でも空文字列でもない文字列を返した場合、これはエラーメッセージと見なされ、hg_util.Abort 例外を発生させます。hg_util.Abort は Mercurial のユーティリティ関数で、指定されたメッセージとともに Mercurial の実行を中断するために使用されます。これにより、Mercurial はエラーメッセージを適切に表示し、非ゼロの終了コードで終了できます。

このデコレータを既存のすべての Mercurial コマンド関数に適用することで、各コマンド関数が個別に終了コードの規約に対応する必要がなくなり、コードの重複を避け、保守性を高めています。

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

変更は lib/codereview/codereview.py ファイルに集中しています。

  1. hgcommand デコレータの追加:

    def hgcommand(f):
    	def wrapped(ui, repo, *pats, **opts):
    		err = f(ui, repo, *pats, **opts)
    		if type(err) is int:
    			return err
    		if not err:
    			return 0
    		raise hg_util.Abort(err)
    	return wrapped
    

    このデコレータは、元の関数 f をラップし、その戻り値 err の型をチェックします。整数であればそのまま返し、None や空文字列であれば 0 を返し、それ以外の文字列であれば hg_util.Abort 例外を発生させます。

  2. 既存のコマンド関数へのデコレータの適用: 以下の Mercurial コマンド関数に @hgcommand デコレータが追加されました。

    • change
    • code_login
    • clpatch
    • undo
    • release_apply
    • download
    • file
    • gofmt
    • mail
    • ps
    • pq
    • pending
    • submit
    • sync
    • upload

    例:

    @hgcommand
    def change(ui, repo, *pats, **opts):
    	"""create, edit or delete a change list
    	...
    

コアとなるコードの解説

hgcommand デコレータは、Mercurial のコマンド関数が呼び出される際に、その戻り値を統一的な形式(整数の終了コード)に変換する役割を担っています。

  • def hgcommand(f): これはデコレータの定義です。引数 f はデコレートされる元の関数(Mercurial コマンド関数)です。

  • def wrapped(ui, repo, *pats, **opts): これはデコレータが返す「ラッパー関数」です。Mercurial コマンド関数は通常、ui (ユーザーインターフェースオブジェクト)、repo (リポジトリオブジェクト)、可変長の引数 *pats、およびキーワード引数 **opts を受け取ります。ラッパー関数はこれらの引数をそのまま元の関数 f に渡します。

  • err = f(ui, repo, *pats, **opts) ここで元の Mercurial コマンド関数が実行され、その戻り値が err 変数に格納されます。

  • if type(err) is int: もし err が整数型であれば、それはすでに適切な終了コードであるため、そのまま return err で返されます。

  • if not err: もし errNone または空の文字列(Python ではこれらは False と評価されます)であれば、これは成功と見なされます。したがって、標準的な成功を示す終了コード 0 が返されます。

  • raise hg_util.Abort(err) 上記のどの条件にも当てはまらず、err が文字列(かつ空ではない)である場合、これはエラーメッセージと見なされます。この場合、hg_util.Abort(err) 例外が送出されます。hg_util.Abort は Mercurial の内部で定義されている例外クラスで、この例外が捕捉されると、Mercurial は err の内容をエラーメッセージとして表示し、非ゼロの終了コードで終了します。これにより、Mercurial 2.1 の新しい要件を満たしつつ、以前のバージョンで文字列を返していたコマンドの動作をエミュレートしています。

このデコレータを各コマンド関数に適用することで、Mercurial 2.1 の厳格な終了コード要件に、最小限のコード変更で対応しています。

関連リンク

参考にした情報源リンク