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

Nested dicts as input? #20

Open
chasehensel opened this issue Jul 3, 2020 · 3 comments
Open

Nested dicts as input? #20

chasehensel opened this issue Jul 3, 2020 · 3 comments

Comments

@chasehensel
Copy link

I am really enjoying using this. I've been trying to get parsing of nested maps to work, but somehow the inner dicts loose their "mapness" and as a result can't be index.

Think input like:

x = {args : {"a" : {
"b" : "c"
}
}}

as input to the env. x is a starlark.StringDict, and accessing x works fine inside of starlark.

args is a perfectly valid command.
args["a"] also works, but args["a"]["b"] throws a runtime exception of:

Error: unhandled index operation starlight_interface<map[string]interface {}>[string]

Curious if you ever solved this?

In general, I've been playing around with some additional wrappers on top of FromValue and ToValue in order to make them work recursively -- the use case is getting json in and out of starlark. My from value recursion works really well, but I'm struggling with ToValue and figured I'd try and start a conversation

@chasehensel
Copy link
Author

chasehensel commented Jul 3, 2020

I solved it with the following. Wondering why you don't have a recursive form of ToValue and FromValue in starlight already? Would be happy to add in a pull request

func recursiveToValue(input interface{}) (out starlark.Value, err error) {
	if err != nil {
		return nil, err
	}
	switch input.(type) {
	//Do I need other map types?
	case map[string]interface{}:
		dict := starlark.Dict{}
		for k, v := range input.(map[string]interface{}) {
			key, err := convert.ToValue(k)
			if err != nil {
				return nil, err
			}
			val, err := recursiveToValue(v)
			if err != nil {
				return nil, err
			}
			dict.SetKey(key, val)
		}
		return &dict, nil
	case []interface{}:
		l := input.([]interface{})
		out := make([]starlark.Value, 0, len(l))
		for i := 0; i < len(l); i++ {
			val, err := recursiveToValue(out[i])
			if err != nil {
				return nil, err
			}
			out[i] = val
		}
		return starlark.NewList(out), nil
	default:
		return convert.ToValue(input)
	}
}

@johnha
Copy link

johnha commented Oct 20, 2022

thanks for the code! There is a bug in the 'out' array initialisation. Corrected:


// ToValue will convert an internal go value to starlark value.  It operates differently from the starlark/starlight convert.ToValue
//	in that it is recursive.
func ToValue(input interface{}) (out starlark.Value, err error) {
	if err != nil {
		return nil, err
	}
	switch input.(type) {
	case map[string]interface{}:
		dict := starlark.Dict{}
		for k, v := range input.(map[string]interface{}) {
			key, err := convert.ToValue(k)
			if err != nil {
				return nil, err
			}
			val, err := ToValue(v)
			if err != nil {
				return nil, err
			}
			dict.SetKey(key, val)
		}
		return &dict, nil
	case []interface{}:
		l := input.([]interface{})
		out := make([]starlark.Value, len(l))
		for i := 0; i < len(l); i++ {
			val, err := ToValue(l[i])
			if err != nil {
				return nil, err
			}
			out[i] = val
		}
		return starlark.NewList(out), nil
	default:
		return convert.ToValue(input)
	}
}

@johnha
Copy link

johnha commented Oct 20, 2022

and just to clarify for anyone using this routine: Note that internally go does not have the tuple type. If you are serialising to/from JSON then tuples are represented as json arrays and therefore we cannot differentiate between list and tuple types (json conversion in this respect is lossy). Conversions through this function will assume a list type.

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

No branches or pull requests

2 participants