Skip to content

Commit

Permalink
Merge pull request ogier#9 from eparis/deprecated-flag
Browse files Browse the repository at this point in the history
Ability to mark flags as deprecated
  • Loading branch information
eparis committed Apr 29, 2015
2 parents 60d4c37 + 8824ec2 commit 8e80315
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 2 deletions.
3 changes: 2 additions & 1 deletion bool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ func TestImplicitFalse(t *testing.T) {
func TestInvalidValue(t *testing.T) {
var tristate triStateValue
f := setUpFlagSet(&tristate)
err := f.Parse([]string{"--tristate=invalid"})
args := []string{"--tristate=invalid"}
_, err := parseReturnStderr(t, f, args)
if err == nil {
t.Fatal("expected an error but did not get any, tristate has value", tristate)
}
Expand Down
25 changes: 24 additions & 1 deletion flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type Flag struct {
Value Value // value as set
DefValue string // default value (as text); for usage message
Changed bool // If the user set the value (or if left to default)
Deprecated string // If this flag is deprecated, this string is the new or now thing to use
Annotations map[string][]string // used by cobra.Command bash autocomple code
}

Expand Down Expand Up @@ -243,6 +244,16 @@ func (f *FlagSet) lookup(name normalizedName) *Flag {
return f.formal[name]
}

// Mark a flag deprecated in your program
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name)
if flag == nil {
return fmt.Errorf("flag %q does not exist", name)
}
flag.Deprecated = usageMessage
return nil
}

// Lookup returns the Flag structure of the named command-line flag,
// returning nil if none exists.
func Lookup(name string) *Flag {
Expand All @@ -264,7 +275,10 @@ func (f *FlagSet) Set(name, value string) error {
f.actual = make(map[normalizedName]*Flag)
}
f.actual[normalName] = flag
f.lookup(normalName).Changed = true
flag.Changed = true
if len(flag.Deprecated) > 0 {
fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
}
return nil
}

Expand All @@ -277,6 +291,9 @@ func Set(name, value string) error {
// otherwise, the default values of all defined flags in the set.
func (f *FlagSet) PrintDefaults() {
f.VisitAll(func(flag *Flag) {
if len(flag.Deprecated) > 0 {
return
}
format := "--%s=%s: %s\n"
if _, ok := flag.Value.(*stringValue); ok {
// put quotes on the value
Expand All @@ -295,6 +312,9 @@ func (f *FlagSet) FlagUsages() string {
x := new(bytes.Buffer)

f.VisitAll(func(flag *Flag) {
if len(flag.Deprecated) > 0 {
return
}
format := "--%s=%s: %s\n"
if _, ok := flag.Value.(*stringValue); ok {
// put quotes on the value
Expand Down Expand Up @@ -466,6 +486,9 @@ func (f *FlagSet) setFlag(flag *Flag, value string, origArg string) error {
}
f.actual[f.normalizeFlagName(flag.Name)] = flag
flag.Changed = true
if len(flag.Deprecated) > 0 {
fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
}
return nil
}

Expand Down
72 changes: 72 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package pflag_test
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
Expand Down Expand Up @@ -445,3 +446,74 @@ func TestTermination(t *testing.T) {
t.Errorf("expected argument %q got %q", arg2, f.Args()[1])
}
}

func TestDeprecatedFlagInDocs(t *testing.T) {
f := NewFlagSet("bob", ContinueOnError)
f.Bool("badflag", true, "always true")
f.MarkDeprecated("badflag", "use --good-flag instead")

out := new(bytes.Buffer)
f.SetOutput(out)
f.PrintDefaults()

if strings.Contains(out.String(), "badflag") {
t.Errorf("found deprecated flag in usage!")
}
}

func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) {
oldStderr := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

err := f.Parse(args)

outC := make(chan string)
// copy the output in a separate goroutine so printing can't block indefinitely
go func() {
var buf bytes.Buffer
io.Copy(&buf, r)
outC <- buf.String()
}()

w.Close()
os.Stderr = oldStderr
out := <-outC

return out, err
}

func TestDeprecatedFlagUsage(t *testing.T) {
f := NewFlagSet("bob", ContinueOnError)
f.Bool("badflag", true, "always true")
usageMsg := "use --good-flag instead"
f.MarkDeprecated("badflag", usageMsg)

args := []string{"--badflag"}
out, err := parseReturnStderr(t, f, args)
if err != nil {
t.Fatal("expected no error; got ", err)
}

if !strings.Contains(out, usageMsg) {
t.Errorf("usageMsg not printed when using a deprecated flag!")
}
}

func TestDeprecatedFlagUsageNormalized(t *testing.T) {
f := NewFlagSet("bob", ContinueOnError)
f.Bool("bad-double_flag", true, "always true")
f.SetWordSeparators([]string{"-", "_"})
usageMsg := "use --good-flag instead"
f.MarkDeprecated("bad_double-flag", usageMsg)

args := []string{"--bad_double_flag"}
out, err := parseReturnStderr(t, f, args)
if err != nil {
t.Fatal("expected no error; got ", err)
}

if !strings.Contains(out, usageMsg) {
t.Errorf("usageMsg not printed when using a deprecated flag!")
}
}

0 comments on commit 8e80315

Please sign in to comment.