| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| 1 # This Source Code Form is subject to the terms of the Mozilla Public | 1 # This Source Code Form is subject to the terms of the Mozilla Public |
| 2 # License, v. 2.0. If a copy of the MPL was not distributed with this | 2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. | 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 4 | 4 |
| 5 import os | 5 import os |
| 6 import io | 6 import io |
| 7 import ConfigParser | 7 import ConfigParser |
| 8 from StringIO import StringIO | 8 from StringIO import StringIO |
| 9 | 9 |
| 10 | 10 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 is_addition = option.endswith('+') | 86 is_addition = option.endswith('+') |
| 87 is_diff = is_addition or option.endswith('-') | 87 is_diff = is_addition or option.endswith('-') |
| 88 | 88 |
| 89 if is_diff: | 89 if is_diff: |
| 90 option = option[:-1].rstrip() | 90 option = option[:-1].rstrip() |
| 91 try: | 91 try: |
| 92 orig_value = self.get(section, option) | 92 orig_value = self.get(section, option) |
| 93 except ConfigParser.NoOptionError: | 93 except ConfigParser.NoOptionError: |
| 94 raise DiffForUnknownOptionError(option, section) | 94 raise DiffForUnknownOptionError(option, section) |
| 95 | 95 |
| 96 orig_values = orig_value.split() | 96 orig_values = orig_value.splitlines() |
| 97 diff_values = value.split() | 97 diff_values = value.splitlines() |
| 98 | 98 |
| 99 if is_addition: | 99 if is_addition: |
| 100 new_values = orig_values + [v for v in diff_values if v not in o rig_values] | 100 new_values = orig_values + [v for v in diff_values if v not in o rig_values] |
| 101 else: | 101 else: |
| 102 new_values = [v for v in orig_values if v not in diff_values] | 102 new_values = [v for v in orig_values if v not in diff_values] |
| 103 | 103 |
| 104 value = ' '.join(new_values) | 104 value = '\n'.join(new_values) |
| 105 | 105 |
| 106 return is_diff, option, value | 106 return is_diff, option, value |
| 107 | 107 |
| 108 def _process_parsers(self, parsers): | 108 def _process_parsers(self, parsers): |
| 109 for parser, filename in parsers: | 109 for parser, filename in parsers: |
| 110 for section in parser.sections(): | 110 for section in parser.sections(): |
| 111 if not self.has_section(section): | 111 if not self.has_section(section): |
| 112 try: | 112 try: |
| 113 ConfigParser.SafeConfigParser.add_section(self, section) | 113 ConfigParser.SafeConfigParser.add_section(self, section) |
| 114 except ValueError: | 114 except ValueError: |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 | 150 |
| 151 def option_source(self, section, option): | 151 def option_source(self, section, option): |
| 152 option = self.optionxform(option) | 152 option = self.optionxform(option) |
| 153 try: | 153 try: |
| 154 return self._origin[(section, option)] | 154 return self._origin[(section, option)] |
| 155 except KeyError: | 155 except KeyError: |
| 156 if not self.has_section(section): | 156 if not self.has_section(section): |
| 157 raise ConfigParser.NoSectionError(section) | 157 raise ConfigParser.NoSectionError(section) |
| 158 raise ConfigParser.NoOptionError(option, section) | 158 raise ConfigParser.NoOptionError(option, section) |
| 159 | 159 |
| 160 def section_as_dict(self, section, base): | |
| 161 """Parse a given section into a dictionary. | |
| 162 | |
| 163 Parse arbitrary key/value pairs from 'section' of the current | |
| 164 configuration into a dictionary and deep merge it into `base`. | |
| 165 | |
| 166 The following rules need to be considered: | |
| 167 | |
| 168 * An option's key may be declared as a series of nested dictionary keys, | |
| 169 seperated by '.'. | |
| 170 * Declaring an option's value in a new line (even if only one is given) | |
| 171 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.
| |
| 172 * When an option's value is defined as an array, no other nested | |
| 173 objects may follow. | |
| 174 * An array is expandable by the ConfigParser's '+=' token (Note: A | |
| 175 previously declared string will be converted into an array). | |
| 176 * Values may be marked as `number` or `bool` by prefixing them | |
| 177 accordingly (this also applies to values in an array): | |
| 178 * bool:<value> | |
| 179 * number:<value> | |
| 180 | |
| 181 Example: | |
| 182 { | |
| 183 foo = foo "foo": "foo", | |
| 184 asd = "asd": ["asd"], | |
| 185 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.
| |
| 186 bar.baz = a "baz": ["a", "c", "d"] | |
| 187 baz.foo = a }, | |
| 188 baz.z = "baz": { | |
| 189 bar "foo": "a", | |
| 190 bool:true ===> "z": ["bar", true] | |
| 191 bar.baz += }, | |
| 192 c "bad": true, | |
| 193 d "good": false, | |
| 194 bad = bool:true "is": { | |
| 195 good = bool:false "integer": 1, | |
| 196 is.integer = number:1 "float": 1.4 | |
| 197 is.float = number:1.4 } | |
| 198 } | |
| 199 """ | |
| 200 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.
| |
| 201 if isinstance(v, list): | |
| 202 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.
| |
| 203 | |
| 204 if v.startswith('number:'): | |
| 205 v = v.split(':', 1)[1] | |
| 206 try: | |
| 207 return int(v) | |
| 208 except ValueError: | |
| 209 return float(v) | |
| 210 if v == 'bool:true': | |
| 211 return True | |
| 212 if v == 'bool:false': | |
| 213 return False | |
| 214 return v | |
| 215 | |
| 216 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.
| |
| 217 | |
| 218 for k, v in data: | |
| 219 | |
|
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.
| |
| 220 parents = k.split('.') | |
| 221 tail = parents.pop() | |
| 222 current = base | |
| 223 for name in parents: | |
| 224 current = base.setdefault(name, {}) | |
| 225 | |
| 226 if '\n' in v: | |
| 227 current[tail] = [parse_values(x) for x in v.splitlines() if x] | |
| 228 else: | |
| 229 current[tail] = parse_values(v) | |
| 230 | |
| 231 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.
| |
| 232 | |
| 160 def readfp(self, fp, filename=None): | 233 def readfp(self, fp, filename=None): |
| 161 raise NotImplementedError | 234 raise NotImplementedError |
| 162 | 235 |
| 163 def set(self, section, option, value=None): | 236 def set(self, section, option, value=None): |
| 164 raise NotImplementedError | 237 raise NotImplementedError |
| 165 | 238 |
| 166 def add_section(self, section): | 239 def add_section(self, section): |
| 167 raise NotImplementedError | 240 raise NotImplementedError |
| 168 | 241 |
| 169 def remove_option(self, section, option): | 242 def remove_option(self, section, option): |
| 170 raise NotImplementedError | 243 raise NotImplementedError |
| 171 | 244 |
| 172 def remove_section(self, section): | 245 def remove_section(self, section): |
| 173 raise NotImplementedError | 246 raise NotImplementedError |
| OLD | NEW |