mercurial/minirst.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 45942 89a2afe31e82
--- a/mercurial/minirst.py	Sun Oct 06 09:45:02 2019 -0400
+++ b/mercurial/minirst.py	Sun Oct 06 09:48:39 2019 -0400
@@ -32,23 +32,23 @@
 
 
 def section(s):
-    return "%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s))
+    return b"%s\n%s\n\n" % (s, b"\"" * encoding.colwidth(s))
 
 
 def subsection(s):
-    return "%s\n%s\n\n" % (s, '=' * encoding.colwidth(s))
+    return b"%s\n%s\n\n" % (s, b'=' * encoding.colwidth(s))
 
 
 def subsubsection(s):
-    return "%s\n%s\n\n" % (s, "-" * encoding.colwidth(s))
+    return b"%s\n%s\n\n" % (s, b"-" * encoding.colwidth(s))
 
 
 def subsubsubsection(s):
-    return "%s\n%s\n\n" % (s, "." * encoding.colwidth(s))
+    return b"%s\n%s\n\n" % (s, b"." * encoding.colwidth(s))
 
 
 def subsubsubsubsection(s):
-    return "%s\n%s\n\n" % (s, "'" * encoding.colwidth(s))
+    return b"%s\n%s\n\n" % (s, b"'" * encoding.colwidth(s))
 
 
 def replace(text, substs):
@@ -85,12 +85,12 @@
     has an 'indent' field and a 'lines' field.
     """
     blocks = []
-    for b in _blockre.split(text.lstrip('\n').rstrip()):
+    for b in _blockre.split(text.lstrip(b'\n').rstrip()):
         lines = b.splitlines()
         if lines:
             indent = min((len(l) - len(l.lstrip())) for l in lines)
             lines = [l[indent:] for l in lines]
-            blocks.append({'indent': indent, 'lines': lines})
+            blocks.append({b'indent': indent, b'lines': lines})
     return blocks
 
 
@@ -111,44 +111,44 @@
         #    +---------------------------+
         #    | indented literal block    |
         #    +---------------------------+
-        blocks[i]['type'] = 'paragraph'
-        if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
-            indent = blocks[i]['indent']
-            adjustment = blocks[i + 1]['indent'] - indent
+        blocks[i][b'type'] = b'paragraph'
+        if blocks[i][b'lines'][-1].endswith(b'::') and i + 1 < len(blocks):
+            indent = blocks[i][b'indent']
+            adjustment = blocks[i + 1][b'indent'] - indent
 
-            if blocks[i]['lines'] == ['::']:
+            if blocks[i][b'lines'] == [b'::']:
                 # Expanded form: remove block
                 del blocks[i]
                 i -= 1
-            elif blocks[i]['lines'][-1].endswith(' ::'):
+            elif blocks[i][b'lines'][-1].endswith(b' ::'):
                 # Partially minimized form: remove space and both
                 # colons.
-                blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
+                blocks[i][b'lines'][-1] = blocks[i][b'lines'][-1][:-3]
             elif (
-                len(blocks[i]['lines']) == 1
-                and blocks[i]['lines'][0].lstrip(' ').startswith('.. ')
-                and blocks[i]['lines'][0].find(' ', 3) == -1
+                len(blocks[i][b'lines']) == 1
+                and blocks[i][b'lines'][0].lstrip(b' ').startswith(b'.. ')
+                and blocks[i][b'lines'][0].find(b' ', 3) == -1
             ):
                 # directive on its own line, not a literal block
                 i += 1
                 continue
             else:
                 # Fully minimized form: remove just one colon.
-                blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
+                blocks[i][b'lines'][-1] = blocks[i][b'lines'][-1][:-1]
 
             # List items are formatted with a hanging indent. We must
             # correct for this here while we still have the original
             # information on the indentation of the subsequent literal
             # blocks available.
-            m = _bulletre.match(blocks[i]['lines'][0])
+            m = _bulletre.match(blocks[i][b'lines'][0])
             if m:
                 indent += m.end()
                 adjustment -= m.end()
 
             # Mark the following indented blocks.
-            while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
-                blocks[i + 1]['type'] = 'literal'
-                blocks[i + 1]['indent'] -= adjustment
+            while i + 1 < len(blocks) and blocks[i + 1][b'indent'] > indent:
+                blocks[i + 1][b'type'] = b'literal'
+                blocks[i + 1][b'indent'] -= adjustment
                 i += 1
         i += 1
     return blocks
@@ -169,10 +169,10 @@
     # matters: definition lists has the least specific regexp and must
     # come last.
     listtypes = [
-        ('bullet', _bulletre, True),
-        ('option', _optionre, True),
-        ('field', _fieldre, True),
-        ('definition', _definitionre, False),
+        (b'bullet', _bulletre, True),
+        (b'option', _optionre, True),
+        (b'field', _fieldre, True),
+        (b'definition', _definitionre, False),
     ]
 
     def match(lines, i, itemre, singleline):
@@ -182,18 +182,18 @@
         item (but only if singleline is True).
         """
         line1 = lines[i]
