[PATCH hglib] hglib: Wrap all application string literals in util.b() (issue4520)

Brett Cannon brett at python.org
Sun Mar 8 12:08:52 CDT 2015


# HG changeset patch
# User Brett Cannon <brett at python.org>
# Date 1425834517 14400
#      Sun Mar 08 13:08:37 2015 -0400
# Node ID 06babaeea98aaa7c58502bf3d9879fac72fd8d46
# Parent  ea80bd2775f6612399eb3aedf2444fcb11b0cf8a
hglib: Wrap all application string literals in util.b() (issue4520)

Conversion also included changing use of string interpolation to
string concatenation as bytes interpolation does not exist in Python
3. Indexing related to bytes was also changed to length-1 bytes
through slicing as Python 3 returns an int in this instance.

Tests have not been switched to using util.b() so that the change to
application code can be independently verified as not being broken.

diff -r ea80bd2775f6 -r 06babaeea98a hglib/client.py
--- a/hglib/client.py	Sat Mar 07 10:08:52 2015 -0500
+++ b/hglib/client.py	Sun Mar 08 13:08:37 2015 -0400
@@ -1,7 +1,7 @@
 import subprocess, os, struct, cStringIO, re, datetime
 import hglib, error, util, templates, merge, context
 
-from util import cmdbuilder
+from util import b, cmdbuilder
 
 class revision(tuple):
     def __new__(cls, rev, node, tags, branch, author, desc, date):
@@ -72,11 +72,11 @@
     def _readhello(self):
         """ read the hello message the server sends when started """
         ch, msg = self._readchannel()
-        assert ch == 'o'
+        assert ch == b('o')
 
-        msg = msg.split('\n')
+        msg = msg.split(b('\n'))
 
