| Index: chainedconfigparser.py |
| diff --git a/chainedconfigparser.py b/chainedconfigparser.py |
| index ce26b04b50e175e1cbd99a772f2a589acd1ac64c..9a560fa82ad8e93a101a573bcc9ddf0a2ace1e0d 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,73 @@ 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 a list. |
| + * When an option's value is defined as a list, no other nested |
| + objects may follow. |
| + * A list is expandable by the ConfigParser's '+=' token (Note: A |
| + previously declared string will be converted into a list). |
| + * Values may be marked as `number` or `bool` by prefixing them |
| + accordingly (this also applies to values in a list): |
| + * bool:<value> |
| + * number:<value> |
| + |
| + Example: |
| + { |
| + foo = foo "foo": "foo", |
| + asd = "asd": ["asd"], |
| + asd "bar": { |
| + 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_value(v): |
| + 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 |
| + |
| + for k, v in self.items(section): |
| + parents = k.split('.') |
| + tail = parents.pop() |
| + current = base |
| + for name in parents: |
| + current = base.setdefault(name, {}) |
| + |
| + if '\n' in v: |
| + current[tail] = [parse_value(x) for x in v.splitlines() if x] |
| + else: |
| + current[tail] = parse_value(v) |
| + |
| + return base |
| + |
| def readfp(self, fp, filename=None): |
| raise NotImplementedError |