Other solutions have one or more of the following characteristics, which won't work for my needs:
* don't support compound / nested names
* require python 3
* don't work
* don't give control over hierarchical default names in the case of a missing key
So here's a solution that addresses these issues. Note that this solution only works for text names e.g. "{key}" or "{nested[key]}". Not sure it will work for more than that e.g. "{foo:<2s}".
This also doesn't handle arrays, but could easily be extended to do so. You could also substitute your own function to output whatever placeholder result for missing values you wanted.
Usage examples:
my_data = {
'hi': 'there',
'abc': {
'def': 'ghi'
},
'level1': {
'level2': {
'level3': 'nested'
}
}
}
s = "{hi} there"
print FormatDict(my_data).format(s) # 'there there'
s = "{hi} there {abc[def]}"
print FormatDict(my_data).format(s) # 'there there ghi'
s = "{hix} there {abc[def]}"
print FormatDict(my_data).format(s) # '{hix} there ghi'
s = "{hix} there {abc[defx]}"
print FormatDict(my_data).format(s) # '{hix} there {abc[defx]}'
s = "{hi} there {level1[level2][level3]}"
print FormatDict(my_data).format(s) # 'there there nested'
s = "{hix} there {level1[level2][level3x]}"
print FormatDict(my_data).format(s) # '{hix} there {level1[level2][level3x]}'
Here's the code:
import string
class FormatDict(dict):
def set_parent(self, parent):
self.parent = parent
def __init__(self, *args, **kwargs):
self.parent = None
self.last_get = ''
for arg in (args or []):
if isinstance(arg, dict):
for k in arg:
self.__setitem__(k, arg[k])
for k in (kwargs or {}):
self.__setitem__(k, kwargs[k])
def __getitem__(self, k):
self.last_get = k
try:
val = dict.__getitem__(self, k)
return val
except:
ancestry = [k]
x = self.parent
while x:
ancestry.append(x.last_get)
x = x.parent
ancestry.reverse()
return '{' + ancestry[0] + ''.join(['[' + x + ']' for x in ancestry[1:]]) + '}'
def __setitem__(self, k, v):
if isinstance(v, dict):
v = FormatDict(v)
v.set_parent(self)
dict.__setitem__(self, k, v)
def format(self, s):
return string.Formatter().vformat(s, (), self)