Patchwork [3,of,3] drawdag: port to python 3

login
register
mail settings
Submitter Jun Wu
Date Aug. 27, 2017, 3:47 a.m.
Message ID <1503805614-sup-7030@x1c>
Download mbox | patch
Permalink /patch/23370/
State Not Applicable
Headers show

Comments

Jun Wu - Aug. 27, 2017, 3:47 a.m.
Changes needed to make it work:


Doctest needs update accordingly:

@@ -152,9 +152,9 @@ def _parseasciigraph(text):
     {'a': [],
      'b': ['a'],
-     'bar': ['baz'],
+     'bar': ['b', 'a'],
      'baz': [],
      'c': ['b'],
      'd': ['b'],
-     'foo': ['b']}
+     'foo': ['baz', 'b']}
     """
     lines = text.splitlines()

Excerpts from Augie Fackler's message of 2017-08-23 11:00:21 -0400:
> 
> > On Aug 23, 2017, at 10:57 AM, Augie Fackler <raf@durin42.com> wrote:
> > 
> > # HG changeset patch
> > # User Augie Fackler <raf@durin42.com>
> > # Date 1503465796 14400
> > #      Wed Aug 23 01:23:16 2017 -0400
> > # Node ID 4be5cef7bafa1d216668b2bbdd3d3b8074c30d26
> > # Parent  de96681ea4cc716967307ccfdb7c4a1684c14f50
> > drawdag: port to python 3
> 
> This is *almost* correct, but I can’t figure out what I’ve got wrong. The included doctests all pass on Python 2 and fail on Python 3, so you can see the failing example with
> 
> python3 -m doctest tests/drawdag.py
> 
> Jun, if you could take a glance at this it’d help a lot - I think it’s something simple that I’ve overlooked.
> 
> Thanks!
> 
> > 
> > diff --git a/tests/drawdag.py b/tests/drawdag.py
> > --- a/tests/drawdag.py
> > +++ b/tests/drawdag.py
> > @@ -92,6 +92,7 @@ from mercurial import (
> >     error,
> >     node,
> >     obsolete,
> > +    pycompat,
> >     registrar,
> >     scmutil,
> >     tags as tagsmod,
> > @@ -100,9 +101,9 @@ from mercurial import (
> > cmdtable = {}
> > command = registrar.command(cmdtable)
> > 
> > -_pipechars = '\\/+-|'
> > -_nonpipechars = ''.join(chr(i) for i in xrange(33, 127)
> > -                        if chr(i) not in _pipechars)
> > +_pipechars = b'\\/+-|'
> > +_nonpipechars = b''.join(pycompat.bytechr(i) for i in range(33, 127)
> > +                        if pycompat.bytechr(i) not in _pipechars)
> > 
> > def _isname(ch):
> >     """char -> bool. return True if ch looks like part of a name, False
> > @@ -163,16 +164,16 @@ def _parseasciigraph(text):
> >         """(int, int) -> char. give a coordinate, return the char. return a
> >         space for anything out of range"""
> >         if x < 0 or y < 0:
> > -            return ' '
> > +            return b' '
> >         try:
> > -            return lines[y][x]
> > +            return lines[y][x:x + 1] or b' '
> >         except IndexError:
> > -            return ' '
> > +            return b' '
> > 
> >     def getname(y, x):
> >         """(int, int) -> str. like get(y, x) but concatenate left and right
> >         parts. if name is an 'o', try to replace it to the right"""
> > -        result = ''
> > +        result = b''
> >         for i in itertools.count(0):
> >             ch = get(y, x - i)
> >             if not _isname(ch):
> > @@ -183,17 +184,17 @@ def _parseasciigraph(text):
> >             if not _isname(ch):
> >                 break
> >             result += ch
> > -        if result == 'o':
> > +        if result == b'o':
> >             # special handling, find the name to the right
> > -            result = ''
> > +            result = b''
> >             for i in itertools.count(2):
> >                 ch = get(y, x + i)
> > -                if ch == ' ' or ch in _pipechars:
> > +                if ch == b' ' or ch in _pipechars:
> >                     if result or x + i >= len(lines[y]):
> >                         break
> >                 else:
> >                     result += ch
> > -            return result or 'o'
> > +            return result or b'o'
> >         return result
> > 
> >     def parents(y, x):
> > @@ -209,19 +210,19 @@ def _parseasciigraph(text):
> >             if '-' (or '+') is not in excepted, and get(y, x) is '-' (or '+'),
> >             the next line (y + 1, x) will be checked instead."""
> >             ch = get(y, x)
> > -            if any(ch == c and c not in expected for c in '-+'):
> > +            if any(ch == c and c not in expected for c in b'-+'):
> >                 y += 1
> >                 return follow(y + 1, x, expected)
> > -            if ch in expected or ('o' in expected and _isname(ch)):
> > +            if ch in expected or (b'o' in expected and _isname(ch)):
> >                 visit.append((y, x))
> > 
> >         #  -o-  # starting point:
> >         #  /|\ # follow '-' (horizontally), and '/|\' (to the bottom)
> > -        follow(y + 1, x, '|')
> > -        follow(y + 1, x - 1, '/')
> > -        follow(y + 1, x + 1, '\\')
> > -        follow(y, x - 1, '-')
> > -        follow(y, x + 1, '-')
> > +        follow(y + 1, x, b'|')
> > +        follow(y + 1, x - 1, b'/')
> > +        follow(y + 1, x + 1, b'\\')
> > +        follow(y, x - 1, b'-')
> > +        follow(y, x + 1, b'-')
> > 
> >         while visit:
> >             y, x = visit.pop()
> > @@ -232,28 +233,28 @@ def _parseasciigraph(text):
> >             if _isname(ch):
> >                 result.append(getname(y, x))
> >                 continue
> > -            elif ch == '|':
> > -                follow(y + 1, x, '/|o')
> > -                follow(y + 1, x - 1, '/')
> > -                follow(y + 1, x + 1, '\\')
> > -            elif ch == '+':
> > -                follow(y, x - 1, '-')
> > -                follow(y, x + 1, '-')
> > -                follow(y + 1, x - 1, '/')
> > -                follow(y + 1, x + 1, '\\')
> > -                follow(y + 1, x, '|')
> > -            elif ch == '\\':
> > -                follow(y + 1, x + 1, '\\|o')
> > -            elif ch == '/':
> > -                follow(y + 1, x - 1, '/|o')
> > -            elif ch == '-':
> > -                follow(y, x - 1, '-+o')
> > -                follow(y, x + 1, '-+o')
> > +            elif ch == b'|':
> > +                follow(y + 1, x, b'/|o')
> > +                follow(y + 1, x - 1, b'/')
> > +                follow(y + 1, x + 1, b'\\')
> > +            elif ch == b'+':
> > +                follow(y, x - 1, b'-')
> > +                follow(y, x + 1, b'-')
> > +                follow(y + 1, x - 1, b'/')
> > +                follow(y + 1, x + 1, b'\\')
> > +                follow(y + 1, x, b'|')
> > +            elif ch == b'\\':
> > +                follow(y + 1, x + 1, b'\\|o')
> > +            elif ch == b'/':
> > +                follow(y + 1, x - 1, b'/|o')
> > +            elif ch == b'-':
> > +                follow(y, x - 1, b'-+o')
> > +                follow(y, x + 1, b'-+o')
> >         return result
> > 
> >     for y, line in enumerate(lines):
> > -        for x, ch in enumerate(line):
> > -            if ch == '#':  # comment
> > +        for x, ch in enumerate(pycompat.bytestr(line)):
> > +            if ch == b'#':  # comment
> >                 break
> >             if _isname(ch):
> >                 edges[getname(y, x)] += parents(y, x)
> > @@ -278,14 +279,14 @@ class simplefilectx(object):
> >         return None
> > 
> >     def flags(self):
> > -        return ''
> > +        return b''
> > 
> > class simplecommitctx(context.committablectx):
> >     def __init__(self, repo, name, parentctxs, added):
> >         opts = {
> >             'changes': scmutil.status([], list(added), [], [], [], [], []),
> > -            'date': '0 0',
> > -            'extra': {'branch': 'default'},
> > +            'date': b'0 0',
> > +            'extra': {b'branch': b'default'},
> >         }
> >         super(simplecommitctx, self).__init__(self, name, **opts)
> >         self._repo = repo
> > @@ -304,7 +305,7 @@ def _walkgraph(edges):
> >     """yield node, parents in topologically order"""
> >     visible = set(edges.keys())
> >     remaining = {}  # {str: [str]}
> > -    for k, vs in edges.iteritems():
> > +    for k, vs in edges.items():
> >         for v in vs:
> >             if v not in remaining:
> >                 remaining[v] = []
> > @@ -317,7 +318,7 @@ def _walkgraph(edges):
> >             if leaf in visible:
> >                 yield leaf, edges[leaf]
> >             del remaining[leaf]
> > -            for k, v in remaining.iteritems():
> > +            for k, v in remaining.items():
> >                 if leaf in v:
> >                     v.remove(leaf)
> > 
> > @@ -335,11 +336,11 @@ def _getcomments(text):
> >     ['split: B -> E, F, G', 'replace: C -> D -> H', 'prune: F, I']
> >     """
> >     for line in text.splitlines():
> > -        if ' # ' not in line:
> > +        if b' # ' not in line:
> >             continue
> > -        yield line.split(' # ', 1)[1].split(' # ')[0].strip()
> > +        yield line.split(b' # ', 1)[1].split(b' # ')[0].strip()
> > 
> > -@command('debugdrawdag', [])
> > +@command(b'debugdrawdag', [])
> > def debugdrawdag(ui, repo, **opts):
> >     """read an ASCII graph from stdin and create changesets
> > 
> > @@ -360,22 +361,22 @@ def debugdrawdag(ui, repo, **opts):
> > 
> >     # parse the graph and make sure len(parents) <= 2 for each node
> >     edges = _parseasciigraph(text)
> > -    for k, v in edges.iteritems():
> > +    for k, v in edges.items():
> >         if len(v) > 2:
> >             raise error.Abort(_('%s: too many parents: %s')
> > -                              % (k, ' '.join(v)))
> > +                              % (k, b' '.join(v)))
> > 
> >     # parse comments to get extra file content instructions
> >     files = collections.defaultdict(dict) # {(name, path): content}
> >     comments = list(_getcomments(text))
> > -    filere = re.compile(r'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M)
> > -    for name, path, content in filere.findall('\n'.join(comments)):
> > -        files[name][path] = content.replace(r'\n', '\n')
> > +    filere = re.compile(br'^(\w+)/([\w/]+)\s*=\s*(.*)$', re.M)
> > +    for name, path, content in filere.findall(b'\n'.join(comments)):
> > +        files[name][path] = content.replace(br'\n', b'\n')
> > 
> >     committed = {None: node.nullid}  # {name: node}
> > 
> >     # for leaf nodes, try to find existing nodes in repo
> > -    for name, parents in edges.iteritems():
> > +    for name, parents in edges.items():
> >         if len(parents) == 0:
> >             try:
> >                 committed[name] = scmutil.revsingle(repo, name)
> > @@ -407,27 +408,27 @@ def debugdrawdag(ui, repo, **opts):
> >                     local=True)
> > 
> >     # handle special comments
> > -    with repo.wlock(), repo.lock(), repo.transaction('drawdag'):
> > +    with repo.wlock(), repo.lock(), repo.transaction(b'drawdag'):
> >         getctx = lambda x: repo.unfiltered()[committed[x.strip()]]
> >         for comment in comments:
> >             rels = [] # obsolete relationships
> > -            args = comment.split(':', 1)
> > +            args = comment.split(b':', 1)
> >             if len(args) <= 1:
> >                 continue
> > 
> >             cmd = args[0].strip()
> >             arg = args[1].strip()
> > 
> > -            if cmd in ('replace', 'rebase', 'amend'):
> > -                nodes = [getctx(m) for m in arg.split('->')]
> > +            if cmd in (b'replace', b'rebase', b'amend'):
> > +                nodes = [getctx(m) for m in arg.split(b'->')]
> >                 for i in range(len(nodes) - 1):
> >                     rels.append((nodes[i], (nodes[i + 1],)))
> > -            elif cmd in ('split',):
> > -                pre, succs = arg.split('->')
> > -                succs = succs.split(',')
> > +            elif cmd in (b'split',):
> > +                pre, succs = arg.split(b'->')
> > +                succs = succs.split(b',')
> >                 rels.append((getctx(pre), (getctx(s) for s in succs)))
> > -            elif cmd in ('prune',):
> > -                for n in arg.split(','):
> > +            elif cmd in (b'prune',):
> > +                for n in arg.split(b','):
> >                     rels.append((getctx(n), ()))
> >             if rels:
> >                 obsolete.createmarkers(repo, rels, date=(0, 0), operation=cmd)
> > _______________________________________________
> > Mercurial-devel mailing list
> > Mercurial-devel@mercurial-scm.org
> > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

Patch

diff --git a/tests/drawdag.py b/tests/drawdag.py
--- a/tests/drawdag.py
+++ b/tests/drawdag.py
@@ -135,5 +135,5 @@  def _parseasciigraph(text):
      'I': ['H']}
     >>> pprint.pprint({pycompat.sysstr(k): [pycompat.sysstr(vv) for vv in v]
-    ...  for k, v in _parseasciigraph(b'''
+    ...  for k, v in _parseasciigraph(br'''
     ...  o    foo
     ...  |\
@@ -211,5 +211,5 @@  def _parseasciigraph(text):
             the next line (y + 1, x) will be checked instead."""
             ch = get(y, x)
-            if any(ch == c and c not in expected for c in b'-+'):
+            if any(ch == c and c not in expected for c in (b'-', b'+')):
                 y += 1
                 return follow(y + 1, x, expected)