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): |
| 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 |
| 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 special types by appending '\{code}': |
| 176 * \b - boolean |
| 177 * \i - integer |
| 178 * \f - float |
| 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 "float": 1.4 |
| 190 bar.baz[] += bazinga ==> }, |
| 191 bad = true\b "baz": { |
| 192 good = f\b "foo": "a", |
| 193 is.integer = 1\i "z": ["b"] |
| 194 is.float = 1.4\f }, |
| 195 "bad": true |
| 196 "foo": "foo" |
| 197 } |
| 198 """ |
| 199 def parse_values(v): |
| 200 import distutils |
| 201 if isinstance(v, list): |
| 202 return [parse_values(x) for x in v] |
| 203 |
| 204 mapping = { |
| 205 '\\b': lambda x: bool(distutils.util.strtobool(x)), |
| 206 '\\i': int, |
| 207 '\\f': float, |
| 208 } |
| 209 if '\\' in v: |
| 210 return mapping[v[-2:]](v[:-2]) |
| 211 return v |
| 212 |
| 213 def setdefault_recursive(target, arr): |
| 214 if len(arr) == 2: |
| 215 target.setdefault(arr[0], parse_values(arr[1])) |
| 216 else: |
| 217 current = target.setdefault(arr[0], OrderedDict()) |
| 218 setdefault_recursive(current, arr[1:]) |
| 219 |
| 220 data = self.items(section) |
| 221 result = OrderedDict() |
| 222 |
| 223 for k, v in data: |
| 224 if k.endswith('[]'): |
| 225 k = k[:-2] |
| 226 v = v.split() |
| 227 |
| 228 setdefault_recursive(result, k.split('.') + [v]) |
| 229 |
| 230 return result |
| 231 |
160 def readfp(self, fp, filename=None): | 232 def readfp(self, fp, filename=None): |
161 raise NotImplementedError | 233 raise NotImplementedError |
162 | 234 |
163 def set(self, section, option, value=None): | 235 def set(self, section, option, value=None): |
164 raise NotImplementedError | 236 raise NotImplementedError |
165 | 237 |
166 def add_section(self, section): | 238 def add_section(self, section): |
167 raise NotImplementedError | 239 raise NotImplementedError |
168 | 240 |
169 def remove_option(self, section, option): | 241 def remove_option(self, section, option): |
170 raise NotImplementedError | 242 raise NotImplementedError |
171 | 243 |
172 def remove_section(self, section): | 244 def remove_section(self, section): |
173 raise NotImplementedError | 245 raise NotImplementedError |
OLD | NEW |