Patchwork [config,easy] ui: move configlist parser to config.py

login
register
mail settings
Submitter Jun Wu
Date March 17, 2017, 4:34 p.m.
Message ID <012156d455f06480e382.1489768449@localhost.localdomain>
Download mbox | patch
Permalink /patch/19424/
State Accepted
Headers show

Comments

Jun Wu - March 17, 2017, 4:34 p.m.
# HG changeset patch
# User Jun Wu <quark@fb.com>
# Date 1489767596 25200
#      Fri Mar 17 09:19:56 2017 -0700
# Node ID 012156d455f06480e3825edb450fcb37b63e9a1c
# Parent  96929bd6e58d29bc3d44e1db7c1283f224bd1dc1
# Available At https://bitbucket.org/quark-zju/hg-draft
#              hg pull https://bitbucket.org/quark-zju/hg-draft -r 012156d455f0
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.
Yuya Nishihara - March 18, 2017, 5:11 a.m.
On Fri, 17 Mar 2017 09:34:09 -0700, Jun Wu wrote:
> # HG changeset patch
> # User Jun Wu <quark@fb.com>
> # Date 1489767596 25200
> #      Fri Mar 17 09:19:56 2017 -0700
> # Node ID 012156d455f06480e3825edb450fcb37b63e9a1c
> # Parent  96929bd6e58d29bc3d44e1db7c1283f224bd1dc1
> # Available At https://bitbucket.org/quark-zju/hg-draft
> #              hg pull https://bitbucket.org/quark-zju/hg-draft -r 012156d455f0
> ui: move configlist parser to config.py

Nice. Queued, thanks.

I've updated test-doctest.py to include config.py.
Jun Wu - March 18, 2017, 3:30 p.m.
Excerpts from Yuya Nishihara's message of 2017-03-18 14:11:33 +0900:
> On Fri, 17 Mar 2017 09:34:09 -0700, Jun Wu wrote:
> > # HG changeset patch
> > # User Jun Wu <quark@fb.com>
> > # Date 1489767596 25200
> > #      Fri Mar 17 09:19:56 2017 -0700
> > # Node ID 012156d455f06480e3825edb450fcb37b63e9a1c
> > # Parent  96929bd6e58d29bc3d44e1db7c1283f224bd1dc1
> > # Available At https://bitbucket.org/quark-zju/hg-draft 
> > #              hg pull https://bitbucket.org/quark-zju/hg-draft  -r 012156d455f0
> > ui: move configlist parser to config.py
> 
> Nice. Queued, thanks.
> 
> I've updated test-doctest.py to include config.py.

Thanks! I thought doctest.py discovers files automatically.
Gregory Szorc - March 18, 2017, 5:19 p.m.
On Sat, Mar 18, 2017 at 8:30 AM, Jun Wu <quark@fb.com> wrote:

> Excerpts from Yuya Nishihara's message of 2017-03-18 14:11:33 +0900:
> > On Fri, 17 Mar 2017 09:34:09 -0700, Jun Wu wrote:
> > > # HG changeset patch
> > > # User Jun Wu <quark@fb.com>
> > > # Date 1489767596 25200
> > > #      Fri Mar 17 09:19:56 2017 -0700
> > > # Node ID 012156d455f06480e3825edb450fcb37b63e9a1c
> > > # Parent  96929bd6e58d29bc3d44e1db7c1283f224bd1dc1
> > > # Available At https://bitbucket.org/quark-zju/hg-draft
> > > #              hg pull https://bitbucket.org/quark-zju/hg-draft  -r
> 012156d455f0
> > > ui: move configlist parser to config.py
> >
> > Nice. Queued, thanks.
> >
> > I've updated test-doctest.py to include config.py.
>
> Thanks! I thought doctest.py discovers files automatically.
>

I think it would be an effective use of someone's time to change this or
figure out why we can't.

Patch

diff --git a/mercurial/config.py b/mercurial/config.py
--- a/mercurial/config.py
+++ b/mercurial/config.py
@@ -180,2 +180,85 @@  class config(object):
         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 --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -563,83 +563,9 @@  class ui(object):
         ['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):