diff options
author | Ondřej Surý <ondrej@sury.org> | 2012-04-06 15:14:11 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2012-04-06 15:14:11 +0200 |
commit | 505c19580e0f43fe5224431459cacb7c21edd93d (patch) | |
tree | 79e2634c253d60afc0cc0b2f510dc7dcbb48497b /src/pkg/flag | |
parent | 1336a7c91e596c423a49d1194ea42d98bca0d958 (diff) | |
download | golang-505c19580e0f43fe5224431459cacb7c21edd93d.tar.gz |
Imported Upstream version 1upstream/1
Diffstat (limited to 'src/pkg/flag')
-rw-r--r-- | src/pkg/flag/Makefile | 11 | ||||
-rw-r--r-- | src/pkg/flag/example_test.go | 83 | ||||
-rw-r--r-- | src/pkg/flag/flag.go | 207 | ||||
-rw-r--r-- | src/pkg/flag/flag_test.go | 54 |
4 files changed, 274 insertions, 81 deletions
diff --git a/src/pkg/flag/Makefile b/src/pkg/flag/Makefile deleted file mode 100644 index 3408ca474..000000000 --- a/src/pkg/flag/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2009 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../Make.inc - -TARG=flag -GOFILES=\ - flag.go\ - -include ../../Make.pkg diff --git a/src/pkg/flag/example_test.go b/src/pkg/flag/example_test.go new file mode 100644 index 000000000..04a0d20ee --- /dev/null +++ b/src/pkg/flag/example_test.go @@ -0,0 +1,83 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// These examples demonstrate more intricate uses of the flag package. +package flag_test + +import ( + "errors" + "flag" + "fmt" + "strings" + "time" +) + +// Example 1: A single string flag called "species" with default value "gopher". +var species = flag.String("species", "gopher", "the species we are studying") + +// Example 2: Two flags sharing a variable, so we can have a shorthand. +// The order of initialization is undefined, so make sure both use the +// same default value. They must be set up with an init function. +var gopherType string + +func init() { + const ( + defaultGopher = "pocket" + usage = "the variety of gopher" + ) + flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage) + flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)") +} + +// Example 3: A user-defined flag type, a slice of durations. +type interval []time.Duration + +// String is the method to format the flag's value, part of the flag.Value interface. +// The String method's output will be used in diagnostics. +func (i *interval) String() string { + return fmt.Sprint(*i) +} + +// Set is the method to set the flag value, part of the flag.Value interface. +// Set's argument is a string to be parsed to set the flag. +// It's a comma-separated list, so we split it. +func (i *interval) Set(value string) error { + // If we wanted to allow the flag to be set multiple times, + // accumulating values, we would delete this if statement. + // That would permit usages such as + // -deltaT 10s -deltaT 15s + // and other combinations. + if len(*i) > 0 { + return errors.New("interval flag already set") + } + for _, dt := range strings.Split(value, ",") { + duration, err := time.ParseDuration(dt) + if err != nil { + return err + } + *i = append(*i, duration) + } + return nil +} + +// Define a flag to accumulate durations. Because it has a special type, +// we need to use the Var function and therefore create the flag during +// init. + +var intervalFlag interval + +func init() { + // Tie the command-line flag to the intervalFlag variable and + // set a usage message. + flag.Var(&intervalFlag, "deltaT", "comma-separated list of intervals to use between events") +} + +func Example() { + // All the interesting pieces are with the variables declared above, but + // to enable the flag package to see the flags defined there, one must + // execute, typically at the start of main (not init!): + // flag.Parse() + // We don't run it here because this is not a main function and + // the testing suite has already parsed the flags. +} diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index 01bbc3770..c28d0e720 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -49,6 +49,7 @@ Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. + Duration flags accept any input valid for time.ParseDuration. The default set of command-line flags is controlled by top-level functions. The FlagSet type allows one to define @@ -60,16 +61,19 @@ package flag import ( + "errors" "fmt" + "io" "os" "sort" "strconv" + "time" ) // ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. -var ErrHelp = os.NewError("flag: help requested") +var ErrHelp = errors.New("flag: help requested") -// -- Bool Value +// -- bool Value type boolValue bool func newBoolValue(val bool, p *bool) *boolValue { @@ -77,15 +81,15 @@ func newBoolValue(val bool, p *bool) *boolValue { return (*boolValue)(p) } -func (b *boolValue) Set(s string) bool { - v, err := strconv.Atob(s) +func (b *boolValue) Set(s string) error { + v, err := strconv.ParseBool(s) *b = boolValue(v) - return err == nil + return err } func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } -// -- Int Value +// -- int Value type intValue int func newIntValue(val int, p *int) *intValue { @@ -93,15 +97,15 @@ func newIntValue(val int, p *int) *intValue { return (*intValue)(p) } -func (i *intValue) Set(s string) bool { - v, err := strconv.Btoi64(s, 0) +func (i *intValue) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 64) *i = intValue(v) - return err == nil + return err } func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } -// -- Int64 Value +// -- int64 Value type int64Value int64 func newInt64Value(val int64, p *int64) *int64Value { @@ -109,15 +113,15 @@ func newInt64Value(val int64, p *int64) *int64Value { return (*int64Value)(p) } -func (i *int64Value) Set(s string) bool { - v, err := strconv.Btoi64(s, 0) +func (i *int64Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 64) *i = int64Value(v) - return err == nil + return err } func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } -// -- Uint Value +// -- uint Value type uintValue uint func newUintValue(val uint, p *uint) *uintValue { @@ -125,10 +129,10 @@ func newUintValue(val uint, p *uint) *uintValue { return (*uintValue)(p) } -func (i *uintValue) Set(s string) bool { - v, err := strconv.Btoui64(s, 0) +func (i *uintValue) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 64) *i = uintValue(v) - return err == nil + return err } func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } @@ -141,10 +145,10 @@ func newUint64Value(val uint64, p *uint64) *uint64Value { return (*uint64Value)(p) } -func (i *uint64Value) Set(s string) bool { - v, err := strconv.Btoui64(s, 0) +func (i *uint64Value) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 64) *i = uint64Value(v) - return err == nil + return err } func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } @@ -157,14 +161,14 @@ func newStringValue(val string, p *string) *stringValue { return (*stringValue)(p) } -func (s *stringValue) Set(val string) bool { +func (s *stringValue) Set(val string) error { *s = stringValue(val) - return true + return nil } func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } -// -- Float64 Value +// -- float64 Value type float64Value float64 func newFloat64Value(val float64, p *float64) *float64Value { @@ -172,19 +176,35 @@ func newFloat64Value(val float64, p *float64) *float64Value { return (*float64Value)(p) } -func (f *float64Value) Set(s string) bool { - v, err := strconv.Atof64(s) +func (f *float64Value) Set(s string) error { + v, err := strconv.ParseFloat(s, 64) *f = float64Value(v) - return err == nil + return err } func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } +// -- time.Duration Value +type durationValue time.Duration + +func newDurationValue(val time.Duration, p *time.Duration) *durationValue { + *p = val + return (*durationValue)(p) +} + +func (d *durationValue) Set(s string) error { + v, err := time.ParseDuration(s) + *d = durationValue(v) + return err +} + +func (d *durationValue) String() string { return (*time.Duration)(d).String() } + // Value is the interface to the dynamic value stored in a flag. // (The default value is represented as a string.) type Value interface { String() string - Set(string) bool + Set(string) error } // ErrorHandling defines how to handle flag parsing errors. @@ -204,11 +224,13 @@ type FlagSet struct { Usage func() name string + parsed bool actual map[string]*Flag formal map[string]*Flag args []string // arguments after flags exitOnError bool // does the program exit if there's an error? errorHandling ErrorHandling + output io.Writer // nil means stderr; use out() accessor } // A Flag represents the state of a flag. @@ -235,6 +257,19 @@ func sortFlags(flags map[string]*Flag) []*Flag { return result } +func (f *FlagSet) out() io.Writer { + if f.output == nil { + return os.Stderr + } + return f.output +} + +// SetOutput sets the destination for usage and error messages. +// If output is nil, os.Stderr is used. +func (f *FlagSet) SetOutput(output io.Writer) { + f.output = output +} + // VisitAll visits the flags in lexicographical order, calling fn for each. // It visits all flags, even those not set. func (f *FlagSet) VisitAll(fn func(*Flag)) { @@ -274,36 +309,38 @@ func Lookup(name string) *Flag { return commandLine.formal[name] } -// Set sets the value of the named flag. It returns true if the set succeeded; false if -// there is no such flag defined. -func (f *FlagSet) Set(name, value string) bool { +// Set sets the value of the named flag. +func (f *FlagSet) Set(name, value string) error { flag, ok := f.formal[name] if !ok { - return false + return fmt.Errorf("no such flag -%v", name) } - ok = flag.Value.Set(value) - if !ok { - return false + err := flag.Value.Set(value) + if err != nil { + return err + } + if f.actual == nil { + f.actual = make(map[string]*Flag) } f.actual[name] = flag - return true + return nil } -// Set sets the value of the named command-line flag. It returns true if the -// set succeeded; false if there is no such flag defined. -func Set(name, value string) bool { +// Set sets the value of the named command-line flag. +func Set(name, value string) error { return commandLine.Set(name, value) } -// PrintDefaults prints to standard error the default values of all defined flags in the set. +// PrintDefaults prints, to standard error unless configured +// otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { - f.VisitAll(func(f *Flag) { + f.VisitAll(func(flag *Flag) { format := " -%s=%s: %s\n" - if _, ok := f.Value.(*stringValue); ok { + if _, ok := flag.Value.(*stringValue); ok { // put quotes on the value format = " -%s=%q: %s\n" } - fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage) + fmt.Fprintf(f.out(), format, flag.Name, flag.DefValue, flag.Usage) }) } @@ -314,14 +351,19 @@ func PrintDefaults() { // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", f.name) + fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) f.PrintDefaults() } +// 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. + // Usage prints to standard error a usage message documenting all defined command-line flags. // The function is a variable that may be changed to point to a custom function. var Usage = func() { - defaultUsage(commandLine) + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + PrintDefaults() } // NFlag returns the number of flags that have been set. @@ -533,12 +575,38 @@ func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { return p } -// Float64 defines an int flag with specified name, default value, and usage string. +// 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) } +// 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 (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { + f.Var(newDurationValue(value, p), name, usage) +} + +// 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) +} + +// 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 (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { + p := new(time.Duration) + f.DurationVar(p, name, value, usage) + return p +} + +// 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) +} + // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the @@ -550,9 +618,12 @@ func (f *FlagSet) Var(value Value, name string, usage string) { flag := &Flag{name, usage, value, value.String()} _, alreadythere := f.formal[name] if alreadythere { - fmt.Fprintf(os.Stderr, "%s flag redefined: %s\n", f.name, name) + fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name) panic("flag redefinition") // Happens only if flags are declared with identical names } + if f.formal == nil { + f.formal = make(map[string]*Flag) + } f.formal[name] = flag } @@ -568,9 +639,9 @@ func Var(value Value, name string, usage string) { // failf prints to standard error a formatted error and usage message and // returns the error. -func (f *FlagSet) failf(format string, a ...interface{}) os.Error { +func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) - fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(f.out(), err) f.usage() return err } @@ -580,13 +651,15 @@ func (f *FlagSet) failf(format string, a ...interface{}) os.Error { func (f *FlagSet) usage() { if f == commandLine { Usage() + } else if f.Usage == nil { + defaultUsage(f) } else { f.Usage() } } // parseOne parses one flag. It returns whether a flag was seen. -func (f *FlagSet) parseOne() (bool, os.Error) { +func (f *FlagSet) parseOne() (bool, error) { if len(f.args) == 0 { return false, nil } @@ -630,8 +703,8 @@ func (f *FlagSet) parseOne() (bool, os.Error) { } if fv, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg if has_value { - if !fv.Set(value) { - f.failf("invalid boolean value %q for flag: -%s", value, name) + if err := fv.Set(value); err != nil { + f.failf("invalid boolean value %q for -%s: %v", value, name, err) } } else { fv.Set("true") @@ -646,11 +719,13 @@ func (f *FlagSet) parseOne() (bool, os.Error) { if !has_value { return false, f.failf("flag needs an argument: -%s", name) } - ok = flag.Value.Set(value) - if !ok { - return false, f.failf("invalid value %q for flag: -%s", value, name) + if err := flag.Value.Set(value); err != nil { + return false, f.failf("invalid value %q for flag -%s: %v", value, name, err) } } + if f.actual == nil { + f.actual = make(map[string]*Flag) + } f.actual[name] = flag return true, nil } @@ -659,7 +734,8 @@ func (f *FlagSet) parseOne() (bool, os.Error) { // include the command name. Must be called after all flags in the FlagSet // are defined and before flags are accessed by the program. // The return value will be ErrHelp if -help was set but not defined. -func (f *FlagSet) Parse(arguments []string) os.Error { +func (f *FlagSet) Parse(arguments []string) error { + f.parsed = true f.args = arguments for { seen, err := f.parseOne() @@ -681,6 +757,11 @@ func (f *FlagSet) Parse(arguments []string) os.Error { return nil } +// Parsed reports whether f.Parse has been called. +func (f *FlagSet) Parsed() bool { + return f.parsed +} + // 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() { @@ -688,6 +769,11 @@ func Parse() { commandLine.Parse(os.Args[1:]) } +// Parsed returns true if the command-line flags have been parsed. +func Parsed() bool { + return commandLine.Parsed() +} + // The default set of command-line flags, parsed from os.Args. var commandLine = NewFlagSet(os.Args[0], ExitOnError) @@ -696,10 +782,15 @@ var commandLine = NewFlagSet(os.Args[0], ExitOnError) func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { f := &FlagSet{ name: name, - actual: make(map[string]*Flag), - formal: make(map[string]*Flag), errorHandling: errorHandling, } - f.Usage = func() { defaultUsage(f) } return f } + +// Init sets the name and error handling property for a flag set. +// By default, the zero FlagSet uses an empty name and the +// ContinueOnError error handling policy. +func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { + f.name = name + f.errorHandling = errorHandling +} diff --git a/src/pkg/flag/flag_test.go b/src/pkg/flag/flag_test.go index 63d0a9fc8..a9561f269 100644 --- a/src/pkg/flag/flag_test.go +++ b/src/pkg/flag/flag_test.go @@ -5,21 +5,25 @@ package flag_test import ( + "bytes" . "flag" "fmt" "os" "sort" + "strings" "testing" + "time" ) var ( - test_bool = Bool("test_bool", false, "bool value") - test_int = Int("test_int", 0, "int value") - test_int64 = Int64("test_int64", 0, "int64 value") - test_uint = Uint("test_uint", 0, "uint value") - test_uint64 = Uint64("test_uint64", 0, "uint64 value") - test_string = String("test_string", "0", "string value") - test_float64 = Float64("test_float64", 0, "float64 value") + test_bool = Bool("test_bool", false, "bool value") + test_int = Int("test_int", 0, "int value") + test_int64 = Int64("test_int64", 0, "int64 value") + test_uint = Uint("test_uint", 0, "uint value") + test_uint64 = Uint64("test_uint64", 0, "uint64 value") + test_string = String("test_string", "0", "string value") + test_float64 = Float64("test_float64", 0, "float64 value") + test_duration = Duration("test_duration", 0, "time.Duration value") ) func boolString(s string) string { @@ -41,6 +45,8 @@ func TestEverything(t *testing.T) { ok = true case f.Name == "test_bool" && f.Value.String() == boolString(desired): ok = true + case f.Name == "test_duration" && f.Value.String() == desired+"s": + ok = true } if !ok { t.Error("Visit: bad value", f.Value.String(), "for", f.Name) @@ -48,7 +54,7 @@ func TestEverything(t *testing.T) { } } VisitAll(visitor) - if len(m) != 7 { + if len(m) != 8 { t.Error("VisitAll misses some flags") for k, v := range m { t.Log(k, *v) @@ -70,9 +76,10 @@ func TestEverything(t *testing.T) { Set("test_uint64", "1") Set("test_string", "1") Set("test_float64", "1") + Set("test_duration", "1s") desired = "1" Visit(visitor) - if len(m) != 7 { + if len(m) != 8 { t.Error("Visit fails after set") for k, v := range m { t.Log(k, *v) @@ -98,6 +105,9 @@ func TestUsage(t *testing.T) { } func testParse(f *FlagSet, t *testing.T) { + if f.Parsed() { + t.Error("f.Parse() = true before Parse") + } boolFlag := f.Bool("bool", false, "bool value") bool2Flag := f.Bool("bool2", false, "bool2 value") intFlag := f.Int("int", 0, "int value") @@ -106,6 +116,7 @@ func testParse(f *FlagSet, t *testing.T) { uint64Flag := f.Uint64("uint64", 0, "uint64 value") stringFlag := f.String("string", "0", "string value") float64Flag := f.Float64("float64", 0, "float64 value") + durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value") extra := "one-extra-argument" args := []string{ "-bool", @@ -116,11 +127,15 @@ func testParse(f *FlagSet, t *testing.T) { "--uint64", "25", "-string", "hello", "-float64", "2718e28", + "-duration", "2m", extra, } if err := f.Parse(args); err != nil { t.Fatal(err) } + if !f.Parsed() { + t.Error("f.Parse() = false after Parse") + } if *boolFlag != true { t.Error("bool flag should be true, is ", *boolFlag) } @@ -145,6 +160,9 @@ func testParse(f *FlagSet, t *testing.T) { if *float64Flag != 2718e28 { t.Error("float64 flag should be 2718e28, is ", *float64Flag) } + if *durationFlag != 2*time.Minute { + t.Error("duration flag should be 2m, is ", *durationFlag) + } if len(f.Args()) != 1 { t.Error("expected one argument, got", len(f.Args())) } else if f.Args()[0] != extra { @@ -168,13 +186,14 @@ func (f *flagVar) String() string { return fmt.Sprint([]string(*f)) } -func (f *flagVar) Set(value string) bool { +func (f *flagVar) Set(value string) error { *f = append(*f, value) - return true + return nil } func TestUserDefined(t *testing.T) { - flags := NewFlagSet("test", ContinueOnError) + var flags FlagSet + flags.Init("test", ContinueOnError) var v flagVar flags.Var(&v, "v", "usage") if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { @@ -189,6 +208,17 @@ func TestUserDefined(t *testing.T) { } } +func TestSetOutput(t *testing.T) { + var flags FlagSet + var buf bytes.Buffer + flags.SetOutput(&buf) + flags.Init("test", ContinueOnError) + flags.Parse([]string{"-unknown"}) + if out := buf.String(); !strings.Contains(out, "-unknown") { + t.Logf("expected output mentioning unknown; got %q", out) + } +} + // This tests that one can reset the flags. This still works but not well, and is // superseded by FlagSet. func TestChangingArgs(t *testing.T) { |