-        self.capabilities = msg[0][len('capabilities: '):]
+        self.capabilities = msg[0][len(b('capabilities: ')):]
         if not self.capabilities:
             raise error.ResponseError(
                 "bad hello message: expected 'capabilities: '"
@@ -85,9 +85,9 @@
         self.capabilities = set(self.capabilities.split())
 
         # at the very least the server should be able to run commands
-        assert 'runcommand' in self.capabilities
+        assert b('runcommand') in self.capabilities
 
-        self._encoding = msg[1][len('encoding: '):]
+        self._encoding = msg[1][len(b('encoding: ')):]
         if not self._encoding:
             raise error.ResponseError("bad hello message: expected 'encoding: '"
                                       ", got %r" % msg[1])
@@ -97,7 +97,7 @@
         if not data:
             raise error.ServerError()
         channel, length = struct.unpack(hgclient.outputfmt, data)
-        if channel in 'IL':
+        if channel in b('IL'):
             return channel, length
         else:
             return channel, self.server.stdout.read(length)
@@ -110,7 +110,7 @@
         revs = []
         for rev in util.grouper(7, splitted):
             # truncate the timezone and convert to a local datetime
-            posixtime = float(rev[6].split('.', 1)[0])
+            posixtime = float(rev[6].split(b('.'), 1)[0])
             dt = datetime.datetime.fromtimestamp(posixtime)
             revs.append(revision(rev[0], rev[1], rev[2], rev[3],
                                  rev[4], rev[5], dt))
@@ -125,8 +125,9 @@
         if not self.server:
             raise ValueError("server not connected")
 
-        self.server.stdin.write('runcommand\n')
-        writeblock('\0'.join(args))
+        self.server.stdin.write(b('runcommand\n'))
+
+        writeblock(b('\0').join(args))
 
         while True:
             channel, data = self._readchannel()
@@ -138,7 +139,7 @@
             elif channel in outchannels:
                 outchannels[channel](data)
             # result channel, command finished
-            elif channel == 'r':
+            elif channel == b('r'):
                 return struct.unpack(hgclient.retfmt, data)[0]
             # a channel that we don't know and can't ignore
             elif channel.isupper():
@@ -162,18 +163,17 @@
         input is used to reply to bulk data requests by the server
         It receives the max number of bytes to return
         """
-
         out, err = cStringIO.StringIO(), cStringIO.StringIO()
-        outchannels = {'o' : out.write, 'e' : err.write}
+        outchannels = {b('o') : out.write, b('e') : err.write}
 
         inchannels = {}
         if prompt is not None:
             def func(size):
                 reply = prompt(size, out.getvalue())
-                return str(reply)
-            inchannels['L'] = func
+                return reply
+            inchannels[b('L')] = func
         if input is not None:
-            inchannels['I'] = input
+            inchannels[b('I')] = input
 
         ret = self.runcommand(args, inchannels, outchannels)
         out, err = out.getvalue(), err.getvalue()
@@ -222,7 +222,7 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('add', n=dryrun, S=subrepos, I=include, X=exclude,
+        args = cmdbuilder(b('add'), n=dryrun, S=subrepos, I=include, X=exclude,
                           *files)
 
         eh = util.reterrorhandler(args)
@@ -257,7 +257,7 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('addremove', s=similarity, n=dryrun, I=include,
+        args = cmdbuilder(b('addremove'), s=similarity, n=dryrun, I=include,
                           X=exclude, *files)
 
         eh = util.reterrorhandler(args)
@@ -289,7 +289,7 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('annotate', r=rev, no_follow=nofollow, a=text,
+        args = cmdbuilder(b('annotate'), r=rev, no_follow=nofollow, a=text,
                           u=user, f=file, d=date, n=number, c=changeset,
                           l=line, v=verbose, I=include, X=exclude,
                           hidden=self.hidden, *files)
@@ -297,7 +297,7 @@
         out = self.rawcommand(args)
 
         for line in out.splitlines():
-            yield tuple(line.split(': ', 1))
+            yield tuple(line.split(b(': '), 1))
 
     def archive(self, dest, rev=None, nodecode=False, prefix=None, type=None,
                 subrepos=False, include=None, exclude=None):
@@ -334,7 +334,7 @@
         exclude - exclude names matching the given patterns
 
         """
-        args = cmdbuilder('archive', dest, r=rev, no_decode=nodecode, p=prefix,
+        args = cmdbuilder(b('archive'), dest, r=rev, no_decode=nodecode, p=prefix,
                           t=type, S=subrepos, I=include, X=exclude,
                           hidden=self.hidden)
 
@@ -362,8 +362,8 @@
         if message and logfile:
             raise ValueError("cannot specify both a message and a logfile")
 
-        args = cmdbuilder('backout', r=rev, merge=merge, parent=parent, t=tool,
-                          m=message, l=logfile, d=date, u=user,
+        args = cmdbuilder(b('backout'), r=rev, merge=merge, parent=parent,
+                          t=tool, m=message, l=logfile, d=date, u=user,
                           hidden=self.hidden)
 
         self.rawcommand(args)
@@ -381,7 +381,7 @@
         inactive - do not mark the new bookmark active
         rename - rename the bookmark given by rename to name
         """
-        args = cmdbuilder('bookmark', name, r=rev, f=force, d=delete,
+        args = cmdbuilder(b('bookmark'), name, r=rev, f=force, d=delete,
                           i=inactive, m=rename)
 
         self.rawcommand(args)
@@ -393,18 +393,18 @@
 
         If there isn't a current one, -1 is returned as the index.
         """
-        args = cmdbuilder('bookmarks', hidden=self.hidden)
+        args = cmdbuilder(b('bookmarks'), hidden=self.hidden)
         out = self.rawcommand(args)
 
         bms = []
         current = -1
-        if out.rstrip() != 'no bookmarks set':
+        if out.rstrip() != b('no bookmarks set'):
             for line in out.splitlines():
                 iscurrent, line = line[0:3], line[3:]
-                if '*' in iscurrent:
+                if b('*') in iscurrent:
                     current = len(bms)
-                name, line = line.split(' ', 1)
-                rev, node = line.split(':')
+                name, line = line.split(b(' '), 1)
+                rev, node = line.split(b(':'))
                 bms.append((name, int(rev), node))
         return bms, current
 
@@ -427,7 +427,7 @@
         if name and clean:
             raise ValueError('cannot use both name and clean')
 
-        args = cmdbuilder('branch', name, f=force, C=clean)
+        args = cmdbuilder(b('branch'), name, f=force, C=clean)
         out = self.rawcommand(args).rstrip()
 
         if name:
@@ -445,13 +445,13 @@
         active - show only branches that have unmerged heads
         closed - show normal and closed branches
         """
-        args = cmdbuilder('branches', a=active, c=closed, hidden=self.hidden)
+        args = cmdbuilder(b('branches'), a=active, c=closed, hidden=self.hidden)
         out = self.rawcommand(args)
 
         branches = []
         for line in out.rstrip().splitlines():
-            namerev, node = line.rsplit(':', 1)
-            name, rev = namerev.rsplit(' ', 1)
+            namerev, node = line.rsplit(b(':'), 1)
+            name, rev = namerev.rsplit(b(' '), 1)
             name = name.rstrip()
             node = node.split()[0] # get rid of ' (inactive)'
             branches.append((name, int(rev), node))
@@ -485,7 +485,7 @@
         Return True if a bundle was created, False if no changes were found.
 
         """
-        args = cmdbuilder('bundle', file, destrepo, f=force, r=rev, b=branch,
+        args = cmdbuilder(b('bundle'), file, destrepo, f=force, r=rev, b=branch,
                           base=base, a=all, t=type, e=ssh, remotecmd=remotecmd,
                           insecure=insecure, hidden=self.hidden)
 
@@ -509,13 +509,13 @@
         "%p"  root-relative path name of file being printed
 
         """
-        args = cmdbuilder('cat', r=rev, o=output, hidden=self.hidden, *files)
+        args = cmdbuilder(b('cat'), r=rev, o=output, hidden=self.hidden, *files)
         out = self.rawcommand(args)
 
         if not output:
             return out
 
-    def clone(self, source='.', dest=None, branch=None, updaterev=None,
+    def clone(self, source=b('.'), dest=None, branch=None, updaterev=None,
               revrange=None):
         """
         Create a copy of an existing repository specified by source in a new
@@ -527,7 +527,7 @@
         updaterev - revision, tag or branch to check out
         revrange - include the specified changeset
         """
-        args = cmdbuilder('clone', source, dest, b=branch,
+        args = cmdbuilder(b('clone'), source, dest, b=branch,
                           u=updaterev, r=revrange)
         self.rawcommand(args)
 
@@ -549,18 +549,18 @@
         """
         if amend and message is None and logfile is None:
             # retrieve current commit message
-            message = self.log('.')[0][5]
+            message = self.log(b('.'))[0][5]
         if message is None and logfile is None and not amend:
             raise ValueError("must provide at least a message or a logfile")
         elif message and logfile:
             raise ValueError("cannot specify both a message and a logfile")
 
         # --debug will print the committed cset
-        args = cmdbuilder('commit', debug=True, m=message, A=addremove,
+        args = cmdbuilder(b('commit'), debug=True, m=message, A=addremove,
                           close_branch=closebranch, d=date, u=user, l=logfile,
                           I=include, X=exclude, amend=amend)
         out = self.rawcommand(args)
-        rev, node = out.splitlines()[-1].rsplit(':')
+        rev, node = out.splitlines()[-1].rsplit(b(':'))
         return int(rev.split()[-1]), node
 
     def config(self, names=[], untrusted=False, showsource=False):
@@ -572,21 +572,22 @@
 
         """
         def splitline(s):
-            k, value = s.rstrip().split('=', 1)
-            section, key = k.split('.', 1)
-            return (section, key, value)
+            k, value = s.rstrip().split(b('='), 1)
+            section, key = k.split(b('.'), 1)
+            return section, key, value
 
         if not isinstance(names, list):
             names = [names]
 
-        args = cmdbuilder('showconfig', u=untrusted, debug=showsource, *names)
+        args = cmdbuilder(b('showconfig'), u=untrusted, debug=showsource,
+                          *names)
         out = self.rawcommand(args)
 
         conf = []
         if showsource:
-            out = util.skiplines(out, 'read config from: ')
+            out = util.skiplines(out, b('read config from: '))
             for line in out.splitlines():
-                m = re.match(r"(.+?:(?:\d+:)?) (.*)", line)
+                m = re.match(b(r"(.+?:(?:\d+:)?) (.*)"), line)
                 t = splitline(m.group(2))
                 conf.append((m.group(1)[:-1], t[0], t[1], t[2]))
         else:
@@ -600,11 +601,11 @@
         """
         Return the server's encoding (as reported in the hello message).
         """
-        if not 'getencoding' in self.capabilities:
+        if not b('getencoding') in self.capabilities:
             raise CapabilityError('getencoding')
 
         if not self._encoding:
-            self.server.stdin.write('getencoding\n')
+            self.server.stdin.write(b('getencoding\n'))
             self._encoding = self._readfromchannel('r')
 
         return self._encoding
@@ -630,7 +631,7 @@
             source = [source]
 
         source.append(dest)
-        args = cmdbuilder('copy', A=after, f=force, n=dryrun,
+        args = cmdbuilder(b('copy'), A=after, f=force, n=dryrun,
                           I=include, X=exclude, *source)
 
         eh = util.reterrorhandler(args)
@@ -666,7 +667,7 @@
         if change and revs:
             raise ValueError('cannot specify both change and rev')
 
-        args = cmdbuilder('diff', r=revs, c=change,
+        args = cmdbuilder(b('diff'), r=revs, c=change,
                           a=text, g=git, nodates=nodates,
                           p=showfunction, reverse=reverse,
                           w=ignoreallspace, b=ignorespacechange,
@@ -701,7 +702,7 @@
         """
         if not isinstance(revs, list):
             revs = [revs]
-        args = cmdbuilder('export', o=output, switch_parent=switchparent,
+        args = cmdbuilder(b('export'), o=output, switch_parent=switchparent,
                           a=text, g=git, nodates=nodates, hidden=self.hidden,
                           *revs)
 
@@ -726,7 +727,7 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('forget', I=include, X=exclude, *files)
+        args = cmdbuilder(b('forget'), I=include, X=exclude, *files)
 
         eh = util.reterrorhandler(args)
         self.rawcommand(args, eh=eh)
@@ -761,18 +762,18 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('grep', all=all, a=text, f=follow, i=ignorecase,
+        args = cmdbuilder(b('grep'), all=all, a=text, f=follow, i=ignorecase,
                           l=fileswithmatches, n=line, u=user, d=date,
                           I=include, X=exclude, hidden=self.hidden,
                           *[pattern] + files)
-        args.append('-0')
+        args.append(b('-0'))
 
         def eh(ret, out, err):
             if ret != 1:
                 raise error.CommandError(args, ret, out, err)
-            return ''
+            return b('')
 
-        out = self.rawcommand(args, eh=eh).split('\0')
+        out = self.rawcommand(args, eh=eh).split(b('\0'))
 
         fieldcount = 3
         if user:
@@ -804,16 +805,16 @@
         if not isinstance(rev, list):
             rev = [rev]
 
-        args = cmdbuilder('heads', r=startrev, t=topological, c=closed,
+        args = cmdbuilder(b('heads'), r=startrev, t=topological, c=closed,
                           template=templates.changeset, hidden=self.hidden,
                           *rev)
 
         def eh(ret, out, err):
             if ret != 1:
                 raise error.CommandError(args, ret, out, err)
-            return ''
+            return b('')
 
-        out = self.rawcommand(args, eh=eh).split('\0')[:-1]
+        out = self.rawcommand(args, eh=eh).split(b('\0'))[:-1]
         return self._parserevs(out)
 
     def identify(self, rev=None, source=None, num=False, id=False, branch=False,
@@ -836,7 +837,7 @@
         bookmarks - show bookmarks
 
         """
-        args = cmdbuilder('identify', source, r=rev, n=num, i=id,
+        args = cmdbuilder(b('identify'), source, r=rev, n=num, i=id,
                           b=branch, t=tags, B=bookmarks,
                           hidden=self.hidden)
 
@@ -878,7 +879,7 @@
             prompt = None
             input = None
 
-        args = cmdbuilder('import', strip=strip, force=force,
+        args = cmdbuilder(b('import'), strip=strip, force=force,
                           no_commit=nocommit, bypass=bypass, exact=exact,
                           import_branch=importbranch, message=message,
                           date=date, user=user, similarity=similarity, _=stdin,
@@ -911,7 +912,7 @@
         subrepos - recurse into subrepositories
 
         """
-        args = cmdbuilder('incoming', path,
+        args = cmdbuilder(b('incoming'), path,
                           template=templates.changeset, r=revrange,
                           f=force, n=newest, bundle=bundle,
                           B=bookmarks, b=branch, l=limit, M=nomerges,
@@ -932,7 +933,7 @@
                 bms.append(tuple(line.split()))
             return bms
         else:
-            out = out.split('\0')[:-1]
+            out = out.split(b('\0'))[:-1]
             return self._parserevs(out)
 
     def log(self, revrange=None, files=[], follow=False,
@@ -983,7 +984,7 @@
         """
         if hidden is None:
             hidden = self.hidden
-        args = cmdbuilder('log', template=templates.changeset,
+        args = cmdbuilder(b('log'), template=templates.changeset,
                           r=revrange, f=follow, follow_first=followfirst,
                           d=date, C=copies, k=keyword, removed=removed,
                           m=onlymerges, u=user, b=branch, P=prune,
@@ -991,7 +992,7 @@
                           hidden=hidden, *files)
 
         out = self.rawcommand(args)
-        out = out.split('\0')[:-1]
+        out = out.split(b('\0'))[:-1]
 
         return self._parserevs(out)
 
@@ -1005,7 +1006,7 @@
         (just the name). This includes deleted and renamed files.
 
         """
-        args = cmdbuilder('manifest', r=rev, all=all, debug=True,
+        args = cmdbuilder(b('manifest'), r=rev, all=all, debug=True,
                           hidden=self.hidden)
 
         out = self.rawcommand(args)
@@ -1017,9 +1018,9 @@
             for line in out.splitlines():
                 node = line[0:40]
                 perm = line[41:44]
-                symlink = line[45] == '@'
-                executable = line[45] == '*'
-                yield (node, perm, executable, symlink, line[47:])
+                symlink = line[45:46] == b('@')
+                executable = line[45:46] == b('*')
+                yield node, perm, executable, symlink, line[47:]
 
     def merge(self, rev=None, force=False, tool=None, cb=merge.handlers.abort):
         """Merge working directory with rev. If no revision is specified, the
@@ -1047,15 +1048,15 @@
 
         """
         # we can't really use --preview since merge doesn't support --template
-        args = cmdbuilder('merge', r=rev, f=force, t=tool)
+        args = cmdbuilder(b('merge'), r=rev, f=force, t=tool)
 
         prompt = None
         if cb is merge.handlers.abort:
             prompt = cb
         elif cb is merge.handlers.noninteractive:
-            args.append('-y')
+            args.append(b('-y'))
         else:
-            prompt = lambda size, output: cb(output) + '\n'
+            prompt = lambda size, output: cb(output) + b('\n')
 
         self.rawcommand(args, prompt=prompt)
 
@@ -1080,7 +1081,7 @@
             source = [source]
 
         source.append(dest)
-        args = cmdbuilder('move', A=after, f=force, n=dryrun,
+        args = cmdbuilder(b('move'), A=after, f=force, n=dryrun,
                           I=include, X=exclude, *source)
 
         eh = util.reterrorhandler(args)
@@ -1108,7 +1109,7 @@
         subrepositories
 
         """
-        args = cmdbuilder('outgoing',
+        args = cmdbuilder(b('outgoing'),
                           path,
                           template=templates.changeset, r=revrange,
                           f=force, n=newest, B=bookmarks,
@@ -1129,7 +1130,7 @@
                 bms.append(tuple(line.split()))
             return bms
         else:
-            out = out.split('\0')[:-1]
+            out = out.split(b('\0'))[:-1]
             return self._parserevs(out)
 
     def parents(self, rev=None, file=None):
@@ -1140,14 +1141,14 @@
         is returned.
 
         """
-        args = cmdbuilder('parents', file, template=templates.changeset, r=rev,
-                          hidden=self.hidden)
+        args = cmdbuilder(b('parents'), file, template=templates.changeset,
+                          r=rev, hidden=self.hidden)
 
         out = self.rawcommand(args)
         if not out:
             return
 
-        out = out.split('\0')[:-1]
+        out = out.split(b('\0'))[:-1]
 
         return self._parserevs(out)
 
@@ -1161,13 +1162,14 @@
         used, too.
         """
         if not name:
-            out = self.rawcommand(['paths'])
+            out = self.rawcommand([b('paths')])
             if not out:
                 return {}
 
-            return dict([s.split(' = ') for s in out.rstrip().split('\n')])
+            return dict([s.split(b(' = '))
+                         for s in out.rstrip().split(b('\n'))])
         else:
-            args = cmdbuilder('paths', name)
+            args = cmdbuilder(b('paths'), name)
             out = self.rawcommand(args)
             return out.rstrip()
 
@@ -1196,7 +1198,7 @@
         tool - specify merge tool for rebase
 
         """
-        args = cmdbuilder('pull', source, r=rev, u=update, f=force,
+        args = cmdbuilder(b('pull'), source, r=rev, u=update, f=force,
                           B=bookmark, b=branch, e=ssh,
                           remotecmd=remotecmd, insecure=insecure,
                           t=tool)
@@ -1233,7 +1235,7 @@
          web.cacerts config)
 
         """
-        args = cmdbuilder('push', dest, r=rev, f=force, B=bookmark, b=branch,
+        args = cmdbuilder(b('push'), dest, r=rev, f=force, B=bookmark, b=branch,
                           new_branch=newbranch, e=ssh, remotecmd=remotecmd,
                           insecure=insecure)
 
@@ -1259,7 +1261,7 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('remove', A=after, f=force, I=include, X=exclude,
+        args = cmdbuilder(b('remove'), A=after, f=force, I=include, X=exclude,
                           *files)
 
         eh = util.reterrorhandler(args)
@@ -1287,7 +1289,7 @@
         if not isinstance(file, list):
             file = [file]
 
-        args = cmdbuilder('resolve', a=all, l=listfiles, m=mark, u=unmark,
+        args = cmdbuilder(b('resolve'), a=all, l=listfiles, m=mark, u=unmark,
                           t=tool, I=include, X=exclude, *file)
 
         out = self.rawcommand(args)
@@ -1295,7 +1297,7 @@
         if listfiles:
             l = []
             for line in out.splitlines():
-                l.append(tuple(line.split(' ', 1)))
+                l.append(tuple(line.split(b(' '), 1)))
             return l
 
     def revert(self, files, rev=None, all=False, date=None, nobackup=False,
@@ -1330,7 +1332,7 @@
         if not isinstance(files, list):
             files = [files]
 
-        args = cmdbuilder('revert', r=rev, a=all, d=date,
+        args = cmdbuilder(b('revert'), r=rev, a=all, d=date,
                           no_backup=nobackup, n=dryrun, I=include, X=exclude,
                           hidden=self.hidden, *files)
 
@@ -1343,7 +1345,7 @@
         """
         Return the root directory of the current repository.
         """
-        return self.rawcommand(['root']).rstrip()
+        return self.rawcommand([b('root')]).rstrip()
 
     def status(self, rev=None, change=None, all=False, modified=False,
                added=False, removed=False, deleted=False, clean=False,
@@ -1380,22 +1382,22 @@
         if rev and change:
             raise ValueError('cannot specify both rev and change')
 
-        args = cmdbuilder('status', rev=rev, change=change, A=all, m=modified,
-                          a=added, r=removed, d=deleted, c=clean, u=unknown,
-                          i=ignored, C=copies, S=subrepos, I=include,
+        args = cmdbuilder(b('status'), rev=rev, change=change, A=all,
+                          m=modified, a=added, r=removed, d=deleted, c=clean,
+                          u=unknown, i=ignored, C=copies, S=subrepos, I=include,
                           X=exclude, hidden=self.hidden)
 
-        args.append('-0')
+        args.append(b('-0'))
 
         out = self.rawcommand(args)
         l = []
 
-        for entry in out.split('\0'):
+        for entry in out.split(b('\0')):
             if entry:
-                if entry[0] == ' ':
-                    l.append((' ', entry[2:]))
+                if entry[0:1] == b(' '):
+                    l.append((b(' '), entry[2:]))
                 else:
-                    l.append(tuple(entry.split(' ', 1)))
+                    l.append(tuple(entry.split(b(' '), 1)))
 
         return l
 
@@ -1422,7 +1424,7 @@
         if not isinstance(names, list):
             names = [names]
 
-        args = cmdbuilder('tag', r=rev, m=message, f=force, l=local,
+        args = cmdbuilder(b('tag'), r=rev, m=message, f=force, l=local,
                           remove=remove, d=date, u=user, hidden=self.hidden,
                           *names)
 
@@ -1432,17 +1434,17 @@
         """
         Return a list of repository tags as: (name, rev, node, islocal)
         """
-        args = cmdbuilder('tags', v=True)
+        args = cmdbuilder(b('tags'), v=True)
 
         out = self.rawcommand(args)
 
         t = []
         for line in out.splitlines():
-            taglocal = line.endswith(' local')
+            taglocal = line.endswith(b(' local'))
             if taglocal:
                 line = line[:-6]
-            name, rev = line.rsplit(' ', 1)
-            rev, node = rev.split(':')
+            name, rev = line.rsplit(b(' '), 1)
+            rev, node = rev.split(b(':'))
             t.append((name.rstrip(), int(rev), node, taglocal))
         return t
 
@@ -1462,14 +1464,14 @@
         '''
         if not isinstance(revs, (list, tuple)):
             revs = [revs]
-        args = util.cmdbuilder('phase', secret=secret, draft=draft,
+        args = util.cmdbuilder(b('phase'), secret=secret, draft=draft,
                                public=public, force=force,
                                hidden=self.hidden, *revs)
         out = self.rawcommand(args)
         if draft or public or secret:
             return
         else:
-            output = [i.split(': ')for i in out.strip().split('\n')]
+            output = [i.split(b(': '))for i in out.strip().split(b('\n'))]
             return [(int(num), phase) for (num, phase) in output]
 
     def summary(self, remote=False):
@@ -1486,65 +1488,65 @@
 
             unparsed entries will be of them form key : value
         """
-        args = cmdbuilder('summary', remote=remote, hidden=self.hidden)
+        args = cmdbuilder(b('summary'), remote=remote, hidden=self.hidden)
 
         out = self.rawcommand(args).splitlines()
 
         d = {}
         while out:
             line = out.pop(0)
-            name, value = line.split(': ', 1)
+            name, value = line.split(b(': '), 1)
 
-            if name == 'parent':
-                parent, tags = value.split(' ', 1)
-                rev, node = parent.split(':')
+            if name == b('parent'):
+                parent, tags = value.split(b(' '), 1)
+                rev, node = parent.split(b(':'))
 
                 if tags:
-                    tags = tags.replace(' (empty repository)', '')
+                    tags = tags.replace(b(' (empty repository)'), b(''))
                 else:
                     tags = None
 
                 value = d.get(name, [])
 
-                if rev == '-1':
+                if rev == b('-1'):
                     value.append((int(rev), node, tags, None))
                 else:
                     message = out.pop(0)[1:]
                     value.append((int(rev), node, tags, message))
-            elif name == 'branch':
+            elif name == b('branch'):
                 pass
-            elif name == 'commit':
-                value = value == '(clean)'
-            elif name == 'update':
-                if value == '(current)':
+            elif name == b('commit'):
+                value = value == b('(clean)')
+            elif name == b('update'):
+                if value == b('(current)'):
                     value = 0
                 else:
-                    value = int(value.split(' ', 1)[0])
-            elif remote and name == 'remote':
-                if value == '(synced)':
+                    value = int(value.split(b(' '), 1)[0])
+            elif remote and name == b('remote'):
+                if value == b('(synced)'):
                     value = 0, 0, 0, 0
                 else:
                     inc = incb = out_ = outb = 0
 
-                    for v in value.split(', '):
-                        count, v = v.split(' ', 1)
-                        if v == 'outgoing':
+                    for v in value.split(b(', ')):
+                        count, v = v.split(b(' '), 1)
+                        if v == b('outgoing'):
                             out_ = int(count)
-                        elif v.endswith('incoming'):
+                        elif v.endswith(b('incoming')):
                             inc = int(count)
-                        elif v == 'incoming bookmarks':
+                        elif v == b('incoming bookmarks'):
                             incb = int(count)
-                        elif v == 'outgoing bookmarks':
+                        elif v == b('outgoing bookmarks'):
                             outb = int(count)
 
                     value = inc, incb, out_, outb
-            elif name == 'mq':
+            elif name == b('mq'):
                 applied = unapplied = 0
-                for v in value.split(', '):
-                    count, v = v.split(' ', 1)
-                    if v == 'applied':
+                for v in value.split(b(', ')):
+                    count, v = v.split(b(' '), 1)
+                    if v == b('applied'):
                         applied = int(count)
-                    elif v == 'unapplied':
+                    elif v == b('unapplied'):
                         unapplied = int(count)
                 value = applied, unapplied
 
@@ -1558,10 +1560,10 @@
         changeset most recently added to the repository (and therefore the most
         recently changed head).
         """
-        args = cmdbuilder('tip', template=templates.changeset,
+        args = cmdbuilder(b('tip'), template=templates.changeset,
                           hidden=self.hidden)
         out = self.rawcommand(args)
-        out = out.split('\0')
+        out = out.split(b('\0'))
 
         return self._parserevs(out)[0]
 
@@ -1579,7 +1581,7 @@
         if clean and check:
             raise ValueError('clean and check cannot both be True')
 
-        args = cmdbuilder('update', r=rev, C=clean, c=check, d=date,
+        args = cmdbuilder(b('update'), r=rev, C=clean, c=check, d=date,
                           hidden=self.hidden)
 
         def eh(ret, out, err):
@@ -1591,7 +1593,8 @@
 
         out = self.rawcommand(args, eh=eh)
 
-        m = re.search(r'^(\d+).+, (\d+).+, (\d+).+, (\d+)', out, re.MULTILINE)
+        m = re.search(b(r'^(\d+).+, (\d+).+, (\d+).+, (\d+)'), out,
+                      re.MULTILINE)
         return tuple(map(int, list(m.groups())))
 
     @property
@@ -1601,8 +1604,8 @@
         1, '+4-3095db9f5c2c')
         """
         if self._version is None:
-            v = self.rawcommand(cmdbuilder('version', q=True))
-            v = list(re.match(r'.*?(\d+)\.(\d+)\.?(\d+)?(\+[0-9a-f-]+)?',
+            v = self.rawcommand(cmdbuilder(b('version'), q=True))
+            v = list(re.match(b(r'.*?(\d+)\.(\d+)\.?(\d+)?(\+[0-9a-f-]+)?'),
                               v).groups())
 
             for i in range(3):
diff -r ea80bd2775f6 -r 06babaeea98a hglib/context.py
--- a/hglib/context.py	Sat Mar 07 10:08:52 2015 -0500
+++ b/hglib/context.py	Sun Mar 08 13:08:37 2015 -0400
@@ -1,16 +1,17 @@
 from hglib.error import CommandError
 import client, util, templates
+from hglib.util import b
 
-_nullcset = ['-1', '0000000000000000000000000000000000000000', '', '',
-             '', '', '']
+_nullcset = [b('-1'), b('0000000000000000000000000000000000000000'), b(''),
+             b(''), b(''), b(''), b('')]
 
 class changectx(object):
     """A changecontext object makes access to data related to a particular
     changeset convenient."""
-    def __init__(self, repo, changeid=''):
+    def __init__(self, repo, changeid=b('')):
         """changeid is a revision number, node, or tag"""
-        if changeid == '':
-            changeid = '.'
+        if changeid == b(''):
+            changeid = b('.')
         self._repo = repo
         if isinstance(changeid, client.revision):
             cset = changeid
@@ -18,7 +19,7 @@
             cset = _nullcset
         else:
             if isinstance(changeid, (long, int)):
-                changeid = 'rev(%d)' % changeid
+                changeid = b('rev(') + str(changeid).encode('latin-1') + b(')')
 
             notfound = False
             try:
@@ -43,7 +44,7 @@
 
         self._tags = self._tags.split()
         try:
-            self._tags.remove('tip')
+            self._tags.remove(b('tip'))
         except ValueError:
             pass
 
@@ -89,10 +90,13 @@
         return self._parsestatus(self._repo.status(change=self))[:4]
 
     def _parsestatus(self, stat):
-        d = dict((c, []) for c in 'MAR!?IC ')
+        d = dict((c, [])
+                 for c in (b('M'), b('A'), b('R'), b('!'), b('?'), b('I'),
+                    b('C'), b(' ')))
         for k, path in stat:
             d[k].append(path)
-        return d['M'], d['A'], d['R'], d['!'], d['?'], d['I'], d['C']
+        return (d[b('M')], d[b('A')], d[b('R')], d[b('!')], d[b('?')],
+                d[b('I')], d[b('C')])
 
     def status(self, ignored=False, clean=False):
         """Explicit status query
@@ -201,7 +205,7 @@
 
     def hidden(self):
         """return True if the changeset is hidden, else False"""
-        return bool(self._repo.log(revrange='%s and hidden()' % self._node,
+        return bool(self._repo.log(revrange=self._node + b(' and hidden()'),
                                    hidden=True))
 
     def phase(self):
@@ -210,19 +214,20 @@
 
     def children(self):
         """return contexts for each child changeset"""
-        for c in self._repo.log('children(%s)' % self._node):
+        for c in self._repo.log(b('children(') + self._node + b(')')):
             yield changectx(self._repo, c)
 
     def ancestors(self):
-        for a in self._repo.log('ancestors(%s)' % self._node):
+        for a in self._repo.log(b('ancestors(') + self._node + b(')')):
             yield changectx(self._repo, a)
 
     def descendants(self):
-        for d in self._repo.log('descendants(%s)' % self._node):
+        for d in self._repo.log(b('descendants(') + self._node + b(')')):
             yield changectx(self._repo, d)
 
     def ancestor(self, c2):
         """
         return the ancestor context of self and c2
         """
-        return changectx(self._repo, 'ancestor(%s, %s)' % (self, c2))
+        return changectx(self._repo,
+                         b('ancestor(') + self + b(', ') + c2 + b(')'))
diff -r ea80bd2775f6 -r 06babaeea98a hglib/merge.py
--- a/hglib/merge.py	Sat Mar 07 10:08:52 2015 -0500
+++ b/hglib/merge.py	Sun Mar 08 13:08:37 2015 -0400
@@ -1,3 +1,5 @@
+from hglib.util import b
+
 class handlers(object):
     """
     These can be used as the cb argument to hgclient.merge() to control the
@@ -10,7 +12,7 @@
         """
         Abort the merge if a prompt appears.
         """
-        return ''
+        return b('')
 
     """
     This corresponds to Mercurial's -y/--noninteractive global option, which
diff -r ea80bd2775f6 -r 06babaeea98a hglib/templates.py
--- a/hglib/templates.py	Sat Mar 07 10:08:52 2015 -0500
+++ b/hglib/templates.py	Sun Mar 08 13:08:37 2015 -0400
@@ -1,1 +1,3 @@
-changeset = '{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\\0{date}\\0'
+from hglib.util import b
+
+changeset = b('{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\\0{date}\\0')
diff -r ea80bd2775f6 -r 06babaeea98a hglib/util.py
--- a/hglib/util.py	Sat Mar 07 10:08:52 2015 -0500
+++ b/hglib/util.py	Sun Mar 08 13:08:37 2015 -0400
@@ -31,7 +31,7 @@
         n -= 1
         if n == 0:
             return cs.read()
-    return ''
+    return b('')
 
 def skiplines(s, prefix):
     """
@@ -52,7 +52,7 @@
         if not line.startswith(prefix):
             return line + cs.read()
 
-    return ''
+    return b('')
 
 def cmdbuilder(name, *args, **kwargs):
     """
@@ -88,12 +88,12 @@
         if val is None:
             continue
 
-        arg = arg.replace('_', '-')
-        if arg != '-':
+        arg = arg.replace(b('_'), b('-'))
+        if arg != b('-'):
             if len(arg) == 1:
-                arg = '-' + arg
+                arg = b('-') + arg
             else:
-                arg = '--' + arg
+                arg = b('--') + arg
         if isinstance(val, bool):
             if val:
                 cmd.append(arg)


More information about the Mercurial-devel mailing list