| Index: chainedconfigparser.py |
| diff --git a/chainedconfigparser.py b/chainedconfigparser.py |
| index ce26b04b50e175e1cbd99a772f2a589acd1ac64c..0eaa280670fdce9732c92b79fa75c5edaead3cdd 100644 |
| --- a/chainedconfigparser.py |
| +++ b/chainedconfigparser.py |
| @@ -157,6 +157,78 @@ class ChainedConfigParser(ConfigParser.SafeConfigParser): |
| raise ConfigParser.NoSectionError(section) |
| raise ConfigParser.NoOptionError(option, section) |
| + def as_json_object(self, section): |
| + r"""Parse a given section into a JSON object. |
| + |
| + Parse arbitrary key/value pairs from 'section' of the current |
| + configuration into a nested JSON object. |
| + |
| + The following rules need to be considered: |
| + |
| + * An option's key may be declared as a series of nested dictionary keys, |
| + seperated by '.'. |
| + * An option's key must end with '[]', to mark this option as an array |
| + * When an option is marked as an array, no other nested objects may |
| + follow |
| + * An array is expandable by the ConfigParser's '+=' token. |
| + * Values may be marked as special types by appending '\{code}': |
| + * \b - boolean |
| + * \i - integer |
| + * \f - float |
| + |
| + Example: |
| + |
| + { |
| + "good": false, |
| + "bar": { |
| + "baz": ["bar", "bazinga"] |
| + foo = foo }, |
| + bar.baz[] = bar "is": { |
| + baz.foo = a "integer": 1, |
| + baz.z[] = b "float": 1.4 |
| + bar.baz[] += bazinga ==> }, |
| + bad = true\b "baz": { |
| + good = f\b "foo": "a", |
| + is.integer = 1\i "z": ["b"] |
| + is.float = 1.4\f }, |
| + "bad": true |
| + "foo": "foo" |
| + } |
| + """ |
| + def parse_values(v): |
| + import distutils |
| + if isinstance(v, list): |
| + return [parse_values(x) for x in v] |
| + |
| + mapping = { |
| + '\\b': lambda x: bool(distutils.util.strtobool(x)), |
| + '\\i': int, |
| + '\\f': float, |
| + } |
| + if '\\' in v: |
| + return mapping[v[-2:]](v[:-2]) |
| + return v |
|
Sebastian Noack
2018/04/12 12:19:23
* I cannot think of any relevant manifest key wher
tlucas
2018/04/12 13:47:39
I do not fully oppose your thoughts, but what abou
Sebastian Noack
2018/04/12 14:39:29
I was thinking about the version number, but its n
tlucas
2018/04/13 09:14:59
Done.
|
| + |
| + def setdefault_recursive(target, arr): |
| + if len(arr) == 2: |
| + target.setdefault(arr[0], parse_values(arr[1])) |
| + else: |
| + current = target.setdefault(arr[0], {}) |
|
Sebastian Noack
2018/04/12 12:19:23
You might want to use an OrderedDict to make sure
tlucas
2018/04/12 13:47:38
Good point, done.
|
| + setdefault_recursive(current, arr[1:]) |
| + |
| + data = self.items(section) |
| + result = {} |
|
Sebastian Noack
2018/04/12 12:19:23
Same here.
tlucas
2018/04/12 13:47:38
See above, done.
|
| + |
| + for k, v in data: |
| + is_array = k.endswith('[]') |
| + |
| + sub_keys = (k if not is_array else k[:-2]).split('.') |
| + value = v.split() if is_array else v |
|
Sebastian Noack
2018/04/12 12:19:23
It seems the logic here can be simplified:
if k
tlucas
2018/04/12 13:47:39
Done.
|
| + |
| + setdefault_recursive(result, sub_keys + [value]) |
| + |
| + return result |
| + |
| def readfp(self, fp, filename=None): |
| raise NotImplementedError |