mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Since #2627 (1.4.4), `\code`, and then again at #3116 (1.5) `\sphinxcode` which is the new name has become more complicated than the original `\texttt{#1}`. This was to obtain straight quotes in PDF output, and to allow long inline literals to break across lines. This means though that users who want to customize `\sphinxcode`, for example to not only do `\texttt` but to use some colour, have to copy about 10 lines of complicated LaTeX macros which should be not modified in any way. This commit moves all the code out of `\sphinxcode` into a separate macro `\sphinxupquote`. The LaTeX writer will output `\sphinxcode{\sphinxupquote{foo}}` in place of former `\sphinxcode{foo}`. Moving the `\texttt` from innermost to outermost level is with no consequence.
331 lines
11 KiB
Python
331 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
test_markup
|
|
~~~~~~~~~~~
|
|
|
|
Test various Sphinx-specific markup extensions.
|
|
|
|
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
import re
|
|
import pickle
|
|
|
|
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.util import texescape
|
|
from sphinx.util.docutils import sphinx_domains
|
|
from sphinx.writers.html import HTMLWriter, HTMLTranslator
|
|
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
|
|
import pytest
|
|
|
|
from sphinx.testing.util import assert_node
|
|
|
|
|
|
@pytest.fixture
|
|
def settings(app):
|
|
texescape.init() # otherwise done by the latex builder
|
|
optparser = frontend.OptionParser(
|
|
components=(RstParser, HTMLWriter, LaTeXWriter))
|
|
settings = optparser.get_default_values()
|
|
settings.smart_quotes = True
|
|
settings.env = app.builder.env
|
|
settings.env.temp_data['docname'] = 'dummy'
|
|
domain_context = sphinx_domains(settings.env)
|
|
domain_context.enable()
|
|
yield settings
|
|
domain_context.disable()
|
|
|
|
|
|
@pytest.fixture
|
|
def parse(settings):
|
|
def parse_(rst):
|
|
document = utils.new_document(b'test data', settings)
|
|
document['file'] = 'dummy'
|
|
parser = RstParser()
|
|
parser.parse(rst, document)
|
|
SmartQuotes(document, startnode=None).apply()
|
|
for msg in document.traverse(nodes.system_message):
|
|
if msg['level'] == 1:
|
|
msg.replace_self([])
|
|
return document
|
|
return parse_
|
|
|
|
|
|
# since we're not resolving the markup afterwards, these nodes may remain
|
|
class ForgivingTranslator:
|
|
def visit_pending_xref(self, node):
|
|
pass
|
|
|
|
def depart_pending_xref(self, node):
|
|
pass
|
|
|
|
|
|
class ForgivingHTMLTranslator(HTMLTranslator, ForgivingTranslator):
|
|
pass
|
|
|
|
|
|
class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
|
|
pass
|
|
|
|
|
|
@pytest.fixture
|
|
def verify_re_html(app, parse):
|
|
def verify(rst, html_expected):
|
|
document = parse(rst)
|
|
html_translator = ForgivingHTMLTranslator(app.builder, document)
|
|
document.walkabout(html_translator)
|
|
html_translated = ''.join(html_translator.fragment).strip()
|
|
assert re.match(html_expected, html_translated), 'from ' + rst
|
|
return verify
|
|
|
|
|
|
@pytest.fixture
|
|
def verify_re_latex(app, parse):
|
|
def verify(rst, latex_expected):
|
|
document = parse(rst)
|
|
latex_translator = ForgivingLaTeXTranslator(document, app.builder)
|
|
latex_translator.first_document = -1 # don't write \begin{document}
|
|
document.walkabout(latex_translator)
|
|
latex_translated = ''.join(latex_translator.body).strip()
|
|
assert re.match(latex_expected, latex_translated), 'from ' + repr(rst)
|
|
return verify
|
|
|
|
|
|
@pytest.fixture
|
|
def verify_re(verify_re_html, verify_re_latex):
|
|
def verify_re_(rst, html_expected, latex_expected):
|
|
if html_expected:
|
|
verify_re_html(rst, html_expected)
|
|
if latex_expected:
|
|
verify_re_latex(rst, latex_expected)
|
|
return verify_re_
|
|
|
|
|
|
@pytest.fixture
|
|
def verify(verify_re_html, verify_re_latex):
|
|
def verify_(rst, html_expected, latex_expected):
|
|
if html_expected:
|
|
verify_re_html(rst, re.escape(html_expected) + '$')
|
|
if latex_expected:
|
|
verify_re_latex(rst, re.escape(latex_expected) + '$')
|
|
return verify_
|
|
|
|
|
|
@pytest.fixture
|
|
def get_verifier(verify, verify_re):
|
|
v = {
|
|
'verify': verify,
|
|
'verify_re': verify_re,
|
|
}
|
|
|
|
def get(name):
|
|
return v[name]
|
|
return get
|
|
|
|
|
|
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
|
|
(
|
|
# correct interpretation of code with whitespace
|
|
'verify_re',
|
|
'``code sample``',
|
|
('<p><code class="(samp )?docutils literal"><span class="pre">'
|
|
'code</span>   <span class="pre">sample</span></code></p>'),
|
|
r'\\sphinxcode{\\sphinxupquote{code sample}}',
|
|
),
|
|
(
|
|
# correct interpretation of code with whitespace
|
|
'verify_re',
|
|
':samp:`code sample`',
|
|
('<p><code class="(samp )?docutils literal"><span class="pre">'
|
|
'code</span>   <span class="pre">sample</span></code></p>'),
|
|
r'\\sphinxcode{\\sphinxupquote{code sample}}',
|
|
),
|
|
(
|
|
# interpolation of braces in samp and file roles (HTML only)
|
|
'verify',
|
|
':samp:`a{b}c`',
|
|
('<p><code class="samp docutils literal"><span class="pre">a</span>'
|
|
'<em><span class="pre">b</span></em>'
|
|
'<span class="pre">c</span></code></p>'),
|
|
'\\sphinxcode{\\sphinxupquote{a\\sphinxstyleemphasis{b}c}}',
|
|
),
|
|
(
|
|
# interpolation of arrows in menuselection
|
|
'verify',
|
|
':menuselection:`a --> b`',
|
|
(u'<p><span class="menuselection">a \N{TRIANGULAR BULLET} b</span></p>'),
|
|
'\\sphinxmenuselection{a \\(\\rightarrow\\) b}',
|
|
),
|
|
(
|
|
# interpolation of ampersands in guilabel/menuselection
|
|
'verify',
|
|
':guilabel:`&Foo -&&- &Bar`',
|
|
(u'<p><span class="guilabel"><span class="accelerator">F</span>oo '
|
|
'-&- <span class="accelerator">B</span>ar</span></p>'),
|
|
r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
|
|
),
|
|
(
|
|
# non-interpolation of dashes in option role
|
|
'verify_re',
|
|
':option:`--with-option`',
|
|
('<p><code( class="xref std std-option docutils literal")?>'
|
|
'<span class="pre">--with-option</span></code></p>$'),
|
|
r'\\sphinxcode{\\sphinxupquote{-{-}with-option}}$',
|
|
),
|
|
(
|
|
# verify smarty-pants quotes
|
|
'verify',
|
|
'"John"',
|
|
u'<p>“John”</p>',
|
|
u"“John”",
|
|
),
|
|
(
|
|
# ... but not in literal text
|
|
'verify',
|
|
'``"John"``',
|
|
('<p><code class="docutils literal"><span class="pre">'
|
|
'"John"</span></code></p>'),
|
|
'\\sphinxcode{\\sphinxupquote{"John"}}',
|
|
),
|
|
(
|
|
# verify classes for inline roles
|
|
'verify',
|
|
':manpage:`mp(1)`',
|
|
'<p><em class="manpage">mp(1)</em></p>',
|
|
'\\sphinxstyleliteralemphasis{\\sphinxupquote{mp(1)}}',
|
|
),
|
|
(
|
|
# correct escaping in normal mode
|
|
'verify',
|
|
u'Γ\\\\∞$',
|
|
None,
|
|
r'\(\Gamma\)\textbackslash{}\(\infty\)\$',
|
|
),
|
|
(
|
|
# in verbatim code fragments
|
|
'verify',
|
|
u'::\n\n @Γ\\∞${}',
|
|
None,
|
|
(u'\\fvset{hllines={, ,}}%\n'
|
|
u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
|
|
u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
|
|
u'\\end{sphinxVerbatim}'),
|
|
),
|
|
(
|
|
# in URIs
|
|
'verify_re',
|
|
u'`test <http://example.com/~me/>`_',
|
|
None,
|
|
r'\\sphinxhref{http://example.com/~me/}{test}.*',
|
|
),
|
|
])
|
|
def test_inline(get_verifier, type, rst, html_expected, latex_expected):
|
|
verifier = get_verifier(type)
|
|
verifier(rst, html_expected, latex_expected)
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='prolog')
|
|
def test_rst_prolog(app, status, warning):
|
|
app.builder.build_all()
|
|
rst = pickle.loads((app.doctreedir / 'restructuredtext.doctree').bytes())
|
|
md = pickle.loads((app.doctreedir / 'markdown.doctree').bytes())
|
|
|
|
# rst_prolog
|
|
assert_node(rst[0], nodes.paragraph)
|
|
assert_node(rst[0][0], nodes.emphasis)
|
|
assert_node(rst[0][0][0], nodes.Text)
|
|
assert rst[0][0][0] == 'Hello world'
|
|
|
|
# rst_epilog
|
|
assert_node(rst[-1], nodes.section)
|
|
assert_node(rst[-1][-1], nodes.paragraph)
|
|
assert_node(rst[-1][-1][0], nodes.emphasis)
|
|
assert_node(rst[-1][-1][0][0], nodes.Text)
|
|
assert rst[-1][-1][0][0] == 'Good-bye world'
|
|
|
|
# rst_prolog & rst_epilog on exlucding reST parser
|
|
assert not md.rawsource.startswith('*Hello world*.')
|
|
assert not md.rawsource.endswith('*Good-bye world*.\n')
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='keep_warnings')
|
|
def test_keep_warnings_is_True(app, status, warning):
|
|
app.builder.build_all()
|
|
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert len(doctree[0]) == 2
|
|
assert_node(doctree[0][1], nodes.system_message)
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='keep_warnings',
|
|
confoverrides={'keep_warnings': False})
|
|
def test_keep_warnings_is_False(app, status, warning):
|
|
app.builder.build_all()
|
|
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert len(doctree[0]) == 1
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='refonly_bullet_list')
|
|
def test_compact_refonly_bullet_list(app, status, warning):
|
|
app.builder.build_all()
|
|
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert len(doctree[0]) == 5
|
|
|
|
assert doctree[0][1].astext() == 'List A:'
|
|
assert_node(doctree[0][2], nodes.bullet_list)
|
|
assert_node(doctree[0][2][0][0], addnodes.compact_paragraph)
|
|
assert doctree[0][2][0][0].astext() == 'genindex'
|
|
|
|
assert doctree[0][3].astext() == 'List B:'
|
|
assert_node(doctree[0][4], nodes.bullet_list)
|
|
assert_node(doctree[0][4][0][0], nodes.paragraph)
|
|
assert doctree[0][4][0][0].astext() == 'Hello'
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='default_role')
|
|
def test_default_role1(app, status, warning):
|
|
app.builder.build_all()
|
|
|
|
# default-role: pep
|
|
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert_node(doctree[0][1], nodes.paragraph)
|
|
assert_node(doctree[0][1][0], addnodes.index)
|
|
assert_node(doctree[0][1][1], nodes.target)
|
|
assert_node(doctree[0][1][2], nodes.reference, classes=["pep"])
|
|
|
|
# no default-role
|
|
doctree = pickle.loads((app.doctreedir / 'foo.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert_node(doctree[0][1], nodes.paragraph)
|
|
assert_node(doctree[0][1][0], nodes.title_reference)
|
|
assert_node(doctree[0][1][1], nodes.Text)
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='default_role',
|
|
confoverrides={'default_role': 'guilabel'})
|
|
def test_default_role2(app, status, warning):
|
|
app.builder.build_all()
|
|
|
|
# default-role directive is stronger than configratuion
|
|
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert_node(doctree[0][1], nodes.paragraph)
|
|
assert_node(doctree[0][1][0], addnodes.index)
|
|
assert_node(doctree[0][1][1], nodes.target)
|
|
assert_node(doctree[0][1][2], nodes.reference, classes=["pep"])
|
|
|
|
# default_role changes the default behavior
|
|
doctree = pickle.loads((app.doctreedir / 'foo.doctree').bytes())
|
|
assert_node(doctree[0], nodes.section)
|
|
assert_node(doctree[0][1], nodes.paragraph)
|
|
assert_node(doctree[0][1][0], nodes.inline, classes=["guilabel"])
|
|
assert_node(doctree[0][1][1], nodes.Text)
|