-        line2 = i + 1 < len(lines) and lines[i + 1] or ''
+        line2 = i + 1 < len(lines) and lines[i + 1] or b''
         if not itemre.match(line1):
             return False
         if singleline:
-            return line2 == '' or line2[0:1] == ' ' or itemre.match(line2)
+            return line2 == b'' or line2[0:1] == b' ' or itemre.match(line2)
         else:
-            return line2.startswith(' ')
+            return line2.startswith(b' ')
 
     i = 0
     while i < len(blocks):
-        if blocks[i]['type'] == 'paragraph':
-            lines = blocks[i]['lines']
+        if blocks[i][b'type'] == b'paragraph':
+            lines = blocks[i][b'lines']
             for type, itemre, singleline in listtypes:
                 if match(lines, 0, itemre, singleline):
                     items = []
@@ -201,12 +201,12 @@
                         if match(lines, j, itemre, singleline):
                             items.append(
                                 {
-                                    'type': type,
-                                    'lines': [],
-                                    'indent': blocks[i]['indent'],
+                                    b'type': type,
+                                    b'lines': [],
+                                    b'indent': blocks[i][b'indent'],
                                 }
                             )
-                        items[-1]['lines'].append(line)
+                        items[-1][b'lines'].append(line)
                     blocks[i : i + 1] = items
                     break
         i += 1
@@ -220,16 +220,16 @@
     """Find key for field lists."""
     i = 0
     while i < len(blocks):
-        if blocks[i]['type'] != 'field':
+        if blocks[i][b'type'] != b'field':
             i += 1
             continue
 
         j = i
-        while j < len(blocks) and blocks[j]['type'] == 'field':
-            m = _fieldre.match(blocks[j]['lines'][0])
+        while j < len(blocks) and blocks[j][b'type'] == b'field':
+            m = _fieldre.match(blocks[j][b'lines'][0])
             key, rest = m.groups()
-            blocks[j]['lines'][0] = rest
-            blocks[j]['key'] = key
+            blocks[j][b'lines'][0] = rest
+            blocks[j][b'key'] = key
             j += 1
 
         i = j + 1
@@ -240,37 +240,37 @@
 def updateoptionlists(blocks):
     i = 0
     while i < len(blocks):
-        if blocks[i]['type'] != 'option':
+        if blocks[i][b'type'] != b'option':
             i += 1
             continue
 
         optstrwidth = 0
         j = i
-        while j < len(blocks) and blocks[j]['type'] == 'option':
-            m = _optionre.match(blocks[j]['lines'][0])
+        while j < len(blocks) and blocks[j][b'type'] == b'option':
+            m = _optionre.match(blocks[j][b'lines'][0])
 
             shortoption = m.group(2)
             group3 = m.group(3)
             longoption = group3[2:].strip()
             desc = m.group(6).strip()
             longoptionarg = m.group(5).strip()
-            blocks[j]['lines'][0] = desc
+            blocks[j][b'lines'][0] = desc
 
-            noshortop = ''
+            noshortop = b''
             if not shortoption:
-                noshortop = '   '
+                noshortop = b'   '
 
