# HG changeset patch # User Jun Wu # Date 1489767596 25200 # Node ID a7c687c351194bae8c15e70823fda596a173beda # Parent 07fe520280d2d5fe0c69e008e528e456587a9949 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. diff -r 07fe520280d2 -r a7c687c35119 mercurial/config.py --- a/mercurial/config.py Fri Mar 17 09:58:49 2017 -0700 +++ b/mercurial/config.py Fri Mar 17 09:19:56 2017 -0700 @@ -179,3 +179,86 @@ fp, fp.mode)) self.parse(path, fp.read(), sections=sections, remap=remap, include=self.read) + +def parselist(value): + """parse a configuration value as a list of comma/space separated strings + + >>> parselist('this,is "a small" ,test') + ['this', 'is', 'a small', 'test'] + """ + + def _parse_plain(parts, s, offset): + whitespace = False + while offset < len(s) and (s[offset:offset + 1].isspace() + or s[offset:offset + 1] == ','): + whitespace = True + offset += 1 + if offset >= len(s): + return None, parts, offset + if whitespace: + parts.append('') + if s[offset:offset + 1] == '"' and not parts[-1]: + return _parse_quote, parts, offset + 1 + elif s[offset:offset + 1] == '"' and parts[-1][-1] == '\\': + parts[-1] = parts[-1][:-1] + s[offset:offset + 1] + return _parse_plain, parts, offset + 1 + parts[-1] += s[offset:offset + 1] + return _parse_plain, parts, offset + 1 + + def _parse_quote(parts, s, offset): + if offset < len(s) and s[offset:offset + 1] == '"': # "" + parts.append('') + offset += 1 + while offset < len(s) and (s[offset:offset + 1].isspace() or + s[offset:offset + 1] == ','): + offset += 1 + return _parse_plain, parts, offset + + while offset < len(s) and s[offset:offset + 1] != '"': + if (s[offset:offset + 1] == '\\' and offset + 1 < len(s) + and s[offset + 1:offset + 2] == '"'): + offset += 1 + parts[-1] += '"' + else: + parts[-1] += s[offset:offset + 1] + offset += 1 + + if offset >= len(s): + real_parts = _configlist(parts[-1]) + if not real_parts: + parts[-1] = '"' + else: + real_parts[0] = '"' + real_parts[0] + parts = parts[:-1] + parts.extend(real_parts) + return None, parts, offset + + offset += 1 + while offset < len(s) and s[offset:offset + 1] in [' ', ',']: + offset += 1 + + if offset < len(s): + if offset + 1 == len(s) and s[offset:offset + 1] == '"': + parts[-1] += '"' + offset += 1 + else: + parts.append('') + else: + return None, parts, offset + + return _parse_plain, parts, offset + + def _configlist(s): + s = s.rstrip(' ,') + if not s: + return [] + parser, parts, offset = _parse_plain, [''], 0 + while parser: + parser, parts, offset = parser(parts, s, offset) + return parts + + if value is not None and isinstance(value, bytes): + result = _configlist(value.lstrip(' ,\n')) + else: + result = value + return result or [] diff -r 07fe520280d2 -r a7c687c35119 mercurial/ui.py --- a/mercurial/ui.py Fri Mar 17 09:58:49 2017 -0700 +++ b/mercurial/ui.py Fri Mar 17 09:19:56 2017 -0700 @@ -562,85 +562,11 @@ >>> u.configlist(s, 'list1') ['this', 'is', 'a small', 'test'] """ - - def _parse_plain(parts, s, offset): - whitespace = False - while offset < len(s) and (s[offset:offset + 1].isspace() - or s[offset:offset + 1] == ','): - whitespace = True - offset += 1 - if offset >= len(s): - return None, parts, offset - if whitespace: - parts.append('') - if s[offset:offset + 1] == '"' and not parts[-1]: - return _parse_quote, parts, offset + 1 - elif s[offset:offset + 1] == '"' and parts[-1][-1] == '\\': - parts[-1] = parts[-1][:-1] + s[offset:offset + 1] - return _parse_plain, parts, offset + 1 - parts[-1] += s[offset:offset + 1] - return _parse_plain, parts, offset + 1 - - def _parse_quote(parts, s, offset): - if offset < len(s) and s[offset:offset + 1] == '"': # "" - parts.append('') - offset += 1 - while offset < len(s) and (s[offset:offset + 1].isspace() or - s[offset:offset + 1] == ','): - offset += 1 - return _parse_plain, parts, offset - - while offset < len(s) and s[offset:offset + 1] != '"': - if (s[offset:offset + 1] == '\\' and offset + 1 < len(s) - and s[offset + 1:offset + 2] == '"'): - offset += 1 - parts[-1] += '"' - else: - parts[-1] += s[offset:offset + 1] - offset += 1 - - if offset >= len(s): - real_parts = _configlist(parts[-1]) - if not real_parts: - parts[-1] = '"' - else: - real_parts[0] = '"' + real_parts[0] - parts = parts[:-1] - parts.extend(real_parts) - return None, parts, offset - - offset += 1 - while offset < len(s) and s[offset:offset + 1] in [' ', ',']: - offset += 1 - - if offset < len(s): - if offset + 1 == len(s) and s[offset:offset + 1] == '"': - parts[-1] += '"' - offset += 1 - else: - parts.append('') - else: - return None, parts, offset - - return _parse_plain, parts, offset - - def _configlist(s): - s = s.rstrip(' ,') - if not s: - return [] - parser, parts, offset = _parse_plain, [''], 0 - while parser: - parser, parts, offset = parser(parts, s, offset) - return parts - - result = self.config(section, name, untrusted=untrusted) - if result is None: - result = default or [] - if isinstance(result, bytes): - result = _configlist(result.lstrip(' ,\n')) - if result is None: - result = default or [] - return result + # default is not always a list + if isinstance(default, bytes): + default = config.parselist(default) + return self.configwith(config.parselist, section, name, default or [], + 'list', untrusted) def hasconfig(self, section, name, untrusted=False): return self._data(untrusted).hasitem(section, name) diff -r 07fe520280d2 -r a7c687c35119 tests/test-doctest.py --- a/tests/test-doctest.py Fri Mar 17 09:58:49 2017 -0700 +++ b/tests/test-doctest.py Fri Mar 17 09:19:56 2017 -0700 @@ -23,6 +23,7 @@ testmod('mercurial.changegroup') testmod('mercurial.changelog') +testmod('mercurial.config') testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE) testmod('mercurial.dispatch') testmod('mercurial.encoding')