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 |