mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
0. do not escape Unicode Greek letters via LaTeX math mark-up: pass them through un-modified to LaTeX document, 1. if "fontenc" receives extra option LGR, then pdflatex will support Unicode Greek letters (not in math), and with extra option T2A it will support (most) Unicode Cyrillic letters. 2. for pdflatex with LGR, this will use "textalpha" LaTeX package and "substitutefont" package to set up some automatic font substitution to work around the unavailability of Greek with "times" package (which is default font package chosen by Sphinx for pdflatex), same with T2A and "substitutefont" for Cyrillic. 3. for xelatex/lualatex, set up Computer Modern Unicode as default font, as it supports Cyrillic and Greek scripts, 4. for platex, don't do anything special as the engine already has its default font supporting Cyrillic and Greek (even in math mode!) Closes: #5251 Fixes: #5248 Fixes: #5247
343 lines
11 KiB
Python
343 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 pickle
|
|
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.latex import LaTeXBuilder
|
|
from sphinx.testing.util import assert_node
|
|
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
|
|
|
|
|
|
@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)
|
|
app.builder = LaTeXBuilder(app)
|
|
app.builder.set_environment(app.env)
|
|
app.builder.init_context()
|
|
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 notranslate"><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 notranslate"><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 notranslate">'
|
|
'<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 menuselection
|
|
'verify',
|
|
':menuselection:`&Foo -&&- &Bar`',
|
|
(u'<p><span class="menuselection"><span class="accelerator">F</span>oo '
|
|
'-&- <span class="accelerator">B</span>ar</span></p>'),
|
|
r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
|
|
),
|
|
(
|
|
# interpolation of ampersands in guilabel
|
|
'verify',
|
|
':guilabel:`&Foo -&&- &Bar`',
|
|
(u'<p><span class="guilabel"><span class="accelerator">F</span>oo '
|
|
'-&- <span class="accelerator">B</span>ar</span></p>'),
|
|
r'\sphinxguilabel{\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 notranslate")?>'
|
|
'<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 notranslate"><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,
|
|
u'Γ\\textbackslash{}\\(\\infty\\)\\$',
|
|
),
|
|
(
|
|
# in verbatim code fragments
|
|
'verify',
|
|
u'::\n\n @Γ\\∞${}',
|
|
None,
|
|
(u'\\fvset{hllines={, ,}}%\n'
|
|
u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
|
|
u'@Γ\\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)
|