-
Notifications
You must be signed in to change notification settings - Fork 366
Allows any flag to expect optional arguments #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
67365a8 to
e8cd826
Compare
|
Given flag.IntVar(&flagvar, "list", 10, "list the number of items")
flag.MarkOptional("list")What's the different between and Sort of unrelated: |
|
@eparis Updated the description with an answer and use case to your first question. |
|
@eparis I'd say in autocomplete it should not show the |
|
In fact I think the help should reflect it too: when a flag is set as |
e8cd826 to
ebc2dbc
Compare
|
So I've been doing some thinking. Never a good idea for anyone... Tell me what you think... (And mind you, I have no idea how to make it work sanely) What if we changed the Value interface somehow, so it had both a default and a no-option default. So in your code above somehow becomes Bools all turn into What it would mean for the consumer of pflag is that they can just use the list value. But maybe the code on our end isn't worth it just to keep the users of pflag from having to look at changed... |
|
actually, it is probably as easy as doing it as a string in the Flag structure and calling Set() if the flag was called without an argument.... So probably not as hard to code as I first thought.... |
|
@eparis I like how it looks, but the main reason I didn't choose to internalize the no-option-default value initially (instead delegating to the user the control of storing and managing that value and just giving a way to check if it was set, through I'm ok with it if you think it's reasonable, but in a first glance it looked a little too much. If we are adding something to every possible type, then we could just add new |
|
So I drank a lot and did some hacking on this over the weekend. I completely rewrote parseLongArg and parseShortArg. Not because they were wrong, just because I thought I could do better. In some ways I did better, in some ways worse. But that's not the point here.... The point is the default-ing method isn't too hard. Before this change, given a variable --list with shorthand -l any of the following would be valid
Now if we define list as optional we lose 2 and 4. And I guess we lose 5. I guess that makes sense and people shouldn't be toooo shocked about it.... |
|
Ok, so you are storing That said, I think loosing any of the short|long forms is an issue. |
ebc2dbc to
933843b
Compare
933843b to
24e8eb1
Compare
|
#20 is my alternative. Thoughts? |
|
@eparis I do like how it's exposed to users. But I think loosing any form of short or long flags is an issue. If you can assure we still support all forms then I'm good. |
|
If a user sets NoOptDefVal: Works: Does not work |
|
So trying to follow along... I think I understand why this feature is valuable and the different approaches.. It seems though with either approach the experience will be different for the user depending on if the feature is enabled or not. This seems weird to me that a call that works today will no longer work (or could possibly not work in the future) because a developer decided to make a flag optional. As I understand it With optional params (works): Without optional params (works): I haven't given it enough thought, but I would hope a solution exists that could cover every case that works today. |
|
@spf13 correct. Both approaches have the same limitation. When a flag has been set as taking an 'optional' argument, the code has no idea if user intended the next word to be the argument to the flag, to be the next flag, or to be some interspersed word we should ignore. You are right. If developers turn an existing flag into a 'arg optional' flag users may see breakage. |
|
although admittedly we already this rather odd usage around bools. Fails: |
|
So the bool case makes a lot of sense to me because it's only used to change the current behavior to the opposite behavior. With the case of bool it's a bit surprising that it takes values at all, but I understand that it would want to do that to enable people to be explicit which is typically a good practice when scripting things as it would remain consistent even if the default changes. Is there a practical use case where you intend to use this feature? I'd like to understand it a bit better. I see why it's nice, but I don't think I fully understand it's value. We've also strived to be posix compliant so far. I don't claim to be an expert on the subtleties of the POSIX specification, but I believe this feature is in disagreement with guideline 7 (at the bottom). |
|
@eparis @spf13 Wait, that's not correct. This PR (#18) as originally proposed does not break the current behavior or make any difference either the optional arg feature is enabled or not. All these forms work correctly when having an optional arg (testing right now with my real use case): I agree that the solution must cover every case that works today, the short and long forms with a space between the flag and argument are important and must work. |
|
@fabianofranz so what does deploy -L --some-other-flag do? |
|
@fabianofranz or deploy -lb where b is some boolean shorthand? |
|
just to be thorough, GNU amends the POSIX standards a bit I believe it doesn't address optional argument values in it's specification. |
|
@eparis Fair enough, |
|
So big holy crap moment for me: broke --flag value ! This has basically NEVER worked, and no one noticed! I'm just stunned... |
|
Try it yourself: package main
import (
"fmt"
"os"
"github.com/spf13/pflag"
)
var _ = fmt.Fprintf
var o = os.Stdout
func main() {
var flagvar int
pflag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
pflag.Parse()
fmt.Fprintf(o, "val=%v\n", flagvar)
} |
|
my fix is below. But since --flag name hasn't worked since like 2012, maybe we don't have a problem ehre.... diff --git a/flag.go b/flag.go
index 9f16733..93222c8 100644
--- a/flag.go
+++ b/flag.go
@@ -530,11 +530,15 @@ func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error)
}
var value string
if len(split) == 1 {
- if bv, ok := flag.Value.(boolFlag); !ok || !bv.IsBoolFlag() {
+ if bv, ok := flag.Value.(boolFlag); ok && bv.IsBoolFlag() {
+ value = "true"
+ } else if len(a) > 0 {
+ value = a[0]
+ a = a[1:]
+ } else {
err = f.failf("flag needs an argument: %s", s)
return
}
- value = "true"
} else {
value = split[1]
} |
|
I decided to take a look at what ls does (and how it represents things in the man page). Basically the ls command in linux does what is being described here. Sometimes you can use a space and sometimes you can't... It makes me feel like we should go with the optional arg stuff. |
|
@thockin since you pointed out that --flag val doesn't work. |
|
I still have my PR version of this open #20 |
The motivation for this change is to allow any flag or flag type (not only boolean) to support optional arguments as long as they are declared to do so. Example:
The logic around flags with empty arguments (e.g.
--debug) were detached from the boolean type and moved to a separate attribute calledOptional. This allows to mark any flag type as supporting optional arguments.The difference in comparison to not providing the flag at all is that the
Changedattribute is set, so it allows to control the difference of not providing the flag or providing but without an argument.A possible use case would be:
So when using the flag users could either provide the number of items to display in the list, or not provide any argument to use the default
intvalue:The behavior for default types were not changed (the only flags that allow empty arguments by default are boolean).