ui: move configlist parser to config.py
authorJun Wu <quark@fb.com>
Fri, 17 Mar 2017 09:19:56 -0700
changeset 31481 a7c687c35119
parent 31480 07fe520280d2
child 31482 da7d19324b1e
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.
mercurial/config.py
mercurial/ui.py
tests/test-doctest.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 []
--- 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)
--- 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')