Patchwork [4,of,9] py3: factor out helpers to apply string conversion recursively

login
register
mail settings
Submitter Yuya Nishihara
Date Feb. 3, 2018, 8:36 a.m.
Message ID <85ed4b6e56fdceaae2a7.1517646975@mimosa>
Download mbox | patch
Permalink /patch/27264/
State Accepted
Headers show

Comments

Yuya Nishihara - Feb. 3, 2018, 8:36 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1517027611 -32400
#      Sat Jan 27 13:33:31 2018 +0900
# Node ID 85ed4b6e56fdceaae2a7deb5b129ea8ee24ee8c5
# Parent  9b3d33f9e615002d0a7da4d0d04ec04ae5c04cff
py3: factor out helpers to apply string conversion recursively

Patch

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1594,7 +1594,7 @@  def showmarker(fm, marker, index=None):
     fm.write('date', '(%s) ', fm.formatdate(marker.date()))
     meta = marker.metadata().copy()
     meta.pop('date', None)
-    smeta = {_maybebytestr(k): _maybebytestr(v) for k, v in meta.iteritems()}
+    smeta = util.rapply(_maybebytestr, meta)
     fm.write('metadata', '{%s}', fm.formatdict(smeta, fmt='%r: %r', sep=', '))
     fm.plain('\n')
 
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -148,14 +148,10 @@  b"""# example system-wide hg config (see
 }
 
 def _maybestrurl(maybebytes):
-    if maybebytes is None:
-        return None
-    return pycompat.strurl(maybebytes)
+    return util.rapply(pycompat.strurl, maybebytes)
 
 def _maybebytesurl(maybestr):
-    if maybestr is None:
-        return None
-    return pycompat.bytesurl(maybestr)
+    return util.rapply(pycompat.bytesurl, maybestr)
 
 class httppasswordmgrdbproxy(object):
     """Delays loading urllib2 until it's needed."""
@@ -168,18 +164,14 @@  class httppasswordmgrdbproxy(object):
         return self._mgr
 
     def add_password(self, realm, uris, user, passwd):
-        if isinstance(uris, tuple):
-            uris = tuple(_maybestrurl(u) for u in uris)
-        else:
-            uris = _maybestrurl(uris)
         return self._get_mgr().add_password(
-            _maybestrurl(realm), uris,
+            _maybestrurl(realm), _maybestrurl(uris),
             _maybestrurl(user), _maybestrurl(passwd))
 
     def find_user_password(self, realm, uri):
-        return tuple(_maybebytesurl(v) for v in
-                     self._get_mgr().find_user_password(_maybestrurl(realm),
-                                                        _maybestrurl(uri)))
+        mgr = self._get_mgr()
+        return _maybebytesurl(mgr.find_user_password(_maybestrurl(realm),
+                                                     _maybestrurl(uri)))
 
 def _catchterm(*args):
     raise error.SignalInterrupt
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -183,6 +183,39 @@  os.stat_float_times(False)
 def safehasattr(thing, attr):
     return getattr(thing, attr, _notset) is not _notset
 
+def _rapply(f, xs):
+    if xs is None:
+        # assume None means non-value of optional data
+        return xs
+    if isinstance(xs, (list, set, tuple)):
+        return type(xs)(_rapply(f, x) for x in xs)
+    if isinstance(xs, dict):
+        return type(xs)((_rapply(f, k), _rapply(f, v)) for k, v in xs.items())
+    return f(xs)
+
+def rapply(f, xs):
+    """Apply function recursively to every item preserving the data structure
+
+    >>> def f(x):
+    ...     return 'f(%s)' % x
+    >>> rapply(f, None) is None
+    True
+    >>> rapply(f, 'a')
+    'f(a)'
+    >>> rapply(f, {'a'}) == {'f(a)'}
+    True
+    >>> rapply(f, ['a', 'b', None, {'c': 'd'}, []])
+    ['f(a)', 'f(b)', None, {'f(c)': 'f(d)'}, []]
+
+    >>> xs = [object()]
+    >>> rapply(pycompat.identity, xs) is xs
+    True
+    """
+    if f is pycompat.identity:
+        # fast path mainly for py2
+        return xs
+    return _rapply(f, xs)
+
 def bytesinput(fin, fout, *args, **kwargs):
     sin, sout = sys.stdin, sys.stdout
     try:
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -123,10 +123,7 @@  class webproto(abstractserverproto):
         return [data[k] for k in keys]
 
     def _args(self):
-        args = self._req.form.copy()
-        if pycompat.ispy3:
-            args = {k.encode('ascii'): [v.encode('ascii') for v in vs]
-                    for k, vs in args.items()}
+        args = util.rapply(pycompat.bytesurl, self._req.form.copy())
         postlen = int(self._req.env.get(r'HTTP_X_HGARGS_POST', 0))
         if postlen:
             args.update(cgi.parse_qs(