--- a/mercurial/cmdutil.py Sat Jan 27 13:14:06 2018 +0900
+++ b/mercurial/cmdutil.py Sat Jan 27 13:33:31 2018 +0900
@@ -1594,7 +1594,7 @@
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')
--- a/mercurial/ui.py Sat Jan 27 13:14:06 2018 +0900
+++ b/mercurial/ui.py Sat Jan 27 13:33:31 2018 +0900
@@ -148,14 +148,10 @@
}
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 @@
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
--- a/mercurial/util.py Sat Jan 27 13:14:06 2018 +0900
+++ b/mercurial/util.py Sat Jan 27 13:33:31 2018 +0900
@@ -183,6 +183,39 @@
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:
--- a/mercurial/wireprotoserver.py Sat Jan 27 13:14:06 2018 +0900
+++ b/mercurial/wireprotoserver.py Sat Jan 27 13:33:31 2018 +0900
@@ -123,10 +123,7 @@
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(