Viper: StringToString pflag values are not handled correctly by viper

Created on 11 Dec 2018  路  19Comments  路  Source: spf13/viper

I bind a StringToString pflag like so

RootCmd.PersistentFlags().StringToStringP(key, conf.alias, defaultValue, conf.usage)
viper.BindPFlag(key, RootCmd.PersistentFlags().Lookup(key))

Then retrieve the value from my viper with

smap := viper.GetStringMapString(key)

The string map returned is an empty map, which seems to be the default value returned on error by spf13/cast.

I expected to get a populated string map back.

kinbug

All 19 comments

This issue seems related to what was seen in https://github.com/spf13/viper/issues/200

It looks like viper is fixed, can someone update viper?
https://github.com/spf13/viper/issues/200

I made a demo of this issue: https://github.com/kd7lxl/viper-demo

it seems that in v1.6.2 this issue is fixed

I tried it @danmx and still doesn't work. Using filters = viper.GetStringMapString("filter") with --filter name=test outputs "DEBUG filterArgs = {map[]}".

@danmx @amir20 I've submitted a pr with a fix for this issue. Hopefully someone will have a look at some point.

@terev here you have how the input works:
key_a=value_1,key_b=value_2
https://github.com/spf13/pflag/blob/d929dcb/string_to_string_test.go#L143-L144

@amir20 did you add this to your code:

serverCmd.Flags().Int("filter", map[string]string{}, "desc filter")
if err := viper.BindPFlag("filter", serverCmd.Flags().Lookup("filter")); err != nil {
    return err
}

https://github.com/spf13/viper#working-with-flags

The issue still persists in v1.6.2.

This:

f := pflag.NewFlagSet("", pflag.ContinueOnError)
f.StringToString("filter", map[string]string{}, "desc filter")
_ = viper.BindPFlags(f)
...
filters := viper.GetStringMapString("filter")
fmt.Println(filters)

prints map[].

While using just GetString:

filters := viper.GetString("filter")

prints [a=0].

@matevz is correct here. This is still broken.

@danmx Not sure why you have an Int for pflags. You need to use StringToString to reproduce this. Can you try @matevz code? I get map[] too. Maybe you are doing something different.

@amir20 that was a poor copy-paste of code snippets from my tool.

I wrote a simple cli just to test it and you're right also now I know that I have a bug in my tool 馃う鈥嶁檪

here's the code if someone what's to quickly reproduce it:

package main

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var (
    cfg     *viper.Viper
    rootCmd = &cobra.Command{
        PreRunE: func(cmd *cobra.Command, args []string) error {
            if err := cfg.BindPFlag("test1", cmd.Flags().Lookup("test1")); err != nil {
                return err
            }
            if err := cfg.BindPFlag("test2", cmd.Flags().Lookup("test2")); err != nil {
                return err
            }
            return nil
        },
        RunE: func(cmd *cobra.Command, args []string) error {
            a := cfg.GetStringMapString("test1")
            fmt.Printf("GetStringMapString - T: %T, v: %v, s: %s\n", a, a, a)
            b := cfg.GetStringMap("test1")
            fmt.Printf("GetStringMap - T: %T, v: %v, s: %s\n", b, b, b)
            c := cfg.Get("test1")
            fmt.Printf("Get - T: %T, v: %v, s: %s\n", c, c, c)
            arg2 := cfg.GetString("test2")
            fmt.Printf("GetString - T: %T, v: %v, s: %s\n", arg2, arg2, arg2)
            return nil
        },
    }
)

func init() {
    cfg = viper.GetViper()
    rootCmd.Flags().StringToString("test1", map[string]string{"default_key": "default_value"}, "test description")
    rootCmd.Flags().String("test2", "default_string", "test description")
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        os.Exit(1) //nolint:gomnd
    }
}
$ go build -o main

$ ./main --test1 a=b,c=d
GetStringMapString - T: map[string]string, v: map[], s: map[]
GetStringMap - T: map[string]interface {}, v: map[], s: map[]
Get - T: string, v: [c=d,a=b], s: [c=d,a=b]
GetString - T: string, v: default_string, s: default_string

@amir20 @danmx if you wouldn't mind could you try my fork? i linked the pr above

$ ./main --test1 a=b,c=d
GetStringMapString - T: map[string]string, v: map[a:b c:d], s: map[a:b c:d]
GetStringMap - T: map[string]interface {}, v: map[], s: map[]
Get - T: map[string]string, v: map[a:b c:d], s: map[a:b c:d]
GetString - T: string, v: default_string, s: default_string

@terev it seems to work only for GetStringMapString

@danmx to me that output looks like Get also worked

@terev you're right 馃槀 I only glimpsed over GetStringMap (which is still broken) and GetStringMapString

@amir20 @danmx pushed some changes https://github.com/spf13/viper/pull/874. I was able to get GetStringMap working,

GetStringMapString - T: map[string]string, v: map[a:b c:d], s: map[a:b c:d]
GetStringMap - T: map[string]interface {}, v: map[a:b c:d], s: map[a:b c:d]
Get - T: map[string]interface {}, v: map[a:b c:d], s: map[a:b c:d]
GetString - T: string, v: default_string, s: default_string

This _still_ doesn't work. :( @sagikazarmark I tried testing with EXAMPLE_FOO=bar=test go run main.go and it still fails.

After reading and using debugger, I see that the code eventually gets to https://github.com/spf13/cast/blob/master/cast.go#L108-L110.

func ToStringMapString(i interface{}) map[string]string {
    v, _ := ToStringMapStringE(i)
    return v
}

ToStringMapStringE returns an error because it cannot cast bar=test using JSON. :/

I can create a new bug or reopen this.

@amir20 This issue (and the fix) is for pflag values, not env vars. So I think it should definitely be a new feature request.

Interesting. That's confusing. This whole time I thought viper.GetStringMapString(key) is going to fix both env and pFalgs use case. I'd argue that as a developer it shouldn't be a different feature to know the source.

Regardless, not a big deal. I'll create a new bug.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

orian picture orian  路  4Comments

Giri4joyz picture Giri4joyz  路  3Comments

noandrea picture noandrea  路  4Comments

akshaylb picture akshaylb  路  4Comments

jeffwillette picture jeffwillette  路  5Comments