Open kferrone opened 3 years ago
If the string is static, x["a"]["b"] = "bar"
should work.
If you want to specify it as a.b
without breaking it apart, then it seems like you want a feature that isn't available in Starlark. But that's OK, lots of features aren't - you can write a function:
def set_deep(dict, path, val):
...
Which does what you want - and now you can extend Starlark with that feature. Generally Starlark is a subset of Python, so if you think this is useful, I think the interesting question would be how do you do it in Python, and can Starlark do it the same way or does it lack something. And my guess is in Python you'd do something very much like set_deep
? Or is there a better way in Python?
So after reading the entire spec and also finding out recursive functions are also banned, I actually figured it out. Well I already knew what needed to be done, just wanted to hear there was an easier way. Although this is basically Python, Starlark does also bans imports. So I was unable to import all the fun libraries Python core had to offer like deepSet and deepCopy, ie these are not actually in Python itself.
In my case I am using Starlark with Kustomize.
Just in case someone else out there wants to save a few minutes; here is my painfully manual deepGet and json patch,
# uses a json patch style to patch an objects deep value
# p = { path, op, value }
def patch(p, r):
ref = r
parts = p["path"].split("/")
count = len(parts)
for i in range(count): # recurse into the resource now to set the patch
k = parts[i]
# the first part of this if block is to make sure arrays and objects are initialized correctly
if not k == parts[-1]: # if k is not the last item in the list
if not k.isdigit():
if not k in ref: # we create the structure if the key is not already there
if parts[i+1].isdigit(): # if the next step is an array accessor
ref[k] = [] # we need to init the array
else:
ref[k] = {}
# this section is for actually setting the value
else:
val = p["value"]
if k.isdigit():
ref.insert(int(k), val)
else:
# add dict means apply the values to the source
if type(val) == "dict" and p["op"] == "add":
ref[k].update(val)
# everything else basically means replace
else:
ref[k] = val
break
if k.isdigit():
k = int(k)
# unlink the dicts so the replicas are not linked to each other
if type(ref[k]) == "dict":
ref[k] = unlink(ref[k])
if type(ref[k]) == "list":
ref[k] = list(ref[k])
for ii in range(len(ref[k])):
if type(ref[k][ii]) == "dict":
ref[k][ii] = dict(ref[k][ii])
ref = ref[k]
# Get a value deep within an object using a path
# example: spec/containers/0/name
def deepGet(path, r):
ref = r
parts = path.split("/")
count = len(parts)
for i in range(count): # for each index in the parts
k = parts[i]
# if the part is a number then the next step will access an array
if k.isdigit():
k = int(k)
else:
if not k in ref: # make sure the key actually exists
fail("The value at",path,"with key",k,"was not found on",r["kind"]+"/"+r["metadata"]["name"])
if type(ref[k]) == "dict":
ref = unlink(ref[k])
elif type(ref[k]) == "list":
ref = list(ref[k])
else:
ref = ref[k]
return ref
# quick helper to decide how to unlink a value from its source
# this is only a shallow copy, as good as it's going to get
def unlink(r):
for k, v in r.items():
if type(v) == "dict":
r[k] = dict(v)
elif type(v) == "list":
r[k] = list(v)
return r
I am looking for the ability to arbitrarily set a value deep on a dict using some form of path syntax.
for example:
Taken I have a variable like this for the path to the value
a.b
I want to easily set the value tobar
At this moment I have no idea how to easily do this in starlark without having to split on dot and loop through the keys to build an object for dict.update