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

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

このコミットは、Go言語の標準ライブラリであるflagパッケージにおける変更です。具体的には、パッケージ内部で利用されていたデフォルトのFlagSetであるcommandLineが、外部からアクセス可能なCommandLineとしてエクスポートされるように修正されています。この変更は、src/pkg/flag/export_test.gosrc/pkg/flag/flag.gosrc/pkg/flag/flag_test.goの3つのファイルに影響を与えています。

コミット

commit 6ac93e2ac8bb0e0eea58f691c949b81cb66b5d5d
Author: Rob Pike <r@golang.org>
Date:   Thu Aug 8 11:50:58 2013 +1000

    flag: export commandLine, the previously internal default FlagSet
    s/commandLine/CommandLine/g
    
    Fixes #4209.
    
    R=golang-dev, dsymonds, bradfitz
    CC=golang-dev
    https://golang.org/cl/12587043

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

https://github.com/golang/go/commit/6ac93e2ac8bb0e0eea58f691c949b81cb66b5d5d

元コミット内容

flag: export commandLine, the previously internal default FlagSet s/commandLine/CommandLine/g

Fixes #4209.

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

変更の背景

この変更は、Goのflagパッケージが提供するデフォルトのコマンドライン引数パーサーであるcommandLine(内部変数)を、外部からアクセス可能なCommandLine(エクスポートされた変数)として公開することを目的としています。コミットメッセージにあるFixes #4209が示す通り、この変更はIssue 4209に対応するものです。

Issue 4209は、flagパッケージのデフォルトのFlagSetにアクセスする方法がないという問題提起でした。flagパッケージは、グローバルな関数(例: flag.Parse(), flag.StringVar()など)を提供しており、これらは内部的にcommandLineというデフォルトのFlagSetインスタンスを操作します。しかし、このcommandLine変数はエクスポートされていなかったため、ユーザーはflagパッケージのグローバル関数を通じてしかデフォルトのFlagSetを操作できませんでした。

例えば、ユーザーが独自のFlagSetを作成し、それをデフォルトのFlagSetとして設定したい場合や、デフォルトのFlagSetの特定のプロパティ(例: エラーハンドリングの挙動)を変更したい場合、直接commandLineにアクセスできないことが制約となっていました。このコミットは、この制約を取り除き、より柔軟なflagパッケージの利用を可能にするために行われました。

前提知識の解説

Go言語におけるエクスポートとアンエクスポート

Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字であるか小文字であるかによって、その識別子がパッケージ外からアクセス可能(エクスポートされる)か、パッケージ内でのみアクセス可能(アンエクスポートされる)かが決まります。

  • エクスポートされた識別子 (Exported Identifiers): 最初の文字が大文字の識別子(例: MyVariable, MyFunction, MyType)。これらは、その識別子が定義されているパッケージをインポートした他のパッケージからアクセスできます。
  • アンエクスポートされた識別子 (Unexported Identifiers): 最初の文字が小文字の識別子(例: myVariable, myFunction, myType)。これらは、その識別子が定義されているパッケージ内でのみアクセス可能です。

このコミットでは、アンエクスポートされていたcommandLineが、エクスポートされたCommandLineに変更されています。これにより、flagパッケージをインポートする他のパッケージからCommandLine変数に直接アクセスできるようになります。

Go言語のflagパッケージ

flagパッケージは、コマンドライン引数を解析するための機能を提供します。主な機能は以下の通りです。

  • フラグの定義: flag.StringVar(), flag.IntVar(), flag.BoolVar()などの関数を使って、文字列、整数、真偽値などの型のフラグを定義できます。
  • フラグの解析: flag.Parse()を呼び出すことで、定義されたフラグとコマンドライン引数を解析し、対応する変数に値を設定します。
  • FlagSet: flagパッケージは、複数のフラグをグループ化して管理するためのFlagSet型を提供します。これにより、サブコマンドごとに異なるフラグセットを持つアプリケーションを構築できます。デフォルトでは、flagパッケージのグローバル関数は内部的に単一のデフォルトFlagSet(このコミット以前はcommandLine、以降はCommandLine)を操作します。

