comparison mercurial/config.py @ 31481:a7c687c35119

ui: move configlist parser to config.py The list parser is complex and reusable without ui. Let's move it to config.py. This allows us to parse a list from a "pure" config object without going through ui. Like, we can make "_trustusers" calculated from raw configs, instead of making sure it's synchronized by calling "fixconfig"s.
author Jun Wu <quark@fb.com>
date Fri, 17 Mar 2017 09:19:56 -0700
parents d30fb3de4b40
children 6a773d3050c9
comparison
equal deleted inserted replaced
31480:07fe520280d2 31481:a7c687c35119
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 []