Skip to content

Commit 91c4515

Browse files
committed
Add FlagSet.DisableBuiltinHelp toggle
This commit adds a toggle to disable the handling of --help and -h flags.
1 parent 5ccba14 commit 91c4515

File tree

3 files changed

+116
-13
lines changed

3 files changed

+116
-13
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* [Unknown flags](#unknown-flags)
2323
* [Custom flag types in usage](#custom-flag-types-in-usage)
2424
* [Disable printing default value](#disable-printing-default-value)
25+
* [Disable built-in help flags](#disable-built-in-help-flags)
2526

2627
## Installation
2728

@@ -342,3 +343,14 @@ flag.Lookup("in").DisablePrintDefault = true
342343
``` plain
343344
--in int help message
344345
```
346+
347+
### Disable built-in help flags
348+
349+
Normally pflag will handle `--help` and `-h` when the flags aren't explicitly defined.
350+
351+
If for some reason there is a need to capture the error returned in this condition, it
352+
is possible to disable this built-in handling.
353+
354+
``` go
355+
myFlagSet.DisableBuiltinHelp = true
356+
```

flag.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ type FlagSet struct {
5454
// ParseErrorsWhitelist is used to configure a whitelist of errors
5555
ParseErrorsWhitelist ParseErrorsWhitelist
5656

57+
// DisableBuiltinHelp toggles the built-in convention of handling -h and --help
58+
DisableBuiltinHelp bool
59+
5760
name string
5861
parsed bool
5962
actual map[NormalizedName]*Flag
@@ -983,7 +986,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (outArgs [
983986

984987
if !exists || (flag != nil && flag.ShorthandOnly) {
985988
switch {
986-
case !exists && name == "help":
989+
case !exists && name == "help" && !f.DisableBuiltinHelp:
987990
f.usage()
988991
err = ErrHelp
989992
return
@@ -1034,7 +1037,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
10341037
flag, exists := f.shorthands[char]
10351038
if !exists {
10361039
switch {
1037-
case char == 'h':
1040+
case char == 'h' && !f.DisableBuiltinHelp:
10381041
f.usage()
10391042
err = ErrHelp
10401043
return

flag_test.go

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -971,42 +971,130 @@ func TestChangingArgs(t *testing.T) {
971971

972972
// Test that -help invokes the usage message and returns ErrHelp.
973973
func TestHelp(t *testing.T) {
974+
974975
var helpCalled = false
975976
fs := NewFlagSet("help test", ContinueOnError)
976977
fs.Usage = func() { helpCalled = true }
978+
977979
var flag bool
978980
fs.BoolVar(&flag, "flag", false, "regular flag")
981+
979982
// Regular flag invocation should work
980983
err := fs.Parse([]string{"--flag=true"})
981984
if err != nil {
982985
t.Fatal("expected no error; got ", err)
983986
}
984987
if !flag {
985-
t.Error("flag was not set by --flag")
988+
t.Fatal("flag was not set by --flag")
986989
}
987990
if helpCalled {
988-
t.Error("help called for regular flag")
991+
t.Fatal("help called for regular flag")
989992
helpCalled = false // reset for next test
990993
}
994+
991995
// Help flag should work as expected.
992-
err = fs.Parse([]string{"--help"})
993-
if err == nil {
994-
t.Fatal("error expected")
996+
for _, f := range []string{"--help", "-h", "-help", "-helpxyz", "-hxyz"} {
997+
err = fs.Parse([]string{f})
998+
if err == nil {
999+
t.Fatalf("while passing %s, error expected\n", f)
1000+
}
1001+
if err != ErrHelp {
1002+
t.Fatalf("while passing %s, expected ErrHelp; got %s\n", f, err)
1003+
}
1004+
if !helpCalled {
1005+
t.Fatalf("while passing %s, help was not called\n", f)
1006+
}
1007+
helpCalled = false
1008+
}
1009+
1010+
// Help flag should not work when disabled
1011+
fs.DisableBuiltinHelp = true
1012+
for _, f := range []string{"--help", "-h"} {
1013+
err := fs.Parse([]string{f})
1014+
if err == nil {
1015+
t.Fatalf("while passing %s, error expected", f)
1016+
}
1017+
if err.Error() != "unknown flag: --help" &&
1018+
err.Error() != "unknown shorthand flag: 'h' in -h" {
1019+
1020+
t.Fatalf("while passing %s, unknown flag error expected, got %s\n", f, err)
1021+
}
1022+
if !helpCalled {
1023+
// Help should be triggered because this is an unknown, but not because the help flag was called
1024+
t.Fatalf("while passing %s, help was not called\n", f)
1025+
}
9951026
}
996-
if err != ErrHelp {
997-
t.Fatal("expected ErrHelp; got ", err)
1027+
helpCalled = false
1028+
// ... when disabled, any other shorthands should trigger an error
1029+
for _, f := range []string{"-help", "-helpxyz", "-hxyz"} {
1030+
err = fs.Parse([]string{f})
1031+
if err == nil {
1032+
t.Fatalf("while passing %s, error expected\n", f)
1033+
}
9981034
}
999-
if !helpCalled {
1000-
t.Fatal("help was not called")
1035+
helpCalled = false
1036+
fs.ParseErrorsWhitelist.UnknownFlags = true
1037+
for _, f := range []string{"--help", "-h"} {
1038+
err := fs.Parse([]string{f})
1039+
t.Logf("help called: %v\n", helpCalled)
1040+
if err != nil {
1041+
t.Fatalf("while passing %s, error not expected, got %s\n", f, err)
1042+
}
1043+
if helpCalled {
1044+
// Help should be triggered because this is an unknown, but not because the help flag was called
1045+
t.Fatalf("while passing %s, help was not called\n", f)
1046+
}
10011047
}
1048+
fs.DisableBuiltinHelp = false
1049+
10021050
// If we define a help flag, that should override.
10031051
var help bool
1004-
fs.BoolVar(&help, "help", false, "help flag")
1005-
helpCalled = false
1052+
fs.BoolVarP(&help, "help", "h", false, "help flag")
10061053
err = fs.Parse([]string{"--help"})
10071054
if err != nil {
10081055
t.Fatal("expected no error for defined --help; got ", err)
10091056
}
1057+
if !help {
1058+
t.Fatal("help should be true for defined --help")
1059+
}
1060+
if helpCalled {
1061+
t.Fatal("help was called; should not have been for defined help flag")
1062+
}
1063+
help = false
1064+
// ... including the shorthand
1065+
err = fs.Parse([]string{"-h"})
1066+
if err != nil {
1067+
t.Fatal("expected no error for defined -h; got ", err)
1068+
}
1069+
if !help {
1070+
t.Fatal("help should be true for defined -h")
1071+
}
1072+
if helpCalled {
1073+
t.Fatal("help was called; should not have been for defined help flag")
1074+
}
1075+
help = false
1076+
1077+
// If we define a help flag, that should override when the built in help flag is disabled.
1078+
fs.DisableBuiltinHelp = true
1079+
err = fs.Parse([]string{"--help"})
1080+
if err != nil {
1081+
t.Fatal("expected no error for defined --help; got ", err)
1082+
}
1083+
if !help {
1084+
t.Fatal("help should be true for defined --help")
1085+
}
1086+
if helpCalled {
1087+
t.Fatal("help was called; should not have been for defined help flag")
1088+
}
1089+
help = false
1090+
// ... including the shorthand
1091+
err = fs.Parse([]string{"-h"})
1092+
if err != nil {
1093+
t.Fatal("expected no error for defined -h; got ", err)
1094+
}
1095+
if !help {
1096+
t.Fatal("help should be true for defined -h")
1097+
}
10101098
if helpCalled {
10111099
t.Fatal("help was called; should not have been for defined help flag")
10121100
}

0 commit comments

Comments
 (0)