177 assert getattr(fp, 'mode', r'rb') == r'rb', ( |
177 assert getattr(fp, 'mode', r'rb') == r'rb', ( |
178 'config files must be opened in binary mode, got fp=%r mode=%r' % ( |
178 'config files must be opened in binary mode, got fp=%r mode=%r' % ( |
179 fp, fp.mode)) |
179 fp, fp.mode)) |
180 self.parse(path, fp.read(), |
180 self.parse(path, fp.read(), |
181 sections=sections, remap=remap, include=self.read) |
181 sections=sections, remap=remap, include=self.read) |
|
182 |
|
183 def parselist(value): |
|
184 """parse a configuration value as a list of comma/space separated strings |
|
185 |
|
186 >>> parselist('this,is "a small" ,test') |
|
187 ['this', 'is', 'a small', 'test'] |
|
188 """ |
|
189 |
|
190 def _parse_plain(parts, s, offset): |
|
191 whitespace = False |
|
192 while offset < len(s) and (s[offset:offset + 1].isspace() |
|
193 or s[offset:offset + 1] == ','): |
|
194 whitespace = True |
|
195 offset += 1 |
|
196 if offset >= len(s): |
|
197 return None, parts, offset |
|
198 if whitespace: |
|
199 parts.append('') |
|
200 if s[offset:offset + 1] == '"' and not parts[-1]: |
|
201 return _parse_quote, parts, offset + 1 |
|
202 elif s[offset:offset + 1] == '"' and parts[-1][-1] == '\\': |
|
203 parts[-1] = parts[-1][:-1] + s[offset:offset + 1] |
|
204 return _parse_plain, parts, offset + 1 |
|
205 parts[-1] += s[offset:offset + 1] |
|
206 return _parse_plain, parts, offset + 1 |
|
207 |
|
208 def _parse_quote(parts, s, offset): |
|
209 if offset < len(s) and s[offset:offset + 1] == '"': # "" |
|
210 parts.append('') |
|
211 offset += 1 |
|
212 while offset < len(s) and (s[offset:offset + 1].isspace() or |
|
213 s[offset:offset + 1] == ','): |
|
214 offset += 1 |
|
215 return _parse_plain, parts, offset |
|
216 |
|
217 while offset < len(s) and s[offset:offset + 1] != '"': |
|
218 if (s[offset:offset + 1] == '\\' and offset + 1 < len(s) |
|
219 and s[offset + 1:offset + 2] == '"'): |
|
220 offset += 1 |
|
221 parts[-1] += '"' |
|
222 else: |
|
223 parts[-1] += s[offset:offset + 1] |
|
224 offset += 1 |
|
225 |
|
226 if offset >= len(s): |
|
227 real_parts = _configlist(parts[-1]) |
|
228 if not real_parts: |
|
229 parts[-1] = '"' |
|
230 else: |
|
231 real_parts[0] = '"' + real_parts[0] |
|
232 parts = parts[:-1] |
|
233 parts.extend(real_parts) |
|
234 return None, parts, offset |
|
235 |
|
236 offset += 1 |
|
237 while offset < len(s) and s[offset:offset + 1] in [' ', ',']: |
|
238 offset += 1 |
|
239 |
|
240 if offset < len(s): |
|
241 if offset + 1 == len(s) and s[offset:offset + 1] == '"': |
|
242 parts[-1] += '"' |
|
243 offset += 1 |
|
244 else: |
|
245 parts.append('') |
|
246 else: |
|
247 return None, parts, offset |
|
248 |
|
249 return _parse_plain, parts, offset |
|
250 |
|
251 def _configlist(s): |
|
252 s = s.rstrip(' ,') |
|
253 if not s: |
|
254 return [] |
|
255 parser, parts, offset = _parse_plain, [''], 0 |
|
256 while parser: |
|
257 parser, parts, offset = parser(parts, s, offset) |
|
258 return parts |
|
259 |
|
260 if value is not None and isinstance(value, bytes): |
|
261 result = _configlist(value.lstrip(' ,\n')) |
|
262 else: |
|
263 result = value |
|
264 return result or [] |