pflag
pflag copied to clipboard
Add missing compatibility for flag.boolFlag.IsBoolFlag()
In the standard flag
package the flag.boolValue
interface with its special IsBoolFlag() bool
method is honored by the parser and allows creating flag.Value
types that don't require an argument.
In pflag
creating such a flag is only possible by overwriting the pflag.Flag
's NoOptDefVal
field with "true"
or an equivalent (see strconv.ParseBool
). The behavior of custom Value
types that also implement IsBoolFlag() bool
(hence are flag.boolValue
or pflag.boolValue
) is not compatible to the flag
package, since passing no argument for the flag results in an error.
This snippet demonstrates it:
package main
import (
"flag"
"log"
"strconv"
"github.com/spf13/pflag"
)
/* straight from bool.go */
// -- bool Value
type boolValue bool
func newBoolValue(val bool, p *bool) *boolValue {
*p = val
return (*boolValue)(p)
}
func (b *boolValue) Set(s string) error {
v, err := strconv.ParseBool(s)
*b = boolValue(v)
return err
}
func (b *boolValue) Type() string {
return "bool"
}
func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) }
func (b *boolValue) IsBoolFlag() bool { return true }
func main() {
var flagValue, pflagValue, pflagValue2 bool
flagSet := flag.NewFlagSet("flag", flag.ContinueOnError)
flagSet.Var(newBoolValue(false, &flagValue), "bool", "may omit argument")
pflagSet := pflag.NewFlagSet("pflag", pflag.ContinueOnError)
pflagSet.Var(newBoolValue(false, &pflagValue), "bool", "can't omit argument")
boolFlag := pflagSet.VarPF(newBoolValue(false, &pflagValue2), "bool2", "", "has default argument if none exists")
boolFlag.NoOptDefVal = "true"
fErr := flagSet.Parse([]string{"--bool"})
pfErr := pflagSet.Parse([]string{"--bool2", "--bool"})
if (fErr == nil) != (pfErr == nil) {
log.Println("flag and pflag handle boolValue differently")
}
if !pflagValue && pflagValue2 {
log.Println("have to modify pflag.Flag value to get same behavior as with flag")
}
}
Is this an aspect that is supposed to be compatible to flag
?
I can understand that it makes sense to place the possibility to omit an argument to the Flag
type and not the Value
type.
But I also think it's good if I can specify that my Value
type can be set without a value.
If this should be "fixed", I'd be happy to help.
I would love to see this fixed also, but I did discover a work around, slightly hacky, but works, and with limitations.
By using pflag.AddGoFlag()
or pflag.AddGoFlagSet()
, when pflag converts each flag.Flag
, it
will check for IsBoolValue() , and if true, it will set NoOptDefVal = true
, giving the equivalent behavior.
Using pflag.Var()
you cannot use --foo
, you still must do --foo=true
pflag.Var(&xxx, "foo", "foo help"). // even if xxx implements IsBoolValue() { return true}
This will allow the --foo
without --foo=true
flag.Var(&xxx, "foo", "foo help")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)