| 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 from collections import OrderedDict | |
| 9 | 10 |
| 10 | 11 |
| 11 class Item(tuple): | 12 class Item(tuple): |
| 12 def __new__(cls, name, value, source): | 13 def __new__(cls, name, value, source): |
| 13 result = super(Item, cls).__new__(cls, (name, value)) | 14 result = super(Item, cls).__new__(cls, (name, value)) |
| 14 result.source = source | 15 result.source = source |
| 15 return result | 16 return result |
| 16 | 17 |
| 17 | 18 |
| 18 class DiffForUnknownOptionError(ConfigParser.Error): | 19 class DiffForUnknownOptionError(ConfigParser.Error): |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 | 151 |
| 151 def option_source(self, section, option): | 152 def option_source(self, section, option): |
| 152 option = self.optionxform(option) | 153 option = self.optionxform(option) |
| 153 try: | 154 try: |
| 154 return self._origin[(section, option)] | 155 return self._origin[(section, option)] |
| 155 except KeyError: | 156 except KeyError: |
| 156 if not self.has_section(section): | 157 if not self.has_section(section): |
| 157 raise ConfigParser.NoSectionError(section) | 158 raise ConfigParser.NoSectionError(section) |
| 158 raise ConfigParser.NoOptionError(option, section) | 159 raise ConfigParser.NoOptionError(option, section) |
| 159 | 160 |
| 161 def as_json_object(self, section): | |
|
Sebastian Noack
2018/04/14 00:33:07
Actually there isn't anything specific to JSON in
tlucas
2018/04/14 08:36:23
You are right - what would you suggest? How about
Sebastian Noack
2018/04/14 08:54:26
Maybe section_as_dict()?
tlucas
2018/04/18 14:46:04
Done.
| |
| 162 r"""Parse a given section into a JSON object. | |
| 163 | |
| 164 Parse arbitrary key/value pairs from 'section' of the current | |
| 165 configuration into a nested JSON object. | |
| 166 | |
| 167 The following rules need to be considered: | |
| 168 | |
| 169 * An option's key may be declared as a series of nested dictionary keys, | |
| 170 seperated by '.'. | |
| 171 * An option's key must end with '[]', to mark this option as an array | |
|
Sebastian Noack
2018/04/14 00:33:06
Nit: Please make all bullet point end with punctat
Sebastian Noack
2018/04/14 00:33:07
I just noticed, we could identify arrays by the pr
tlucas
2018/04/14 08:36:23
The bullet points: ack
The newlines: Nice idea, w
tlucas
2018/04/18 14:46:04
Done.
| |
| 172 * When an option is marked as an array, no other nested objects may | |
| 173 follow | |
| 174 * An array is expandable by the ConfigParser's '+=' token. | |
| 175 * Values may be marked as `number` or `bool` by prefixing them | |
| 176 accordingly: | |
| 177 * bool:<value> | |
| 178 * number:<value> | |
| 179 | |
| 180 Example: | |
| 181 | |
| 182 { | |
| 183 "good": false, | |
| 184 "bar": { | |
| 185 "baz": ["bar", "bazinga"] | |
| 186 foo = foo }, | |
| 187 bar.baz[] = bar "is": { | |
| 188 baz.foo = a "integer": 1, | |
| 189 baz.z[] = b bool:true "float": 1.4 | |
| 190 bar.baz[] += bazinga ==> }, | |
| 191 bad = bool:true "baz": { | |
| 192 good = bool:false "foo": "a", | |
| 193 is.integer = number:1 "z": ["b", true] | |
| 194 is.float = number:1.4 }, | |
| 195 "bad": true | |
| 196 "foo": "foo" | |
| 197 } | |
| 198 """ | |
| 199 def parse_values(v): | |
| 200 if isinstance(v, list): | |
| 201 return [parse_values(x) for x in v] | |
| 202 | |
| 203 if v.startswith('number:'): | |
| 204 v = v.split(':', 1)[1] | |
| 205 try: | |
| 206 v = int(v) | |
| 207 except ValueError: | |
| 208 v = float(v) | |
| 209 elif v == 'bool:true': | |
| 210 v = True | |
| 211 elif v == 'bool:false': | |
| 212 v = False | |
| 213 return v | |
| 214 | |
| 215 def setdefault_recursive(target, arr): | |
| 216 if len(arr) == 2: | |
| 217 target.setdefault(arr[0], parse_values(arr[1])) | |
| 218 else: | |
| 219 current = target.setdefault(arr[0], OrderedDict()) | |
| 220 setdefault_recursive(current, arr[1:]) | |
| 221 | |
| 222 data = self.items(section) | |
| 223 result = OrderedDict() | |
| 224 | |
| 225 for k, v in data: | |
| 226 if k.endswith('[]'): | |
| 227 k = k[:-2] | |
| 228 v = v.split() | |
|
Sebastian Noack
2018/04/14 00:33:06
We might want to strip by newlines, so that array
tlucas
2018/04/14 08:36:23
Acknowledged.
tlucas
2018/04/18 14:46:04
Done.
| |
| 229 | |
| 230 setdefault_recursive(result, k.split('.') + [v]) | |
|
Sebastian Noack
2018/04/14 00:33:06
It seems weird/unnecessary to merge the key segmen
tlucas
2018/04/14 08:36:23
Not at all, will change.
tlucas
2018/04/18 14:46:04
Done.
| |
| 231 | |
| 232 return result | |
| 233 | |
| 160 def readfp(self, fp, filename=None): | 234 def readfp(self, fp, filename=None): |
| 161 raise NotImplementedError | 235 raise NotImplementedError |
| 162 | 236 |
| 163 def set(self, section, option, value=None): | 237 def set(self, section, option, value=None): |
| 164 raise NotImplementedError | 238 raise NotImplementedError |
| 165 | 239 |
| 166 def add_section(self, section): | 240 def add_section(self, section): |
| 167 raise NotImplementedError | 241 raise NotImplementedError |
| 168 | 242 |
| 169 def remove_option(self, section, option): | 243 def remove_option(self, section, option): |
| 170 raise NotImplementedError | 244 raise NotImplementedError |
| 171 | 245 |
| 172 def remove_section(self, section): | 246 def remove_section(self, section): |
| 173 raise NotImplementedError | 247 raise NotImplementedError |
| OLD | NEW |