I have a complex nested dict object, e.g:
value = {
'a': '100',
bits: {
1: 'alpha',
2: 'beta',
3: ['31', '32', 901]
}
}
I need to 'safely' format it using a template. Meaning if the keys are not found, just silently ignore the {}
place-holders. The Keys might not exist, and I do not want to raise KeyErrors. The problem is that string.Template cannot handle the same functionality that str.format does. The str.format I used is something like:
"a=${a}, b1={bits[1]}, b31={bits[3]}, b9={bits[9]}".format(**value)
and the output should be:
"a=100, b1=alpha, b31=(31, 32, 901), b9="
I do not need fancy loops or if/else conditions. Just simple formats with sub dicts.
What are the options I have? I prefer to use built-ins as much as possible or a very small library.
This is not a web app, so no if possible I want to avoid loading a lib like jinja2 just for this.
Write you own formatter:
In [1]: from string import Formatter
In [2]: value = {
...: 'a': '100',
...: 'bits': {
...: 1: 'alpha',
...: 2: 'beta',
...: 3: ['31', '32', 901]}}
In [3]: class YourFormatter(Formatter):
...: def get_value(self, field_name, args, kwargs):
...: return kwargs.get(field_name, '')
...:
...: def get_field(self, field_name, args, kwargs):
...: first, rest = field_name._formatter_field_name_split()
...: obj = self.get_value(first, args, kwargs)
...:
...: for is_attr, i in rest:
...: if is_attr:
...: obj = getattr(obj, i)
...: else:
...: obj = obj.get(i, '')
...: return obj, first
...:
In [4]: fmt = YourFormatter()
In [5]: fmt.format("a={a}, b1={bits[1]}, b31={bits[3]}, b9={bits[9]}", **value)
Out[5]: "a=100, b1=alpha, b31=['31', '32', 901], b9="
for Python 3, you need to add
import _string
and replace the line
first, rest = field_name._formatter_field_name_split()
with
first, rest = _string.formatter_field_name_split(field_name)
The only way to do this is to write a wrapper class that implements the dict and sequence protocols, wrapping any list or dict return values in the same class, and catch any KeyError or IndexError exceptions.
Then your call becomes "…".format(**DefaultingWrapper(value))
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With