FlagSetExitOnErrorContinueOnError

FlagSetを作成する際に、エラーハンドリングの挙動を指定できます。

  • flag.ExitOnError: フラグの解析中にエラーが発生した場合、プログラムを終了します。これがデフォルトの挙動です。
  • flag.ContinueOnError: フラグの解析中にエラーが発生しても、プログラムを終了せずにエラーを返します。これにより、呼び出し元がエラーを処理できます。

このコミットでは、CommandLineExitOnErrorで初期化されていることが示されています。

技術的詳細

このコミットの主要な変更点は、flagパッケージの内部変数commandLineを、エクスポートされた変数CommandLineに変更したことです。

  1. 可視性の変更: commandLineは小文字で始まるため、flagパッケージの内部からのみアクセス可能でした。これをCommandLine(大文字で始まる)に変更することで、他のパッケージから直接このFlagSetインスタンスにアクセスできるようになりました。
  2. グローバル関数の変更: flagパッケージが提供するVisitAll, Visit, Lookup, Set, PrintDefaults, NFlag, Arg, NArg, Args, BoolVar, Bool, IntVar, Int, Int64Var, Int64, UintVar, Uint, Uint64Var, Uint64, StringVar, String, Float64Var, Float64, DurationVar, Duration, Var, Parse, Parsedといったグローバル関数は、これまで内部的にcommandLineを参照していました。この変更により、これらの関数はすべてCommandLineを参照するように修正されました。
  3. export_test.goの変更: テスト用のヘルパー関数であるResetForTestingも、内部でcommandLineを操作していましたが、これもCommandLineを使用するように変更されました。また、以前はcommandLineを返すためのエクスポートされた関数CommandLine()が存在しましたが、CommandLine変数自体がエクスポートされたため、この関数は不要となり削除されました。
  4. テストコードの変更: flag_test.go内のテストコードも、CommandLine()関数を呼び出す代わりに、直接CommandLine変数にアクセスするように修正されています。

この変更により、ユーザーは以下のような操作が可能になります。

  • flag.CommandLine.SetOutput(os.Stdout)のように、デフォルトのFlagSetの出力先を変更する。
  • flag.CommandLine.Usage = myCustomUsageのように、デフォルトのFlagSetのヘルプメッセージ表示関数をカスタマイズする。
  • flag.CommandLine.ErrorHandling = flag.ContinueOnErrorのように、デフォルトのFlagSetのエラーハンドリングの挙動を変更する。

これは、flagパッケージの柔軟性を高め、より高度なコマンドラインアプリケーションの構築を容易にするための重要な改善です。

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

src/pkg/flag/export_test.go

--- a/src/pkg/flag/export_test.go
+++ b/src/pkg/flag/export_test.go
@@ -12,11 +12,6 @@ import "os"
 // After calling ResetForTesting, parse errors in flag handling will not
 // exit the program.
 func ResetForTesting(usage func()) {
-	commandLine = NewFlagSet(os.Args[0], ContinueOnError)
+	CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
 	Usage = usage
 }
-
-// CommandLine returns the default FlagSet.
-func CommandLine() *FlagSet {
-	return commandLine
-}

src/pkg/flag/flag.go

