Skip to content
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

Add support for comma separated IntSliceFlag and Int64SliceFlag #1118

Closed
wants to merge 6 commits into from

Conversation

zemzale
Copy link
Contributor

@zemzale zemzale commented Apr 27, 2020

What type of PR is this?

  • bug
  • cleanup
  • documentation
  • feature

What this PR does / why we need it:

IntSliceFlag can't parse comma separated like this -flag 1,2,3.
This PR adds support for that by :

  • Checking for comma in IntSliceFlag.Set and Int64SliceFlag.Set
  • If comma exists split the value string and add each number to the underlying slice

Which issue(s) this PR fixes:

Fixes #1097

Special notes for your reviewer:

My only knowledge of this project is from using it.
I came up with a pretty naive solution to this problem, so I would be happy to know if the change is implemented in the right place.

Testing

Added unit tests :

  • TestParseMultiIntSliceCommaSeperated
  • TestParseMultiInt64SliceCommaSeperated

These tests wouldn't fail if the error of Run isn't checked.
All the other parsing tests do the checking in Action but these tests won't even get there if the value is not valid.

Release Notes

Add support for comma separated `IntSliceFlag` and `Int64SliceFlag`

IntSliceFlag can't parse comma separated  like this `-flag 1,2,3`. This
is because it expects a single value that is parsed using `strconv.ParseInt`.
@zemzale zemzale requested a review from a team as a code owner April 27, 2020 19:22
@zemzale zemzale requested review from saschagrunert and rliebz and removed request for a team April 27, 2020 19:22
Copy link
Member

@coilysiren coilysiren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a high level, I wondering if this is a breaking change for people who do --flag 100,000,000 and expect [100000000] not [100,000,000]?

@rliebz
Copy link
Member

rliebz commented Apr 28, 2020

@lynncyrin The code currently errors on that input. Consider the following program:

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/urfave/cli/v2"
)

func main() {
	app := cli.NewApp()
	app.Flags = []cli.Flag{
		&cli.IntSliceFlag{
			Name: "val",
		},
	}

	app.Action = func(c *cli.Context) error {
		fmt.Println(c.IntSlice("val"))
		return nil
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

With go run main.go -val 5000 we get [5000]. If we pass 5,000, we get:

Incorrect Usage. invalid value "5,000" for flag -val: strconv.ParseInt: parsing "5,000": invalid syntax

That means we can add support for this feature without breaking backward compatibility.

I think that's a good call out that it allows potentially ambiguous inputs, but I'm okay with it in this case. We don't support that style of input in any other context (e.g., IntFlag), and it's consistent with how we handle slices of strings.

coilysiren
coilysiren previously approved these changes Apr 28, 2020
Copy link
Member

@coilysiren coilysiren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the example @rliebz

Copy link
Member

@rliebz rliebz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks great, thanks for putting tests together. One small nitpick in two places, and question:

Should we be fixing float64 slices as well? From my initial look at the code, I think that would just be an identical change in flag_float64_slice.go, so that probably belongs in this PR.

flag_int64_slice.go Outdated Show resolved Hide resolved
flag_int_slice.go Outdated Show resolved Hide resolved
@zemzale
Copy link
Contributor Author

zemzale commented Apr 28, 2020

Should we be fixing float64 slices as well? From my initial look at the code, I think that would just be an identical change in flag_float64_slice.go, so that probably belongs in this PR.

Yeah, I can do that.
Also searching through the tests, I can see that there are no tests TestParseMultiFloat64Slice like there are for IntSlice so I am gonna add those too while I am at it.

zemzale added 4 commits April 28, 2020 09:20
All slice flags have parsing tests, for parsing multiple values, and
with defaults, except for `Float64SliceFlag`, which only have tests for
loading from environment.
flag_test.go Outdated Show resolved Hide resolved
@saschagrunert
Copy link
Member

I think the string slice flag does not support such behavior, right? That's why I had to add a workaround in a urfave/cli consuming project. I know that comma separation could be intended even for string slice flags, but maybe we would like to add an option for such cases as well.

@zemzale
Copy link
Contributor Author

zemzale commented Apr 28, 2020

You are right that it doesn't support comma separated string slices from command line.
If you pass string slice flag -s foo,bar the value will be ["foo,bar"] not ["foo", "bar"].

@saschagrunert
Copy link
Member

You are right that it doesn't support comma separated string slices from command line.
If you pass string slice flag -s foo,bar the value will be ["foo,bar"] not ["foo", "bar"].

We could add an option which enables that kind of parsing 🤔

@zemzale
Copy link
Contributor Author

zemzale commented Apr 28, 2020

It could be done but I have no idea how I should implement that since this is the first time me touching this codebase aside from using it in my own applications 😅

@rliebz
Copy link
Member

rliebz commented Apr 28, 2020

Sorry, I was under the impression that we were adding this to make it consistent with the behavior for strings.

The original issue (#1097) specifically cites:

This works for StringSliceFlag.

If that's not the behavior for string slices, then is #1097 really a bug, or just a misunderstanding of how the library was designed to work?

If we don't yet support this behavior for any sort of slice flag, I would actually caution against adding it at all. We can't actually make string slices behave this way at this point without breaking backward compatibility. Although it's not trivial, it is absolutely possible to get this behavior for very customized CLIs by implementing the cli.Flag interface manually.

So my recommendation here unfortunately would be not to add in this behavior right now.

If the change is desired, the process should be to open a feature request rather than a bug ticket, weigh whether we actually want this behavior against the cost of making a breaking change for StringSliceFlag, and then should we accept it, make sure the PR that implements it covers all four of the flag slice types together.

@saschagrunert
Copy link
Member

I agree with @rliebz and I guess we should not automate any splitting. I think it would make sense in general to be added as optional feature.

@zemzale
Copy link
Contributor Author

zemzale commented Apr 28, 2020

If that's not the behavior for string slices, then is #1097 really a bug, or just a misunderstanding of how the library was designed to work?

It must be a misunderstanding. Following programm :

package main

import (
        "fmt"
        "log"
        "os"

        "github.com/micro/cli"
)

func main() {
        app := cli.NewApp()
        app.Flags = []cli.Flag{
                &cli.StringSliceFlag{
                        Name: "val",
                },
        }

        app.Action = func(c *cli.Context) error {
                fmt.Println(c.StringSlice("val"))
                return nil
        }

        if err := app.Run(os.Args); err != nil {
                log.Fatal(err)
        }
}

When run go run main.go -val foo,bar would return [foo,bar] instead of [foo bar]
It doesn't fail with a parse error like in the issue, it just reads the the given string as one value.

So this really is not a bug, but I would be glad to open up this as a feature request and implement it.

@coilysiren
Copy link
Member

coilysiren commented May 4, 2020

Here's another example that builds off the one above

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/urfave/cli/v2"
)

func main() {
	app := cli.NewApp()
	app.Flags = []cli.Flag{
		&cli.StringSliceFlag{
			Name: "val",
		},
	}

	app.Action = func(c *cli.Context) error {
		val := c.StringSlice("val")
		fmt.Println(val)
		fmt.Println(len(val))
		return nil
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}
$ go run main.go -val cat,hat
[cat,hat]
1

So yep, looks like StringSliceFlag doesn't split on ,

@zemzale
Copy link
Contributor Author

zemzale commented May 5, 2020

Then I am closing this PR and gonna create a feature request for this since there are people that are interested in this kinda functionality

@zemzale zemzale closed this May 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IntSliceFlag failes at parsing -flag=1,2,3
4 participants