Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: chainedconfigparser.py

Issue 29743581: Issue 6552 - Support arbitrary manifest values (Closed) Base URL: https://hg.adblockplus.org/buildtools/file/a3db4a1a49e8
Left Patch Set: Created April 12, 2018, 1:46 p.m.
Right Patch Set: Created April 19, 2018, 11:42 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | packagerChrome.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
10 9
11 10
12 class Item(tuple): 11 class Item(tuple):
13 def __new__(cls, name, value, source): 12 def __new__(cls, name, value, source):
14 result = super(Item, cls).__new__(cls, (name, value)) 13 result = super(Item, cls).__new__(cls, (name, value))
15 result.source = source 14 result.source = source
16 return result 15 return result
17 16
18 17
19 class DiffForUnknownOptionError(ConfigParser.Error): 18 class DiffForUnknownOptionError(ConfigParser.Error):
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 is_addition = option.endswith('+') 86 is_addition = option.endswith('+')
88 is_diff = is_addition or option.endswith('-') 87 is_diff = is_addition or option.endswith('-')
89 88
90 if is_diff: 89 if is_diff:
91 option = option[:-1].rstrip() 90 option = option[:-1].rstrip()
92 try: 91 try:
93 orig_value = self.get(section, option) 92 orig_value = self.get(section, option)
94 except ConfigParser.NoOptionError: 93 except ConfigParser.NoOptionError:
95 raise DiffForUnknownOptionError(option, section) 94 raise DiffForUnknownOptionError(option, section)
96 95
97 orig_values = orig_value.split() 96 orig_values = orig_value.splitlines()
98 diff_values = value.split() 97 diff_values = value.splitlines()
99 98
100 if is_addition: 99 if is_addition:
101 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]
102 else: 101 else:
103 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]
104 103
105 value = ' '.join(new_values) 104 value = '\n'.join(new_values)
106 105
107 return is_diff, option, value 106 return is_diff, option, value
108 107
109 def _process_parsers(self, parsers): 108 def _process_parsers(self, parsers):
110 for parser, filename in parsers: 109 for parser, filename in parsers:
111 for section in parser.sections(): 110 for section in parser.sections():
112 if not self.has_section(section): 111 if not self.has_section(section):
113 try: 112 try:
114 ConfigParser.SafeConfigParser.add_section(self, section) 113 ConfigParser.SafeConfigParser.add_section(self, section)
115 except ValueError: 114 except ValueError:
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 150
152 def option_source(self, section, option): 151 def option_source(self, section, option):
153 option = self.optionxform(option) 152 option = self.optionxform(option)
154 try: 153 try:
155 return self._origin[(section, option)] 154 return self._origin[(section, option)]
156 except KeyError: 155 except KeyError:
157 if not self.has_section(section): 156 if not self.has_section(section):
158 raise ConfigParser.NoSectionError(section) 157 raise ConfigParser.NoSectionError(section)
159 raise ConfigParser.NoOptionError(option, section) 158 raise ConfigParser.NoOptionError(option, section)
160 159
161 def as_json_object(self, section): 160 def serialize_section_if_present(self, section, base):
Vasily Kuznetsov 2018/04/19 14:40:10 What this method does seems more like parsing than
Sebastian Noack 2018/04/19 15:02:59 In the end it gets serialized (as JSON). But you m
162 r"""Parse a given section into a JSON object. 161 """Serialize a given section as a dictionary into `base`.
163 162
164 Parse arbitrary key/value pairs from 'section' of the current 163 Parse arbitrary key/value pairs from 'section' of the current
165 configuration into a nested JSON object. 164 configuration into a dictionary and deep merge it into `base`.
166 165
167 The following rules need to be considered: 166 The following rules need to be considered:
168 167
169 * An option's key may be declared as a series of nested dictionary keys, 168 * An option's key may be declared as a series of nested dictionary keys,
170 seperated by '.'. 169 seperated by '.'.
171 * An option's key must end with '[]', to mark this option as an array 170 * Declaring an option's value in a new line (even if only one is given)
172 * When an option is marked as an array, no other nested objects may 171 will define the option's value as a list.
173 follow 172 * When an option's value is defined as a list, no other nested
174 * An array is expandable by the ConfigParser's '+=' token. 173 objects may follow.
175 * Values may be marked as special types by appending '\{code}': 174 * A list is expandable by the ConfigParser's '+=' token (Note: A
176 * \b - boolean 175 previously declared string will be converted into a list).
177 * \i - integer 176 * Values may be marked as `number` or `bool` by prefixing them
178 * \f - float 177 accordingly (this also applies to values in a list):
178 * bool:<value>
179 * number:<value>
179 180
180 Example: 181 Example:
181 182 {
182 { 183 foo = foo "foo": "foo",
183 "good": false, 184 asd = "asd": ["asd"],
184 "bar": { 185 asd "bar": {
185 "baz": ["bar", "bazinga"] 186 bar.baz = a "baz": ["a", "c", "d"]
186 foo = foo }, 187 baz.foo = a },
187 bar.baz[] = bar "is": { 188 baz.z = "baz": {
188 baz.foo = a "integer": 1, 189 bar "foo": "a",
189 baz.z[] = b "float": 1.4 190 bool:true ===> "z": ["bar", true]
190 bar.baz[] += bazinga ==> }, 191 bar.baz += },
191 bad = true\b "baz": { 192 c "bad": true,
192 good = f\b "foo": "a", 193 d "good": false,
193 is.integer = 1\i "z": ["b"] 194 bad = bool:true "is": {
194 is.float = 1.4\f }, 195 good = bool:false "integer": 1,
195 "bad": true 196 is.integer = number:1 "float": 1.4
196 "foo": "foo" 197 is.float = number:1.4 }
197 } 198 }
198 """ 199 """
199 def parse_values(v): 200 def parse_value(v):
200 import distutils 201 if v.startswith('number:'):
201 if isinstance(v, list): 202 v = v.split(':', 1)[1]
202 return [parse_values(x) for x in v] 203 try:
203 204 return int(v)
204 mapping = { 205 except ValueError:
205 '\\b': lambda x: bool(distutils.util.strtobool(x)), 206 return float(v)
206 '\\i': int, 207 if v == 'bool:true':
207 '\\f': float, 208 return True
208 } 209 if v == 'bool:false':
209 if '\\' in v: 210 return False
210 return mapping[v[-2:]](v[:-2])
211 return v 211 return v
212 212
213 def setdefault_recursive(target, arr): 213 if self.has_section(section):
214 if len(arr) == 2: 214 for k, v in self.items(section):
215 target.setdefault(arr[0], parse_values(arr[1])) 215 parents = k.split('.')
216 else: 216 tail = parents.pop()
217 current = target.setdefault(arr[0], OrderedDict()) 217 current = base
218 setdefault_recursive(current, arr[1:]) 218 for name in parents:
219 219 current = base.setdefault(name, {})
220 data = self.items(section) 220
221 result = OrderedDict() 221 if '\n' in v:
222 222 current[tail] = [parse_value(x) for x in v.splitlines() if x ]
223 for k, v in data: 223 else:
224 if k.endswith('[]'): 224 current[tail] = parse_value(v)
225 k = k[:-2]
226 v = v.split()
227
228 setdefault_recursive(result, k.split('.') + [v])
229
230 return result
231 225
232 def readfp(self, fp, filename=None): 226 def readfp(self, fp, filename=None):
233 raise NotImplementedError 227 raise NotImplementedError
234 228
235 def set(self, section, option, value=None): 229 def set(self, section, option, value=None):
236 raise NotImplementedError 230 raise NotImplementedError
237 231
238 def add_section(self, section): 232 def add_section(self, section):
239 raise NotImplementedError 233 raise NotImplementedError
240 234
241 def remove_option(self, section, option): 235 def remove_option(self, section, option):
242 raise NotImplementedError 236 raise NotImplementedError
243 237
244 def remove_section(self, section): 238 def remove_section(self, section):
245 raise NotImplementedError 239 raise NotImplementedError
LEFTRIGHT
« no previous file | packagerChrome.py » ('j') | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld