Fix #7610: incorrectly renders consecutive backslashes

This commit is contained in:
Takeshi KOMIYA 2020-05-02 00:09:55 +09:00
parent 6ce265dc81
commit 71b653719d
3 changed files with 31 additions and 7 deletions

View File

@ -89,6 +89,7 @@ Bugs fixed
* #7536: sphinx-autogen: crashes when template uses i18n feature
* #2785: html: Bad alignment of equation links
* #7581: napoleon: bad parsing of inline code in attribute docstrings
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
Testing
--------

View File

@ -23,6 +23,7 @@ from sphinx import addnodes
from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.locale import _, __
from sphinx.util import docutils
from sphinx.util import logging
from sphinx.util.docutils import new_document
from sphinx.util.i18n import format_date
@ -360,12 +361,18 @@ class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
def get_tokens(self, txtnodes: List[Text]) -> Generator[Tuple[str, str], None, None]:
# A generator that yields ``(texttype, nodetext)`` tuples for a list
# of "Text" nodes (interface to ``smartquotes.educate_tokens()``).
texttype = {True: 'literal', # "literal" text is not changed:
False: 'plain'}
for txtnode in txtnodes:
notsmartquotable = not is_smartquotable(txtnode)
yield (texttype[notsmartquotable], txtnode.astext())
if is_smartquotable(txtnode):
if docutils.__version_info__ >= (0, 16):
# SmartQuotes uses backslash escapes instead of null-escapes
text = re.sub(r'(?<=\x00)([-\\\'".`])', r'\\\1', str(txtnode))
else:
text = txtnode.astext()
yield ('plain', text)
else:
# skip smart quotes
yield ('literal', txtnode.astext())
class DoctreeReadEvent(SphinxTransform):

View File

@ -13,7 +13,6 @@ import re
import pytest
from docutils import frontend, utils, nodes
from docutils.parsers.rst import Parser as RstParser
from docutils.transforms.universal import SmartQuotes
from sphinx import addnodes
from sphinx.builders.html.transforms import KeyboardTransform
@ -21,6 +20,8 @@ from sphinx.builders.latex import LaTeXBuilder
from sphinx.builders.latex.theming import ThemeFactory
from sphinx.roles import XRefRole
from sphinx.testing.util import Struct, assert_node
from sphinx.transforms import SphinxSmartQuotes
from sphinx.util import docutils
from sphinx.util import texescape
from sphinx.util.docutils import sphinx_domains
from sphinx.writers.html import HTMLWriter, HTMLTranslator
@ -67,7 +68,7 @@ def parse(new_document):
document = new_document()
parser = RstParser()
parser.parse(rst, document)
SmartQuotes(document, startnode=None).apply()
SphinxSmartQuotes(document, startnode=None).apply()
for msg in document.traverse(nodes.system_message):
if msg['level'] == 1:
msg.replace_self([])
@ -349,6 +350,21 @@ def test_inline(get_verifier, type, rst, html_expected, latex_expected):
verifier(rst, html_expected, latex_expected)
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
(
'verify',
r'4 backslashes \\\\',
r'<p>4 backslashes \\</p>',
None,
),
])
@pytest.mark.skipif(docutils.__version_info__ < (0, 16),
reason='docutils-0.16 or above is required')
def test_inline_docutils16(get_verifier, type, rst, html_expected, latex_expected):
verifier = get_verifier(type)
verifier(rst, html_expected, latex_expected)
@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'})
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
(