| Index: chainedconfigparser.py |
| diff --git a/chainedconfigparser.py b/chainedconfigparser.py |
| index ce26b04b50e175e1cbd99a772f2a589acd1ac64c..504ecc3303f214d85ede2da3a325616d167670dd 100644 |
| --- a/chainedconfigparser.py |
| +++ b/chainedconfigparser.py |
| @@ -93,15 +93,15 @@ class ChainedConfigParser(ConfigParser.SafeConfigParser): |
| except ConfigParser.NoOptionError: |
| raise DiffForUnknownOptionError(option, section) |
| - orig_values = orig_value.split() |
| - diff_values = value.split() |
| + orig_values = orig_value.splitlines() |
| + diff_values = value.splitlines() |
| if is_addition: |
| new_values = orig_values + [v for v in diff_values if v not in orig_values] |
| else: |
| new_values = [v for v in orig_values if v not in diff_values] |
| - value = ' '.join(new_values) |
| + value = '\n'.join(new_values) |
| return is_diff, option, value |
| @@ -157,6 +157,79 @@ class ChainedConfigParser(ConfigParser.SafeConfigParser): |
| raise ConfigParser.NoSectionError(section) |
| raise ConfigParser.NoOptionError(option, section) |
| + def section_as_dict(self, section, base): |
| + """Parse a given section into a dictionary. |
| + |
| + Parse arbitrary key/value pairs from 'section' of the current |
| + configuration into a dictionary and deep merge it into `base`. |
| + |
| + The following rules need to be considered: |
| + |
| + * An option's key may be declared as a series of nested dictionary keys, |
| + seperated by '.'. |
| + * Declaring an option's value in a new line (even if only one is given) |
| + will define the option's value as an array. |
|
Sebastian Noack
2018/04/19 10:50:56
Nit: Talking about "arrays" made sense initially w
tlucas
2018/04/19 11:08:13
Done.
|
| + * When an option's value is defined as an array, no other nested |
| + objects may follow. |
| + * An array is expandable by the ConfigParser's '+=' token (Note: A |
| + previously declared string will be converted into an array). |
| + * Values may be marked as `number` or `bool` by prefixing them |
| + accordingly (this also applies to values in an array): |
| + * bool:<value> |
| + * number:<value> |
| + |
| + Example: |
| + { |
| + foo = foo "foo": "foo", |
| + asd = "asd": ["asd"], |
| + asd "bar": { |
|
Sebastian Noack
2018/04/19 10:50:57
Nit: For consistency, we might want to use two spa
tlucas
2018/04/19 11:08:14
Done.
|
| + bar.baz = a "baz": ["a", "c", "d"] |
| + baz.foo = a }, |
| + baz.z = "baz": { |
| + bar "foo": "a", |
| + bool:true ===> "z": ["bar", true] |
| + bar.baz += }, |
| + c "bad": true, |
| + d "good": false, |
| + bad = bool:true "is": { |
| + good = bool:false "integer": 1, |
| + is.integer = number:1 "float": 1.4 |
| + is.float = number:1.4 } |
| + } |
| + """ |
| + def parse_values(v): |
|
Sebastian Noack
2018/04/19 10:50:57
Nit; since this function is only called with a sin
tlucas
2018/04/19 11:08:13
Done.
|
| + if isinstance(v, list): |
| + return [parse_values(x) for x in v] |
|
Sebastian Noack
2018/04/19 10:50:57
This code path can be removed now, since we no lon
tlucas
2018/04/19 11:08:14
Done.
|
| + |
| + if v.startswith('number:'): |
| + v = v.split(':', 1)[1] |
| + try: |
| + return int(v) |
| + except ValueError: |
| + return float(v) |
| + if v == 'bool:true': |
| + return True |
| + if v == 'bool:false': |
| + return False |
| + return v |
| + |
| + data = self.items(section) |
|
Sebastian Noack
2018/04/19 10:50:56
Nit: this variable is only used once, perhaps inli
tlucas
2018/04/19 11:08:14
Done.
One note: Not catching a NoSectionError her
tlucas
2018/04/19 11:45:15
Done.
|
| + |
| + for k, v in data: |
| + |
|
Sebastian Noack
2018/04/19 10:50:58
Nit: Is the blank line here intended? It looks wei
tlucas
2018/04/19 11:08:15
Done.
|
| + parents = k.split('.') |
| + tail = parents.pop() |
| + current = base |
| + for name in parents: |
| + current = base.setdefault(name, {}) |
| + |
| + if '\n' in v: |
| + current[tail] = [parse_values(x) for x in v.splitlines() if x] |
| + else: |
| + current[tail] = parse_values(v) |
| + |
| + return base |
|
Sebastian Noack
2018/04/19 10:50:57
Nit: Returning the dictionary seems redundant. It'
tlucas
2018/04/19 11:08:14
I wouldn't mind either solution - but we also migh
Sebastian Noack
2018/04/19 11:12:41
That is exactly what I said. My suggestions above
tlucas
2018/04/19 11:45:15
Done.
|
| + |
| def readfp(self, fp, filename=None): |
| raise NotImplementedError |