--- a/src/pkg/flag/flag.go
+++ b/src/pkg/flag/flag.go
@@ -322,7 +322,7 @@ func (f *FlagSet) VisitAll(fn func(*Flag)) {
 // VisitAll visits the command-line flags in lexicographical order, calling
 // fn for each.  It visits all flags, even those not set.
 func VisitAll(fn func(*Flag)) {
-	commandLine.VisitAll(fn)
+	CommandLine.VisitAll(fn)
 }
 
 // Visit visits the flags in lexicographical order, calling fn for each.
@@ -336,7 +336,7 @@ func (f *FlagSet) Visit(fn func(*Flag)) {
 // Visit visits the command-line flags in lexicographical order, calling fn
 // for each.  It visits only those flags that have been set.
 func Visit(fn func(*Flag)) {
-	commandLine.Visit(fn)
+	CommandLine.Visit(fn)
 }
 
 // Lookup returns the Flag structure of the named flag, returning nil if none exists.
@@ -347,7 +347,7 @@ func (f *FlagSet) Lookup(name string) *Flag {
 // Lookup returns the Flag structure of the named command-line flag,
 // returning nil if none exists.
 func Lookup(name string) *Flag {
-	return commandLine.formal[name]
+	return CommandLine.formal[name]
 }
 
 // Set sets the value of the named flag.
@@ -369,7 +369,7 @@ func (f *FlagSet) Set(name, value string) error {
 
 // Set sets the value of the named command-line flag.
 func Set(name, value string) error {
-	return commandLine.Set(name, value)
+	return CommandLine.Set(name, value)
 }
 
 // PrintDefaults prints, to standard error unless configured
@@ -387,7 +387,7 @@ func (f *FlagSet) PrintDefaults() {
 
 // PrintDefaults prints to standard error the default values of all defined command-line flags.
 func PrintDefaults() {
-	commandLine.PrintDefaults()
+	CommandLine.PrintDefaults()
 }
 
 // defaultUsage is the default function to print a usage message.
@@ -400,7 +400,7 @@ func defaultUsage(f *FlagSet) {
 	f.PrintDefaults()
 }
 
-// NOTE: Usage is not just defaultUsage(commandLine)
+// NOTE: Usage is not just defaultUsage(CommandLine)
 // because it serves (via godoc flag Usage) as the example
 // for how to write your own usage function.
 
@@ -415,7 +415,7 @@ var Usage = func() {
 func (f *FlagSet) NFlag() int { return len(f.actual) }
 
 // NFlag returns the number of command-line flags that have been set.
-func NFlag() int { return len(commandLine.actual) }
+func NFlag() int { return len(CommandLine.actual) }
 
 // Arg returns the i'th argument.  Arg(0) is the first remaining argument
 // after flags have been processed.
@@ -429,20 +429,20 @@ func (f *FlagSet) Arg(i int) string {
 // Arg returns the i'th command-line argument.  Arg(0) is the first remaining argument
 // after flags have been processed.
 func Arg(i int) string {
-	return commandLine.Arg(i)
+	return CommandLine.Arg(i)
 }
 
 // NArg is the number of arguments remaining after flags have been processed.
 func (f *FlagSet) NArg() int { return len(f.args) }
 
 // NArg is the number of arguments remaining after flags have been processed.
-func NArg() int { return len(commandLine.args) }
+func NArg() int { return len(CommandLine.args) }
 
 // Args returns the non-flag arguments.
 func (f *FlagSet) Args() []string { return f.args }
 
 // Args returns the non-flag command-line arguments.
-func Args() []string { return commandLine.args }
+func Args() []string { return CommandLine.args }
 
 // BoolVar defines a bool flag with specified name, default value, and usage string.
 // The argument p points to a bool variable in which to store the value of the flag.
@@ -453,7 +453,7 @@ func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) {
 // BoolVar defines a bool flag with specified name, default value, and usage string.
 // The argument p points to a bool variable in which to store the value of the flag.
 func BoolVar(p *bool, name string, value bool, usage string) {
-	commandLine.Var(newBoolValue(value, p), name, usage)
+	CommandLine.Var(newBoolValue(value, p), name, usage)
 }
 
 // Bool defines a bool flag with specified name, default value, and usage string.
@@ -467,7 +467,7 @@ func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
 // Bool defines a bool flag with specified name, default value, and usage string.
 // The return value is the address of a bool variable that stores the value of the flag.
 func Bool(name string, value bool, usage string) *bool {
-	return commandLine.Bool(name, value, usage)
+	return CommandLine.Bool(name, value, usage)
 }
 
 // IntVar defines an int flag with specified name, default value, and usage string.
@@ -479,7 +479,7 @@ func (f *FlagSet) IntVar(p *int, name string, value int, usage string) {
 // IntVar defines an int flag with specified name, default value, and usage string.
 // The argument p points to an int variable in which to store the value of the flag.
 func IntVar(p *int, name string, value int, usage string) {
-	commandLine.Var(newIntValue(value, p), name, usage)
+	CommandLine.Var(newIntValue(value, p), name, usage)
 }
 
 // Int defines an int flag with specified name, default value, and usage string.
@@ -493,7 +493,7 @@ func (f *FlagSet) Int(name string, value int, usage string) *int {
 // Int defines an int flag with specified name, default value, and usage string.
 // The return value is the address of an int variable that stores the value of the flag.
 func Int(name string, value int, usage string) *int {
-	return commandLine.Int(name, value, usage)
+	return CommandLine.Int(name, value, usage)
 }
 
 // Int64Var defines an int64 flag with specified name, default value, and usage string.
@@ -505,7 +505,7 @@ func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) {
 // Int64Var defines an int64 flag with specified name, default value, and usage string.
 // The argument p points to an int64 variable in which to store the value of the flag.
 func Int64Var(p *int64, name string, value int64, usage string) {
-	commandLine.Var(newInt64Value(value, p), name, usage)
+	CommandLine.Var(newInt64Value(value, p), name, usage)
 }
 
 // Int64 defines an int64 flag with specified name, default value, and usage string.
@@ -519,7 +519,7 @@ func (f *FlagSet) Int64(name string, value int64, usage string) *int64 {
 // Int64 defines an int64 flag with specified name, default value, and usage string.
 // The return value is the address of an int64 variable that stores the value of the flag.
 func Int64(name string, value int64, usage string) *int64 {
-	return commandLine.Int64(name, value, usage)
+	return CommandLine.Int64(name, value, usage)
 }
 
 // UintVar defines a uint flag with specified name, default value, and usage string.
@@ -531,7 +531,7 @@ func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) {
 // UintVar defines a uint flag with specified name, default value, and usage string.
 // The argument p points to a uint  variable in which to store the value of the flag.
 func UintVar(p *uint, name string, value uint, usage string) {
-	commandLine.Var(newUintValue(value, p), name, usage)
+	CommandLine.Var(newUintValue(value, p), name, usage)
 }
 
 // Uint defines a uint flag with specified name, default value, and usage string.
@@ -545,7 +545,7 @@ func (f *FlagSet) Uint(name string, value uint, usage string) *uint {
 // Uint defines a uint flag with specified name, default value, and usage string.
 // The return value is the address of a uint  variable that stores the value of the flag.
 func Uint(name string, value uint, usage string) *uint {
-	return commandLine.Uint(name, value, usage)
+	return CommandLine.Uint(name, value, usage)
 }
 
 // Uint64Var defines a uint64 flag with specified name, default value, and usage string.
@@ -557,7 +557,7 @@ func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string)\
 // Uint64Var defines a uint64 flag with specified name, default value, and usage string.
 // The argument p points to a uint64 variable in which to store the value of the flag.
 func Uint64Var(p *uint64, name string, value uint64, usage string) {
-	commandLine.Var(newUint64Value(value, p), name, usage)
+	CommandLine.Var(newUint64Value(value, p), name, usage)
 }
 
 // Uint64 defines a uint64 flag with specified name, default value, and usage string.
@@ -571,7 +571,7 @@ func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 {
 // Uint64 defines a uint64 flag with specified name, default value, and usage string.
 // The return value is the address of a uint64 variable that stores the value of the flag.
 func Uint64(name string, value uint64, usage string) *uint64 {
-	return commandLine.Uint64(name, value, usage)
+	return CommandLine.Uint64(name, value, usage)
 }
 
 // StringVar defines a string flag with specified name, default value, and usage string.
@@ -583,7 +583,7 @@ func (f *FlagSet) StringVar(p *string, name string, value string, usage string)\
 // StringVar defines a string flag with specified name, default value, and usage string.
 // The argument p points to a string variable in which to store the value of the flag.
 func StringVar(p *string, name string, value string, usage string) {
-	commandLine.Var(newStringValue(value, p), name, usage)
+	CommandLine.Var(newStringValue(value, p), name, usage)
 }
 
 // String defines a string flag with specified name, default value, and usage string.
@@ -597,7 +597,7 @@ func (f *FlagSet) String(name string, value string, usage string) *string {
 // String defines a string flag with specified name, default value, and usage string.
 // The return value is the address of a string variable that stores the value of the flag.
 func String(name string, value string, usage string) *string {
-	return commandLine.String(name, value, usage)
+	return CommandLine.String(name, value, usage)
 }
 
 // Float64Var defines a float64 flag with specified name, default value, and usage string.
@@ -609,7 +609,7 @@ func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage strin
 // Float64Var defines a float64 flag with specified name, default value, and usage string.
 // The argument p points to a float64 variable in which to store the value of the flag.
 func Float64Var(p *float64, name string, value float64, usage string) {
-	commandLine.Var(newFloat64Value(value, p), name, usage)
+	CommandLine.Var(newFloat64Value(value, p), name, usage)
 }
 
 // Float64 defines a float64 flag with specified name, default value, and usage string.
@@ -623,7 +623,7 @@ func (f *FlagSet) Float64(name string, value float64, usage string) *float64 {
 // Float64 defines a float64 flag with specified name, default value, and usage string.
 // The return value is the address of a float64 variable that stores the value of the flag.
 func Float64(name string, value float64, usage string) *float64 {
-	return commandLine.Float64(name, value, usage)
+	return CommandLine.Float64(name, value, usage)
 }
 
 // DurationVar defines a time.Duration flag with specified name, default value, and usage string.
@@ -635,7 +635,7 @@ func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration
 // DurationVar defines a time.Duration flag with specified name, default value, and usage string.
 // The argument p points to a time.Duration variable in which to store the value of the flag.
 func DurationVar(p *time.Duration, name string, value time.Duration, usage string) {
-	commandLine.Var(newDurationValue(value, p), name, usage)
+	CommandLine.Var(newDurationValue(value, p), name, usage)
 }
 
 // Duration defines a time.Duration flag with specified name, default value, and usage string.
@@ -649,7 +649,7 @@ func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time
 // Duration defines a time.Duration flag with specified name, default value, and usage string.
 // The return value is the address of a time.Duration variable that stores the value of the flag.
 func Duration(name string, value time.Duration, usage string) *time.Duration {
-	return commandLine.Duration(name, value, usage)
+	return CommandLine.Duration(name, value, usage)
 }
 
 // Var defines a flag with the specified name and usage string. The type and
@@ -685,9 +685,9 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
 // of strings by giving the slice the methods of Value; in particular, Set would
 // decompose the comma-separated string into the slice.
 func Var(value Value, name string, usage string) {
-	commandLine.Var(value, name, usage)
+	CommandLine.Var(value, name, usage)
 }
 
 // failf prints to standard error a formatted error and usage message and
@@ -698,9 +698,9 @@ func (f *FlagSet) failf(format string, a ...interface{}) error {
 }
 
 // usage calls the Usage method for the flag set, or the usage function if
-// the flag set is commandLine.\n+// the flag set is CommandLine.
+// the flag set is CommandLine.
 func (f *FlagSet) usage() {
-	if f == commandLine {
+	if f == CommandLine {
 		Usage()
 	} else if f.Usage == nil {
 		defaultUsage(f)
@@ -816,17 +816,19 @@ func (f *FlagSet) Parsed() bool {
 // Parse parses the command-line flags from os.Args[1:].  Must be called
 // after all flags are defined and before flags are accessed by the program.
 func Parse() {
-	// Ignore errors; commandLine is set for ExitOnError.
-	commandLine.Parse(os.Args[1:])
+	// Ignore errors; CommandLine is set for ExitOnError.
+	CommandLine.Parse(os.Args[1:])
 }
 
 // Parsed returns true if the command-line flags have been parsed.
 func Parsed() bool {
-	return commandLine.Parsed()
+	return CommandLine.Parsed()
 }
 
-// The default set of command-line flags, parsed from os.Args.
-var commandLine = NewFlagSet(os.Args[0], ExitOnError)
+// CommandLine is the default set of command-line flags, parsed from os.Args.
+// The top-level functions such as BoolVar, Arg, and on are wrappers for the
+// methods of CommandLine.
+var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
 
 // NewFlagSet returns a new, empty flag set with the specified name and
 // error handling property.

src/pkg/flag/flag_test.go

--- a/src/pkg/flag/flag_test.go
+++ b/src/pkg/flag/flag_test.go
@@ -139,7 +139,7 @@ func TestGet(t *testing.T) {
 func TestUsage(t *testing.T) {
 	called := false
 	ResetForTesting(func() { called = true })
-	if CommandLine().Parse([]string{"-x"}) == nil {
+	if CommandLine.Parse([]string{"-x"}) == nil {
 		t.Error("parse did not fail for unknown flag")
 	}
 	if !called {
@@ -215,7 +215,7 @@ func testParse(f *FlagSet, t *testing.T) {
 
 func TestParse(t *testing.T) {
 	ResetForTesting(func() { t.Error("bad parse") })
-	testParse(CommandLine(), t)
+	testParse(CommandLine, t)
 }
 
 func TestFlagSetParse(t *testing.T) {
@@ -311,7 +311,7 @@ func TestChangingArgs(t *testing.T) {
 	defer func() { os.Args = oldArgs }()
 	os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
 	before := Bool("before", false, "")
-	if err := CommandLine().Parse(os.Args[1:]); err != nil {
+	if err := CommandLine.Parse(os.Args[1:]); err != nil {
 		t.Fatal(err)
 	}
 	cmd := Arg(0)

コアとなるコードの解説

このコミットのコアとなる変更は、flagパッケージ全体でcommandLineというアンエクスポートされた変数名をCommandLineというエクスポートされた変数名に一括置換している点です。

  1. src/pkg/flag/export_test.go:

    • ResetForTesting関数内で、commandLineの代わりにCommandLineが使用されるように変更されています。これは、テスト時にデフォルトのFlagSetをリセットする際に、新しいエクスポートされた変数を使用するためです。
    • 以前はcommandLineを返すためのエクスポートされた関数CommandLine()が存在しましたが、CommandLine変数自体がエクスポートされたため、この関数は冗長となり削除されました。
  2. src/pkg/flag/flag.go:

    • flagパッケージのほとんどのグローバル関数(VisitAll, Visit, Lookup, Set, PrintDefaults, NFlag, Arg, NArg, Args, BoolVar, Bool, IntVar, Int, Int64Var, Int64, UintVar, Uint, Uint64Var, Uint64, StringVar, String, Float64Var, Float64, DurationVar, Duration, Var, Parse, Parsed)は、内部的にデフォルトのFlagSetを操作します。これらの関数内でcommandLineへの参照がすべてCommandLineに置き換えられています。
    • flag.goのファイルの末尾にあるcommandLine変数の宣言が、CommandLineに変更され、コメントも更新されています。これにより、CommandLineがデフォルトのコマンドラインフラグセットであり、トップレベルの関数がそのメソッドのラッパーであることが明確に示されています。
    • usage関数内の比較もf == commandLineからf == CommandLineに変更されています。
  3. src/pkg/flag/flag_test.go:

    • テストコード内でCommandLine()関数を呼び出していた箇所が、直接CommandLine変数にアクセスするように変更されています。これは、CommandLine()関数が削除されたため、テストコードもそれに合わせて修正する必要があったためです。

これらの変更により、flagパッケージのユーザーは、デフォルトのFlagSetに直接アクセスし、その挙動をより細かく制御できるようになりました。これは、flagパッケージの柔軟性と使いやすさを向上させる重要な改善です。

関連リンク

参考にした情報源リンク