Skip to content

Commit 8e80315

Browse files
committed
Merge pull request ogier#9 from eparis/deprecated-flag
Ability to mark flags as deprecated
2 parents 60d4c37 + 8824ec2 commit 8e80315

File tree

3 files changed

+98
-2
lines changed

3 files changed

+98
-2
lines changed

bool_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ func TestImplicitFalse(t *testing.T) {
156156
func TestInvalidValue(t *testing.T) {
157157
var tristate triStateValue
158158
f := setUpFlagSet(&tristate)
159-
err := f.Parse([]string{"--tristate=invalid"})
159+
args := []string{"--tristate=invalid"}
160+
_, err := parseReturnStderr(t, f, args)
160161
if err == nil {
161162
t.Fatal("expected an error but did not get any, tristate has value", tristate)
162163
}

flag.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ type Flag struct {
152152
Value Value // value as set
153153
DefValue string // default value (as text); for usage message
154154
Changed bool // If the user set the value (or if left to default)
155+
Deprecated string // If this flag is deprecated, this string is the new or now thing to use
155156
Annotations map[string][]string // used by cobra.Command bash autocomple code
156157
}
157158

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

247+
// Mark a flag deprecated in your program
248+
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
249+
flag := f.Lookup(name)
250+
if flag == nil {
251+
return fmt.Errorf("flag %q does not exist", name)
252+
}
253+
flag.Deprecated = usageMessage
254+
return nil
255+
}
256+
246257
// Lookup returns the Flag structure of the named command-line flag,
247258
// returning nil if none exists.
248259
func Lookup(name string) *Flag {
@@ -264,7 +275,10 @@ func (f *FlagSet) Set(name, value string) error {
264275
f.actual = make(map[normalizedName]*Flag)
265276
}
266277
f.actual[normalName] = flag
267-
f.lookup(normalName).Changed = true
278+
flag.Changed = true
279+
if len(flag.Deprecated) > 0 {
280+
fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
281+
}
268282
return nil
269283
}
270284

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

297314
f.VisitAll(func(flag *Flag) {
315+
if len(flag.Deprecated) > 0 {
316+
return
317+
}
298318
format := "--%s=%s: %s\n"
299319
if _, ok := flag.Value.(*stringValue); ok {
300320
// put quotes on the value
@@ -466,6 +486,9 @@ func (f *FlagSet) setFlag(flag *Flag, value string, origArg string) error {
466486
}
467487
f.actual[f.normalizeFlagName(flag.Name)] = flag
468488
flag.Changed = true
489+
if len(flag.Deprecated) > 0 {
490+
fmt.Fprintf(os.Stderr, "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated)
491+
}
469492
return nil
470493
}
471494

flag_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package pflag_test
77
import (
88
"bytes"
99
"fmt"
10+
"io"
1011
"io/ioutil"
1112
"os"
1213
"sort"
@@ -445,3 +446,74 @@ func TestTermination(t *testing.T) {
445446
t.Errorf("expected argument %q got %q", arg2, f.Args()[1])
446447
}
447448
}
449+
450+
func TestDeprecatedFlagInDocs(t *testing.T) {
451+
f := NewFlagSet("bob", ContinueOnError)
452+
f.Bool("badflag", true, "always true")
453+
f.MarkDeprecated("badflag", "use --good-flag instead")
454+
455+
out := new(bytes.Buffer)
456+
f.SetOutput(out)
457+
f.PrintDefaults()
458+
459+
if strings.Contains(out.String(), "badflag") {
460+
t.Errorf("found deprecated flag in usage!")
461+
}
462+
}
463+
464+
func parseReturnStderr(t *testing.T, f *FlagSet, args []string) (string, error) {
465+
oldStderr := os.Stderr
466+
r, w, _ := os.Pipe()
467+
os.Stderr = w
468+
469+
err := f.Parse(args)
470+
471+
outC := make(chan string)
472+
// copy the output in a separate goroutine so printing can't block indefinitely
473+
go func() {
474+
var buf bytes.Buffer
475+
io.Copy(&buf, r)
476+
outC <- buf.String()
477+
}()
478+
479+
w.Close()
480+
os.Stderr = oldStderr
481+
out := <-outC
482+
483+
return out, err
484+
}
485+
486+
func TestDeprecatedFlagUsage(t *testing.T) {
487+
f := NewFlagSet("bob", ContinueOnError)
488+
f.Bool("badflag", true, "always true")
489+
usageMsg := "use --good-flag instead"
490+
f.MarkDeprecated("badflag", usageMsg)
491+
492+
args := []string{"--badflag"}
493+
out, err := parseReturnStderr(t, f, args)
494+
if err != nil {
495+
t.Fatal("expected no error; got ", err)
496+
}
497+
498+
if !strings.Contains(out, usageMsg) {
499+
t.Errorf("usageMsg not printed when using a deprecated flag!")
500+
}
501+
}
502+
503+
func TestDeprecatedFlagUsageNormalized(t *testing.T) {
504+
f := NewFlagSet("bob", ContinueOnError)
505+
f.Bool("bad-double_flag", true, "always true")
506+
f.SetWordSeparators([]string{"-", "_"})
507+
usageMsg := "use --good-flag instead"
508+
f.MarkDeprecated("bad_double-flag", usageMsg)
509+
510+
args := []string{"--bad_double_flag"}
511+
out, err := parseReturnStderr(t, f, args)
512+
if err != nil {
513+
t.Fatal("expected no error; got ", err)
514+
}
515+
516+
if !strings.Contains(out, usageMsg) {
517+
t.Errorf("usageMsg not printed when using a deprecated flag!")
518+
}
519+
}

0 commit comments

Comments
 (0)