-            opt = "%s%s" % (
-                shortoption and "-%s " % shortoption or '',
-                "%s--%s %s" % (noshortop, longoption, longoptionarg),
+            opt = b"%s%s" % (
+                shortoption and b"-%s " % shortoption or b'',
+                b"%s--%s %s" % (noshortop, longoption, longoptionarg),
             )
             opt = opt.rstrip()
-            blocks[j]['optstr'] = opt
+            blocks[j][b'optstr'] = opt
             optstrwidth = max(optstrwidth, encoding.colwidth(opt))
             j += 1
 
         for block in blocks[i:j]:
-            block['optstrwidth'] = optstrwidth
+            block[b'optstrwidth'] = optstrwidth
         i = j + 1
     return blocks
 
@@ -291,15 +291,15 @@
         # +---+                               |
         #     | blocks                        |
         #     +-------------------------------+
-        if blocks[i]['type'] == 'paragraph' and blocks[i]['lines'][
+        if blocks[i][b'type'] == b'paragraph' and blocks[i][b'lines'][
             0
-        ].startswith('.. container::'):
-            indent = blocks[i]['indent']
-            adjustment = blocks[i + 1]['indent'] - indent
-            containertype = blocks[i]['lines'][0][15:]
+        ].startswith(b'.. container::'):
+            indent = blocks[i][b'indent']
+            adjustment = blocks[i + 1][b'indent'] - indent
+            containertype = blocks[i][b'lines'][0][15:]
             prune = True
             for c in keep:
-                if c in containertype.split('.'):
+                if c in containertype.split(b'.'):
                     prune = False
             if prune:
                 pruned.append(containertype)
@@ -308,11 +308,11 @@
             del blocks[i]
             j = i
             i -= 1
-            while j < len(blocks) and blocks[j]['indent'] > indent:
+            while j < len(blocks) and blocks[j][b'indent'] > indent:
                 if prune:
                     del blocks[j]
                 else:
-                    blocks[j]['indent'] -= adjustment
+                    blocks[j][b'indent'] -= adjustment
                     j += 1
         i += 1
     return blocks, pruned
@@ -337,26 +337,26 @@
         #  x    y   z
         # === ==== ===
         if (
-            block['type'] == 'paragraph'
-            and len(block['lines']) > 2
-            and _tablere.match(block['lines'][0])
-            and block['lines'][0] == block['lines'][-1]
+            block[b'type'] == b'paragraph'
+            and len(block[b'lines']) > 2
+            and _tablere.match(block[b'lines'][0])
+            and block[b'lines'][0] == block[b'lines'][-1]
         ):
-            block['type'] = 'table'
-            block['header'] = False
-            div = block['lines'][0]
+            block[b'type'] = b'table'
+            block[b'header'] = False
+            div = block[b'lines'][0]
 
             # column markers are ASCII so we can calculate column
             # position in bytes
             columns = [
                 x
                 for x in pycompat.xrange(len(div))
-                if div[x : x + 1] == '=' and (x == 0 or div[x - 1 : x] == ' ')
+                if div[x : x + 1] == b'=' and (x == 0 or div[x - 1 : x] == b' ')
             ]
             rows = []
-            for l in block['lines'][1:-1]:
+            for l in block[b'lines'][1:-1]:
                 if l == div:
-                    block['header'] = True
+                    block[b'header'] = True
                     continue
                 row = []
                 # we measure columns not in bytes or characters but in
@@ -372,7 +372,7 @@
                         row.append(l[pos:].strip())
                 rows.append(row)
 
-            block['table'] = rows
+            block[b'table'] = rows
 
     return blocks
 
@@ -391,34 +391,34 @@
         # | -------------                |
         # +------------------------------+
         if (
-            block['type'] == 'paragraph'
-            and len(block['lines']) == 2
-            and encoding.colwidth(block['lines'][0]) == len(block['lines'][1])
-            and _sectionre.match(block['lines'][1])
+            block[b'type'] == b'paragraph'
+            and len(block[b'lines']) == 2
+            and encoding.colwidth(block[b'lines'][0]) == len(block[b'lines'][1])
+            and _sectionre.match(block[b'lines'][1])
         ):
-            block['underline'] = block['lines'][1][0:1]
-            block['type'] = 'section'
-            del block['lines'][1]
+            block[b'underline'] = block[b'lines'][1][0:1]
+            block[b'type'] = b'section'
+            del block[b'lines'][1]
     return blocks
 
 
 def inlineliterals(blocks):
-    substs = [('``', '"')]
+    substs = [(b'``', b'"')]
     for b in blocks:
-        if b['type'] in ('paragraph', 'section'):
-            b['lines'] = [replace(l, substs) for l in b['lines']]
+        if b[b'type'] in (b'paragraph', b'section'):
+            b[b'lines'] = [replace(l, substs) for l in b[b'lines']]
     return blocks
 
 
 def hgrole(blocks):
-    substs = [(':hg:`', "'hg "), ('`', "'")]
+    substs = [(b':hg:`', b"'hg "), (b'`', b"'")]
     for b in blocks:
-        if b['type'] in ('paragraph', 'section'):
+        if b[b'type'] in (b'paragraph', b'section'):
             # Turn :hg:`command` into "hg command". This also works
             # when there is a line break in the command and relies on
             # the fact that we have no stray back-quotes in the input
             # (run the blocks through inlineliterals first).
-            b['lines'] = [replace(l, substs) for l in b['lines']]
+            b[b'lines'] = [replace(l, substs) for l in b[b'lines']]
     return blocks
 
 
@@ -430,17 +430,17 @@
     """
     i = 1
     while i < len(blocks):
-        if blocks[i]['type'] == blocks[i - 1]['type'] and blocks[i]['type'] in (
-            'bullet',
-            'option',
-            'field',
-        ):
+        if blocks[i][b'type'] == blocks[i - 1][b'type'] and blocks[i][
+            b'type'
+        ] in (b'bullet', b'option', b'field',):
             i += 1
-        elif not blocks[i - 1]['lines']:
+        elif not blocks[i - 1][b'lines']:
             # no lines in previous block, do not separate
             i += 1
         else:
-            blocks.insert(i, {'lines': [''], 'indent': 0, 'type': 'margin'})
+            blocks.insert(
+                i, {b'lines': [b''], b'indent': 0, b'type': b'margin'}
+            )
             i += 2
     return blocks
 
@@ -450,11 +450,11 @@
     i = 0
     while i < len(blocks):
         b = blocks[i]
-        if b['type'] == 'paragraph' and (
-            b['lines'][0].startswith('.. ') or b['lines'] == ['..']
+        if b[b'type'] == b'paragraph' and (
+            b[b'lines'][0].startswith(b'.. ') or b[b'lines'] == [b'..']
         ):
             del blocks[i]
-            if i < len(blocks) and blocks[i]['type'] == 'margin':
+            if i < len(blocks) and blocks[i][b'type'] == b'margin':
                 del blocks[i]
         else:
             i += 1
@@ -469,47 +469,47 @@
     admonitions = admonitions or _admonitiontitles.keys()
 
     admonitionre = re.compile(
-        br'\.\. (%s)::' % '|'.join(sorted(admonitions)), flags=re.IGNORECASE
+        br'\.\. (%s)::' % b'|'.join(sorted(admonitions)), flags=re.IGNORECASE
     )
 
     i = 0
     while i < len(blocks):
-        m = admonitionre.match(blocks[i]['lines'][0])
+        m = admonitionre.match(blocks[i][b'lines'][0])
         if m:
-            blocks[i]['type'] = 'admonition'
-            admonitiontitle = blocks[i]['lines'][0][3 : m.end() - 2].lower()
+            blocks[i][b'type'] = b'admonition'
+            admonitiontitle = blocks[i][b'lines'][0][3 : m.end() - 2].lower()
 
-            firstline = blocks[i]['lines'][0][m.end() + 1 :]
+            firstline = blocks[i][b'lines'][0][m.end() + 1 :]
             if firstline:
-                blocks[i]['lines'].insert(1, '   ' + firstline)
+                blocks[i][b'lines'].insert(1, b'   ' + firstline)
 
-            blocks[i]['admonitiontitle'] = admonitiontitle
-            del blocks[i]['lines'][0]
+            blocks[i][b'admonitiontitle'] = admonitiontitle
+            del blocks[i][b'lines'][0]
         i = i + 1
     return blocks
 
 
 _admonitiontitles = {
-    'attention': _('Attention:'),
-    'caution': _('Caution:'),
-    'danger': _('!Danger!'),
-    'error': _('Error:'),
-    'hint': _('Hint:'),
-    'important': _('Important:'),
-    'note': _('Note:'),
-    'tip': _('Tip:'),
-    'warning': _('Warning!'),
+    b'attention': _(b'Attention:'),
+    b'caution': _(b'Caution:'),
+    b'danger': _(b'!Danger!'),
+    b'error': _(b'Error:'),
+    b'hint': _(b'Hint:'),
+    b'important': _(b'Important:'),
+    b'note': _(b'Note:'),
+    b'tip': _(b'Tip:'),
+    b'warning': _(b'Warning!'),
 }
 
 
 def formatoption(block, width):
-    desc = ' '.join(map(bytes.strip, block['lines']))
-    colwidth = encoding.colwidth(block['optstr'])
+    desc = b' '.join(map(bytes.strip, block[b'lines']))
+    colwidth = encoding.colwidth(block[b'optstr'])
     usablewidth = width - 1
-    hanging = block['optstrwidth']
-    initindent = '%s%s  ' % (block['optstr'], ' ' * ((hanging - colwidth)))
-    hangindent = ' ' * (encoding.colwidth(initindent) + 1)
-    return ' %s\n' % (
+    hanging = block[b'optstrwidth']
+    initindent = b'%s%s  ' % (block[b'optstr'], b' ' * ((hanging - colwidth)))
+    hangindent = b' ' * (encoding.colwidth(initindent) + 1)
+    return b' %s\n' % (
         stringutil.wrap(
             desc, usablewidth, initindent=initindent, hangindent=hangindent
         )
@@ -520,91 +520,91 @@
     """Format a block according to width."""
     if width <= 0:
         width = 78
-    indent = ' ' * block['indent']
-    if block['type'] == 'admonition':
-        admonition = _admonitiontitles[block['admonitiontitle']]
-        if not block['lines']:
-            return indent + admonition + '\n'
-        hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
+    indent = b' ' * block[b'indent']
+    if block[b'type'] == b'admonition':
+        admonition = _admonitiontitles[block[b'admonitiontitle']]
+        if not block[b'lines']:
+            return indent + admonition + b'\n'
+        hang = len(block[b'lines'][-1]) - len(block[b'lines'][-1].lstrip())
 
-        defindent = indent + hang * ' '
-        text = ' '.join(map(bytes.strip, block['lines']))
-        return '%s\n%s\n' % (
+        defindent = indent + hang * b' '
+        text = b' '.join(map(bytes.strip, block[b'lines']))
+        return b'%s\n%s\n' % (
             indent + admonition,
             stringutil.wrap(
                 text, width=width, initindent=defindent, hangindent=defindent
             ),
         )
-    if block['type'] == 'margin':
-        return '\n'
-    if block['type'] == 'literal':
-        indent += '  '
-        return indent + ('\n' + indent).join(block['lines']) + '\n'
-    if block['type'] == 'section':
-        underline = encoding.colwidth(block['lines'][0]) * block['underline']
-        return "%s%s\n%s%s\n" % (indent, block['lines'][0], indent, underline)
-    if block['type'] == 'table':
-        table = block['table']
+    if block[b'type'] == b'margin':
+        return b'\n'
+    if block[b'type'] == b'literal':
+        indent += b'  '
+        return indent + (b'\n' + indent).join(block[b'lines']) + b'\n'
+    if block[b'type'] == b'section':
+        underline = encoding.colwidth(block[b'lines'][0]) * block[b'underline']
+        return b"%s%s\n%s%s\n" % (indent, block[b'lines'][0], indent, underline)
+    if block[b'type'] == b'table':
+        table = block[b'table']
         # compute column widths
         widths = [max([encoding.colwidth(e) for e in c]) for c in zip(*table)]
-        text = ''
+        text = b''
         span = sum(widths) + len(widths) - 1
-        indent = ' ' * block['indent']
-        hang = ' ' * (len(indent) + span - widths[-1])
+        indent = b' ' * block[b'indent']
+        hang = b' ' * (len(indent) + span - widths[-1])
 
         for row in table:
             l = []
             for w, v in zip(widths, row):
-                pad = ' ' * (w - encoding.colwidth(v))
+                pad = b' ' * (w - encoding.colwidth(v))
                 l.append(v + pad)
-            l = ' '.join(l)
+            l = b' '.join(l)
             l = stringutil.wrap(
                 l, width=width, initindent=indent, hangindent=hang
             )
-            if not text and block['header']:
-                text = l + '\n' + indent + '-' * (min(width, span)) + '\n'
+            if not text and block[b'header']:
+                text = l + b'\n' + indent + b'-' * (min(width, span)) + b'\n'
             else:
-                text += l + "\n"
+                text += l + b"\n"
         return text
-    if block['type'] == 'definition':
-        term = indent + block['lines'][0]
-        hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
-        defindent = indent + hang * ' '
-        text = ' '.join(map(bytes.strip, block['lines'][1:]))
-        return '%s\n%s\n' % (
+    if block[b'type'] == b'definition':
+        term = indent + block[b'lines'][0]
+        hang = len(block[b'lines'][-1]) - len(block[b'lines'][-1].lstrip())
+        defindent = indent + hang * b' '
+        text = b' '.join(map(bytes.strip, block[b'lines'][1:]))
+        return b'%s\n%s\n' % (
             term,
             stringutil.wrap(
                 text, width=width, initindent=defindent, hangindent=defindent
             ),
         )
     subindent = indent
-    if block['type'] == 'bullet':
-        if block['lines'][0].startswith('| '):
+    if block[b'type'] == b'bullet':
+        if block[b'lines'][0].startswith(b'| '):
             # Remove bullet for line blocks and add no extra
             # indentation.
-            block['lines'][0] = block['lines'][0][2:]
+            block[b'lines'][0] = block[b'lines'][0][2:]
         else:
-            m = _bulletre.match(block['lines'][0])
-            subindent = indent + m.end() * ' '
-    elif block['type'] == 'field':
-        key = block['key']
-        subindent = indent + _fieldwidth * ' '
+            m = _bulletre.match(block[b'lines'][0])
+            subindent = indent + m.end() * b' '
+    elif block[b'type'] == b'field':
+        key = block[b'key']
+        subindent = indent + _fieldwidth * b' '
         if len(key) + 2 > _fieldwidth:
             # key too large, use full line width
             key = key.ljust(width)
         else:
             # key fits within field width
             key = key.ljust(_fieldwidth)
-        block['lines'][0] = key + block['lines'][0]
-    elif block['type'] == 'option':
+        block[b'lines'][0] = key + block[b'lines'][0]
+    elif block[b'type'] == b'option':
         return formatoption(block, width)
 
-    text = ' '.join(map(bytes.strip, block['lines']))
+    text = b' '.join(map(bytes.strip, block[b'lines']))
     return (
         stringutil.wrap(
             text, width=width, initindent=indent, hangindent=subindent
         )
-        + '\n'
+        + b'\n'
     )
 
 
@@ -612,7 +612,7 @@
     """Format RST blocks as HTML"""
 
     out = []
-    headernest = ''
+    headernest = b''
     listnest = []
 
     def escape(s):
@@ -621,91 +621,91 @@
     def openlist(start, level):
         if not listnest or listnest[-1][0] != start:
             listnest.append((start, level))
-            out.append('<%s>\n' % start)
+            out.append(b'<%s>\n' % start)
 
-    blocks = [b for b in blocks if b['type'] != 'margin']
+    blocks = [b for b in blocks if b[b'type'] != b'margin']
 
     for pos, b in enumerate(blocks):
-        btype = b['type']
-        level = b['indent']
-        lines = b['lines']
+        btype = b[b'type']
+        level = b[b'indent']
+        lines = b[b'lines']
 
-        if btype == 'admonition':
-            admonition = escape(_admonitiontitles[b['admonitiontitle']])
-            text = escape(' '.join(map(bytes.strip, lines)))
-            out.append('<p>\n<b>%s</b> %s\n</p>\n' % (admonition, text))
-        elif btype == 'paragraph':
-            out.append('<p>\n%s\n</p>\n' % escape('\n'.join(lines)))
-        elif btype == 'margin':
+        if btype == b'admonition':
+            admonition = escape(_admonitiontitles[b[b'admonitiontitle']])
+            text = escape(b' '.join(map(bytes.strip, lines)))
+            out.append(b'<p>\n<b>%s</b> %s\n</p>\n' % (admonition, text))
+        elif btype == b'paragraph':
+            out.append(b'<p>\n%s\n</p>\n' % escape(b'\n'.join(lines)))
+        elif btype == b'margin':
             pass
-        elif btype == 'literal':
-            out.append('<pre>\n%s\n</pre>\n' % escape('\n'.join(lines)))
-        elif btype == 'section':
-            i = b['underline']
+        elif btype == b'literal':
+            out.append(b'<pre>\n%s\n</pre>\n' % escape(b'\n'.join(lines)))
+        elif btype == b'section':
+            i = b[b'underline']
             if i not in headernest:
                 headernest += i
             level = headernest.index(i) + 1
-            out.append('<h%d>%s</h%d>\n' % (level, escape(lines[0]), level))
-        elif btype == 'table':
-            table = b['table']
-            out.append('<table>\n')
+            out.append(b'<h%d>%s</h%d>\n' % (level, escape(lines[0]), level))
+        elif btype == b'table':
+            table = b[b'table']
+            out.append(b'<table>\n')
             for row in table:
-                out.append('<tr>')
+                out.append(b'<tr>')
                 for v in row:
-                    out.append('<td>')
+                    out.append(b'<td>')
                     out.append(escape(v))
-                    out.append('</td>')
-                    out.append('\n')
+                    out.append(b'</td>')
+                    out.append(b'\n')
                 out.pop()
-                out.append('</tr>\n')
-            out.append('</table>\n')
-        elif btype == 'definition':
-            openlist('dl', level)
+                out.append(b'</tr>\n')
+            out.append(b'</table>\n')
+        elif btype == b'definition':
+            openlist(b'dl', level)
             term = escape(lines[0])
-            text = escape(' '.join(map(bytes.strip, lines[1:])))
-            out.append(' <dt>%s\n <dd>%s\n' % (term, text))
-        elif btype == 'bullet':
-            bullet, head = lines[0].split(' ', 1)
-            if bullet in ('*', '-'):
-                openlist('ul', level)
+            text = escape(b' '.join(map(bytes.strip, lines[1:])))
+            out.append(b' <dt>%s\n <dd>%s\n' % (term, text))
+        elif btype == b'bullet':
+            bullet, head = lines[0].split(b' ', 1)
+            if bullet in (b'*', b'-'):
+                openlist(b'ul', level)
             else:
-                openlist('ol', level)
-            out.append(' <li> %s\n' % escape(' '.join([head] + lines[1:])))
-        elif btype == 'field':
-            openlist('dl', level)
-            key = escape(b['key'])
-            text = escape(' '.join(map(bytes.strip, lines)))
-            out.append(' <dt>%s\n <dd>%s\n' % (key, text))
-        elif btype == 'option':
-            openlist('dl', level)
-            opt = escape(b['optstr'])
-            desc = escape(' '.join(map(bytes.strip, lines)))
-            out.append(' <dt>%s\n <dd>%s\n' % (opt, desc))
+                openlist(b'ol', level)
+            out.append(b' <li> %s\n' % escape(b' '.join([head] + lines[1:])))
+        elif btype == b'field':
+            openlist(b'dl', level)
+            key = escape(b[b'key'])
+            text = escape(b' '.join(map(bytes.strip, lines)))
+            out.append(b' <dt>%s\n <dd>%s\n' % (key, text))
+        elif btype == b'option':
+            openlist(b'dl', level)
+            opt = escape(b[b'optstr'])
+            desc = escape(b' '.join(map(bytes.strip, lines)))
+            out.append(b' <dt>%s\n <dd>%s\n' % (opt, desc))
 
         # close lists if indent level of next block is lower
         if listnest:
             start, level = listnest[-1]
             if pos == len(blocks) - 1:
-                out.append('</%s>\n' % start)
+                out.append(b'</%s>\n' % start)
                 listnest.pop()
             else:
                 nb = blocks[pos + 1]
-                ni = nb['indent']
+                ni = nb[b'indent']
                 if ni < level or (
                     ni == level
-                    and nb['type'] not in 'definition bullet field option'
+                    and nb[b'type'] not in b'definition bullet field option'
                 ):
-                    out.append('</%s>\n' % start)
+                    out.append(b'</%s>\n' % start)
                     listnest.pop()
 
-    return ''.join(out)
+    return b''.join(out)
 
 
 def parse(text, indent=0, keep=None, admonitions=None):
     """Parse text into a list of blocks"""
     blocks = findblocks(text)
     for b in blocks:
-        b['indent'] += indent
+        b[b'indent'] += indent
     blocks = findliteralblocks(blocks)
     blocks = findtables(blocks)
     blocks, pruned = prunecontainers(blocks, keep or [])
@@ -722,21 +722,21 @@
 
 
 def formatblocks(blocks, width):
-    text = ''.join(formatblock(b, width) for b in blocks)
+    text = b''.join(formatblock(b, width) for b in blocks)
     return text
 
 
 def formatplain(blocks, width):
     """Format parsed blocks as plain text"""
-    return ''.join(formatblock(b, width) for b in blocks)
+    return b''.join(formatblock(b, width) for b in blocks)
 
 
-def format(text, width=80, indent=0, keep=None, style='plain', section=None):
+def format(text, width=80, indent=0, keep=None, style=b'plain', section=None):
     """Parse and format the text according to width."""
     blocks, pruned = parse(text, indent, keep or [])
     if section:
         blocks = filtersections(blocks, section)
-    if style == 'html':
+    if style == b'html':
         return formathtml(blocks)
     else:
         return formatplain(blocks, width=width)
@@ -759,7 +759,7 @@
         path, nest, b = sections[i]
         del parents[nest:]
         parents.append(i)
-        if path == section or path.endswith('.' + section):
+        if path == section or path.endswith(b'.' + section):
             if lastparents != parents:
                 llen = len(lastparents)
                 plen = len(parents)
@@ -787,11 +787,11 @@
     if collapse:
         synthetic.reverse()
         for s in synthetic:
-            path = [blocks[syn]['lines'][0] for syn in s]
+            path = [blocks[syn][b'lines'][0] for syn in s]
             real = s[-1] + 2
-            realline = blocks[real]['lines']
-            realline[0] = '"%s"' % '.'.join(path + [realline[0]]).replace(
-                '"', ''
+            realline = blocks[real][b'lines']
+            realline[0] = b'"%s"' % b'.'.join(path + [realline[0]]).replace(
+                b'"', b''
             )
             del blocks[s[0] : real]
 
@@ -800,31 +800,31 @@
 
 def _getsections(blocks):
     '''return a list of (section path, nesting level, blocks) tuples'''
-    nest = ""
+    nest = b""
     names = ()
     secs = []
 
     def getname(b):
-        if b['type'] == 'field':
-            x = b['key']
+        if b[b'type'] == b'field':
+            x = b[b'key']
         else:
-            x = b['lines'][0]
-        x = encoding.lower(x).strip('"')
-        if '(' in x:
-            x = x.split('(')[0]
+            x = b[b'lines'][0]
+        x = encoding.lower(x).strip(b'"')
+        if b'(' in x:
+            x = x.split(b'(')[0]
         return x
 
     for b in blocks:
-        if b['type'] == 'section':
-            i = b['underline']
+        if b[b'type'] == b'section':
+            i = b[b'underline']
             if i not in nest:
                 nest += i
             level = nest.index(i) + 1
             nest = nest[:level]
             names = names[:level] + (getname(b),)
-            secs.append(('.'.join(names), level, [b]))
-        elif b['type'] in ('definition', 'field'):
-            i = ' '
+            secs.append((b'.'.join(names), level, [b]))
+        elif b[b'type'] in (b'definition', b'field'):
+            i = b' '
             if i not in nest:
                 nest += i
             level = nest.index(i) + 1
@@ -833,10 +833,10 @@
                 sec = secs[-i]
                 if sec[1] < level:
                     break
-                siblings = [a for a in sec[2] if a['type'] == 'definition']
+                siblings = [a for a in sec[2] if a[b'type'] == b'definition']
                 if siblings:
-                    siblingindent = siblings[-1]['indent']
-                    indent = b['indent']
+                    siblingindent = siblings[-1][b'indent']
+                    indent = b[b'indent']
                     if siblingindent < indent:
                         level += 1
                         break
@@ -844,30 +844,30 @@
                         level = sec[1]
                         break
             names = names[:level] + (getname(b),)
-            secs.append(('.'.join(names), level, [b]))
+            secs.append((b'.'.join(names), level, [b]))
         else:
             if not secs:
                 # add an initial empty section
-                secs = [('', 0, [])]
-            if b['type'] != 'margin':
+                secs = [(b'', 0, [])]
+            if b[b'type'] != b'margin':
                 pointer = 1
-                bindent = b['indent']
+                bindent = b[b'indent']
                 while pointer < len(secs):
                     section = secs[-pointer][2][0]
-                    if section['type'] != 'margin':
-                        sindent = section['indent']
-                        if len(section['lines']) > 1:
-                            sindent += len(section['lines'][1]) - len(
-                                section['lines'][1].lstrip(' ')
+                    if section[b'type'] != b'margin':
+                        sindent = section[b'indent']
+                        if len(section[b'lines']) > 1:
+                            sindent += len(section[b'lines'][1]) - len(
+                                section[b'lines'][1].lstrip(b' ')
                             )
                         if bindent >= sindent:
                             break
                     pointer += 1
                 if pointer > 1:
                     blevel = secs[-pointer][1]
-                    if section['type'] != b['type']:
+                    if section[b'type'] != b[b'type']:
                         blevel += 1
-                    secs.append(('', blevel, []))
+                    secs.append((b'', blevel, []))
             secs[-1][2].append(b)
     return secs
 
@@ -876,20 +876,20 @@
     '''Generate an RST table for the given table data as a list of lines'''
 
     widths = [max(encoding.colwidth(e) for e in c) for c in zip(*data)]
-    indent = ' ' * indent
-    div = indent + ' '.join('=' * w for w in widths) + '\n'
+    indent = b' ' * indent
+    div = indent + b' '.join(b'=' * w for w in widths) + b'\n'
 
     out = [div]
     for row in data:
         l = []
         for w, v in zip(widths, row):
-            if '\n' in v:
+            if b'\n' in v:
                 # only remove line breaks and indentation, long lines are
                 # handled by the next tool
-                v = ' '.join(e.lstrip() for e in v.split('\n'))
-            pad = ' ' * (w - encoding.colwidth(v))
+                v = b' '.join(e.lstrip() for e in v.split(b'\n'))
+            pad = b' ' * (w - encoding.colwidth(v))
             l.append(v + pad)
-        out.append(indent + ' '.join(l) + "\n")
+        out.append(indent + b' '.join(l) + b"\n")
     if header and len(data) > 1:
         out.insert(2, div)
     out.append(div)