spf13 / pflag

Drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags.
BSD 3-Clause "New" or "Revised" License
2.43k stars 348 forks source link

Manually Implementing a flag that has a default NoOptDefVal without forcing "=" #321

Open andrewzlchen opened 3 years ago

andrewzlchen commented 3 years ago

Description

Hello everyone,

I've been using the pflag library and it's been really great so far but i ran into an issue when trying to use flag.NoOptDefVal detailed in issue #134.

In short, I'd like to ask if anyone's done something similar and would be willing to provide tips on how to implement a flag that has a default value when it has no arguments passed in.

flag.NoOptDefVal is not sufficient for my use-case because I'd like to not have some flags force = and others, not.

Desired behavior:

with flag name = test-flag with no args default value = "hello-world"

my-binary --test-flag => test-flag.Value = "hello-world"

my-binary --test-flag user-defined-value => test-flag.Value = "user-defined-value"

my-binary --test-flag=user-defined-value => test-flag.Value = "user-defined-value"

I was wondering if anyone has run into this and got around it by implementing a custom flag using the pflag.Var function

I tried doing this but I've been thwarted by not being able to control when pflag.Value.Set() (part of the pflag.Value interface) is called

I'm happy to provide more detail if needed.

makesitgo commented 3 years ago

To tack on to this, it is effectively the same issue as reported here: https://github.com/spf13/pflag/issues/126

What would be ideal is the ability to set whether the "flag needs an argument" is returned or not. Basically a way to configure turning off that check. With the way our CLI is written, we don't use positional args so the drawbacks of not having this check don't apply to many of our scenarios.

I definitely don't have the full picture of what the implications of such a flag configuration would entail, but it does seem if we were able to get through https://github.com/spf13/pflag/blob/85dd5c8bc61cfa382fecd072378089d4e856579d/flag.go#L990-L1005 then our use-case would be fully supported and we could be off-and-running with "optional value flags".

Open to ideas here!

memreflect commented 2 years ago

my-binary --test-flag => test-flag.Value = "hello-world"

my-binary --test-flag user-defined-value => test-flag.Value = "user-defined-value"

my-binary --test-flag=user-defined-value => test-flag.Value = "user-defined-value"

Setting NoOptDefVal changes the behavior of the flag to make it behave like Bool, BoolVar, etc., so --test-flag=VALUE becomes a requirement, just like Bool and BoolVar in Go's standard flag package. What you're proposing is effectively

my-binary --bool-flag false => bool-flag.Value = false my-binary --bool-flag user-defined-value => invalid bool value: "user defined value"

which is not how bool-style flags are supposed to work, including those using NoOptDefVal.

That said, Python's argparse module actually implements the behavior you desire, but I have yet to use another option/flag parser that has the same behavior.

By the way, you forgot the case where a flag with NoOptDefVal is followed by another flag:

my-binary --test-flag --flag2

Is test-flag.Value equal to "--flag2" or "hello-world" in that case? Or if --test-flag did not use NoOptDefVal, then should it cause an error like argparse because any string starting with - is a potential flag and can never possibly be an argument to any flag? (Is my dislike for argparse showing? :smile:)