Index: chainedconfigparser.py |
diff --git a/chainedconfigparser.py b/chainedconfigparser.py |
index ce26b04b50e175e1cbd99a772f2a589acd1ac64c..63ccc42145afcdb4f00b23dd8accddcce5cf36b0 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,83 @@ class ChainedConfigParser(ConfigParser.SafeConfigParser): |
raise ConfigParser.NoSectionError(section) |
raise ConfigParser.NoOptionError(option, section) |
+ def section_as_dict(self, section, additional={}): |
+ """Parse a given section into a dictionary. |
+ |
+ Parse arbitrary key/value pairs from 'section' of the current |
+ configuration into a dictionary and merge it with the optional |
+ parameter `additional`. |
+ |
+ 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. |
+ * 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": { |
+ 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): |
+ if isinstance(v, list): |
+ return [parse_values(x) for x in v] |
Sebastian Noack
2018/04/18 15:59:00
Checking for lists and recursively calling this fu
tlucas
2018/04/19 10:01:39
Done.
|
+ |
+ if v.startswith('number:'): |
+ v = v.split(':', 1)[1] |
+ try: |
+ v = int(v) |
Sebastian Noack
2018/04/18 15:58:59
Nit: I wouldn't reassign v, but just return the pa
tlucas
2018/04/19 10:01:39
Done.
|
+ except ValueError: |
+ v = float(v) |
+ elif v == 'bool:true': |
+ v = True |
+ elif v == 'bool:false': |
+ v = False |
+ return v |
+ |
+ def setdefault_recursive(target, keys, value): |
+ if len(keys) == 1: |
+ target.setdefault(keys[0], parse_values(value)) |
+ else: |
+ current = target.setdefault(keys[0], {}) |
+ setdefault_recursive(current, keys[1:], value) |
+ |
+ data = self.items(section) |
+ result = {} |
+ |
+ for k, v in data: |
+ if '\n' in v: |
+ v = [x for x in v.splitlines() if x] |
+ |
+ setdefault_recursive(result, k.split('.'), v) |
Sebastian Noack
2018/04/18 15:59:00
The recursion here is unnecessary:
parents = k.
tlucas
2018/04/19 10:01:39
Done.
|
+ |
+ result.update(additional) |
Sebastian Noack
2018/04/18 15:58:59
Well, the point of passing the generated part of t
tlucas
2018/04/19 10:01:40
Done.
|
+ |
+ return result |
+ |
def readfp(self, fp, filename=None): |
raise NotImplementedError |