1""" 2 test_markup 3 ~~~~~~~~~~~ 4 5 Test various Sphinx-specific markup extensions. 6 7 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 :license: BSD, see LICENSE for details. 9""" 10 11import re 12 13import pytest 14from docutils import frontend, nodes, utils 15from docutils.parsers.rst import Parser as RstParser 16 17from sphinx import addnodes 18from sphinx.builders.html.transforms import KeyboardTransform 19from sphinx.builders.latex import LaTeXBuilder 20from sphinx.roles import XRefRole 21from sphinx.testing.util import Struct, assert_node 22from sphinx.transforms import SphinxSmartQuotes 23from sphinx.util import docutils, texescape 24from sphinx.util.docutils import sphinx_domains 25from sphinx.writers.html import HTMLTranslator, HTMLWriter 26from sphinx.writers.latex import LaTeXTranslator, LaTeXWriter 27 28 29@pytest.fixture 30def settings(app): 31 texescape.init() # otherwise done by the latex builder 32 optparser = frontend.OptionParser( 33 components=(RstParser, HTMLWriter, LaTeXWriter)) 34 settings = optparser.get_default_values() 35 settings.smart_quotes = True 36 settings.env = app.builder.env 37 settings.env.temp_data['docname'] = 'dummy' 38 settings.contentsname = 'dummy' 39 settings.rfc_base_url = 'http://tools.ietf.org/html/' 40 domain_context = sphinx_domains(settings.env) 41 domain_context.enable() 42 yield settings 43 domain_context.disable() 44 45 46@pytest.fixture 47def new_document(settings): 48 def create(): 49 document = utils.new_document('test data', settings) 50 document['file'] = 'dummy' 51 return document 52 53 return create 54 55 56@pytest.fixture 57def inliner(new_document): 58 document = new_document() 59 document.reporter.get_source_and_line = lambda line=1: ('dummy.rst', line) 60 return Struct(document=document, reporter=document.reporter) 61 62 63@pytest.fixture 64def parse(new_document): 65 def parse_(rst): 66 document = new_document() 67 parser = RstParser() 68 parser.parse(rst, document) 69 SphinxSmartQuotes(document, startnode=None).apply() 70 for msg in document.traverse(nodes.system_message): 71 if msg['level'] == 1: 72 msg.replace_self([]) 73 return document 74 return parse_ 75 76 77# since we're not resolving the markup afterwards, these nodes may remain 78class ForgivingTranslator: 79 def visit_pending_xref(self, node): 80 pass 81 82 def depart_pending_xref(self, node): 83 pass 84 85 86class ForgivingHTMLTranslator(HTMLTranslator, ForgivingTranslator): 87 pass 88 89 90class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator): 91 pass 92 93 94@pytest.fixture 95def verify_re_html(app, parse): 96 def verify(rst, html_expected): 97 document = parse(rst) 98 KeyboardTransform(document).apply() 99 html_translator = ForgivingHTMLTranslator(document, app.builder) 100 document.walkabout(html_translator) 101 html_translated = ''.join(html_translator.fragment).strip() 102 assert re.match(html_expected, html_translated), 'from ' + rst 103 return verify 104 105 106@pytest.fixture 107def verify_re_latex(app, parse): 108 def verify(rst, latex_expected): 109 document = parse(rst) 110 app.builder = LaTeXBuilder(app) 111 app.builder.set_environment(app.env) 112 app.builder.init() 113 theme = app.builder.themes.get('manual') 114 latex_translator = ForgivingLaTeXTranslator(document, app.builder, theme) 115 latex_translator.first_document = -1 # don't write \begin{document} 116 document.walkabout(latex_translator) 117 latex_translated = ''.join(latex_translator.body).strip() 118 assert re.match(latex_expected, latex_translated), 'from ' + repr(rst) 119 return verify 120 121 122@pytest.fixture 123def verify_re(verify_re_html, verify_re_latex): 124 def verify_re_(rst, html_expected, latex_expected): 125 if html_expected: 126 verify_re_html(rst, html_expected) 127 if latex_expected: 128 verify_re_latex(rst, latex_expected) 129 return verify_re_ 130 131 132@pytest.fixture 133def verify(verify_re_html, verify_re_latex): 134 def verify_(rst, html_expected, latex_expected): 135 if html_expected: 136 verify_re_html(rst, re.escape(html_expected) + '$') 137 if latex_expected: 138 verify_re_latex(rst, re.escape(latex_expected) + '$') 139 return verify_ 140 141 142@pytest.fixture 143def get_verifier(verify, verify_re): 144 v = { 145 'verify': verify, 146 'verify_re': verify_re, 147 } 148 149 def get(name): 150 return v[name] 151 return get 152 153 154@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [ 155 ( 156 # pep role 157 'verify', 158 ':pep:`8`', 159 ('<p><span class="target" id="index-0"></span><a class="pep reference external" ' 160 'href="http://www.python.org/dev/peps/pep-0008"><strong>PEP 8</strong></a></p>'), 161 ('\\sphinxAtStartPar\n' 162 '\\index{Python Enhancement Proposals@\\spxentry{Python Enhancement Proposals}' 163 '!PEP 8@\\spxentry{PEP 8}}\\sphinxhref{http://www.python.org/dev/peps/pep-0008}' 164 '{\\sphinxstylestrong{PEP 8}}') 165 ), 166 ( 167 # pep role with anchor 168 'verify', 169 ':pep:`8#id1`', 170 ('<p><span class="target" id="index-0"></span><a class="pep reference external" ' 171 'href="http://www.python.org/dev/peps/pep-0008#id1">' 172 '<strong>PEP 8#id1</strong></a></p>'), 173 ('\\sphinxAtStartPar\n' 174 '\\index{Python Enhancement Proposals@\\spxentry{Python Enhancement Proposals}' 175 '!PEP 8\\#id1@\\spxentry{PEP 8\\#id1}}\\sphinxhref' 176 '{http://www.python.org/dev/peps/pep-0008\\#id1}' 177 '{\\sphinxstylestrong{PEP 8\\#id1}}') 178 ), 179 ( 180 # rfc role 181 'verify', 182 ':rfc:`2324`', 183 ('<p><span class="target" id="index-0"></span><a class="rfc reference external" ' 184 'href="http://tools.ietf.org/html/rfc2324.html"><strong>RFC 2324</strong></a></p>'), 185 ('\\sphinxAtStartPar\n' 186 '\\index{RFC@\\spxentry{RFC}!RFC 2324@\\spxentry{RFC 2324}}' 187 '\\sphinxhref{http://tools.ietf.org/html/rfc2324.html}' 188 '{\\sphinxstylestrong{RFC 2324}}') 189 ), 190 ( 191 # rfc role with anchor 192 'verify', 193 ':rfc:`2324#id1`', 194 ('<p><span class="target" id="index-0"></span><a class="rfc reference external" ' 195 'href="http://tools.ietf.org/html/rfc2324.html#id1">' 196 '<strong>RFC 2324#id1</strong></a></p>'), 197 ('\\sphinxAtStartPar\n' 198 '\\index{RFC@\\spxentry{RFC}!RFC 2324\\#id1@\\spxentry{RFC 2324\\#id1}}' 199 '\\sphinxhref{http://tools.ietf.org/html/rfc2324.html\\#id1}' 200 '{\\sphinxstylestrong{RFC 2324\\#id1}}') 201 ), 202 ( 203 # correct interpretation of code with whitespace 204 'verify_re', 205 '``code sample``', 206 ('<p><code class="(samp )?docutils literal notranslate"><span class="pre">' 207 'code</span>   <span class="pre">sample</span></code></p>'), 208 r'\\sphinxAtStartPar\n\\sphinxcode{\\sphinxupquote{code sample}}', 209 ), 210 ( 211 # interpolation of arrows in menuselection 212 'verify', 213 ':menuselection:`a --> b`', 214 ('<p><span class="menuselection">a \N{TRIANGULAR BULLET} b</span></p>'), 215 '\\sphinxAtStartPar\n\\sphinxmenuselection{a \\(\\rightarrow\\) b}', 216 ), 217 ( 218 # interpolation of ampersands in menuselection 219 'verify', 220 ':menuselection:`&Foo -&&- &Bar`', 221 ('<p><span class="menuselection"><span class="accelerator">F</span>oo ' 222 '-&- <span class="accelerator">B</span>ar</span></p>'), 223 ('\\sphinxAtStartPar\n' 224 r'\sphinxmenuselection{\sphinxaccelerator{F}oo \sphinxhyphen{}' 225 r'\&\sphinxhyphen{} \sphinxaccelerator{B}ar}'), 226 ), 227 ( 228 # interpolation of ampersands in guilabel 229 'verify', 230 ':guilabel:`&Foo -&&- &Bar`', 231 ('<p><span class="guilabel"><span class="accelerator">F</span>oo ' 232 '-&- <span class="accelerator">B</span>ar</span></p>'), 233 ('\\sphinxAtStartPar\n' 234 r'\sphinxguilabel{\sphinxaccelerator{F}oo \sphinxhyphen{}\&\sphinxhyphen{} \sphinxaccelerator{B}ar}'), 235 ), 236 ( 237 # no ampersands in guilabel 238 'verify', 239 ':guilabel:`Foo`', 240 '<p><span class="guilabel">Foo</span></p>', 241 '\\sphinxAtStartPar\n\\sphinxguilabel{Foo}', 242 ), 243 ( 244 # kbd role 245 'verify', 246 ':kbd:`space`', 247 '<p><kbd class="kbd docutils literal notranslate">space</kbd></p>', 248 '\\sphinxAtStartPar\n\\sphinxkeyboard{\\sphinxupquote{space}}', 249 ), 250 ( 251 # kbd role 252 'verify', 253 ':kbd:`Control+X`', 254 ('<p><kbd class="kbd compound docutils literal notranslate">' 255 '<kbd class="kbd docutils literal notranslate">Control</kbd>' 256 '+' 257 '<kbd class="kbd docutils literal notranslate">X</kbd>' 258 '</kbd></p>'), 259 '\\sphinxAtStartPar\n\\sphinxkeyboard{\\sphinxupquote{Control+X}}', 260 ), 261 ( 262 # kbd role 263 'verify', 264 ':kbd:`Alt+^`', 265 ('<p><kbd class="kbd compound docutils literal notranslate">' 266 '<kbd class="kbd docutils literal notranslate">Alt</kbd>' 267 '+' 268 '<kbd class="kbd docutils literal notranslate">^</kbd>' 269 '</kbd></p>'), 270 ('\\sphinxAtStartPar\n' 271 '\\sphinxkeyboard{\\sphinxupquote{Alt+\\textasciicircum{}}}'), 272 ), 273 ( 274 # kbd role 275 'verify', 276 ':kbd:`M-x M-s`', 277 ('<p><kbd class="kbd compound docutils literal notranslate">' 278 '<kbd class="kbd docutils literal notranslate">M</kbd>' 279 '-' 280 '<kbd class="kbd docutils literal notranslate">x</kbd>' 281 ' ' 282 '<kbd class="kbd docutils literal notranslate">M</kbd>' 283 '-' 284 '<kbd class="kbd docutils literal notranslate">s</kbd>' 285 '</kbd></p>'), 286 ('\\sphinxAtStartPar\n' 287 '\\sphinxkeyboard{\\sphinxupquote{M\\sphinxhyphen{}x M\\sphinxhyphen{}s}}'), 288 ), 289 ( 290 # kbd role 291 'verify', 292 ':kbd:`-`', 293 '<p><kbd class="kbd docutils literal notranslate">-</kbd></p>', 294 ('\\sphinxAtStartPar\n' 295 '\\sphinxkeyboard{\\sphinxupquote{\\sphinxhyphen{}}}'), 296 ), 297 ( 298 # kbd role 299 'verify', 300 ':kbd:`Caps Lock`', 301 '<p><kbd class="kbd docutils literal notranslate">Caps Lock</kbd></p>', 302 ('\\sphinxAtStartPar\n' 303 '\\sphinxkeyboard{\\sphinxupquote{Caps Lock}}'), 304 ), 305 ( 306 # non-interpolation of dashes in option role 307 'verify_re', 308 ':option:`--with-option`', 309 ('<p><code( class="xref std std-option docutils literal notranslate")?>' 310 '<span class="pre">--with-option</span></code></p>$'), 311 (r'\\sphinxAtStartPar\n' 312 r'\\sphinxcode{\\sphinxupquote{\\sphinxhyphen{}\\sphinxhyphen{}with\\sphinxhyphen{}option}}$'), 313 ), 314 ( 315 # verify smarty-pants quotes 316 'verify', 317 '"John"', 318 '<p>“John”</p>', 319 "\\sphinxAtStartPar\n“John”", 320 ), 321 ( 322 # ... but not in literal text 323 'verify', 324 '``"John"``', 325 ('<p><code class="docutils literal notranslate"><span class="pre">' 326 '"John"</span></code></p>'), 327 '\\sphinxAtStartPar\n\\sphinxcode{\\sphinxupquote{"John"}}', 328 ), 329 ( 330 # verify classes for inline roles 331 'verify', 332 ':manpage:`mp(1)`', 333 '<p><em class="manpage">mp(1)</em></p>', 334 '\\sphinxAtStartPar\n\\sphinxstyleliteralemphasis{\\sphinxupquote{mp(1)}}', 335 ), 336 ( 337 # correct escaping in normal mode 338 'verify', 339 'Γ\\\\∞$', 340 None, 341 '\\sphinxAtStartPar\nΓ\\textbackslash{}\\(\\infty\\)\\$', 342 ), 343 ( 344 # in verbatim code fragments 345 'verify', 346 '::\n\n @Γ\\∞${}', 347 None, 348 ('\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n' 349 '@Γ\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n' 350 '\\end{sphinxVerbatim}'), 351 ), 352 ( 353 # in URIs 354 'verify_re', 355 '`test <https://www.google.com/~me/>`_', 356 None, 357 r'\\sphinxAtStartPar\n\\sphinxhref{https://www.google.com/~me/}{test}.*', 358 ), 359 ( 360 # description list: simple 361 'verify', 362 'term\n description', 363 '<dl class="docutils">\n<dt>term</dt><dd>description</dd>\n</dl>', 364 None, 365 ), 366 ( 367 # description list: with classifiers 368 'verify', 369 'term : class1 : class2\n description', 370 ('<dl class="docutils">\n<dt>term<span class="classifier">class1</span>' 371 '<span class="classifier">class2</span></dt><dd>description</dd>\n</dl>'), 372 None, 373 ), 374 ( 375 # glossary (description list): multiple terms 376 'verify', 377 '.. glossary::\n\n term1\n term2\n description', 378 ('<dl class="glossary docutils">\n' 379 '<dt id="term-term1">term1<a class="headerlink" href="#term-term1"' 380 ' title="Permalink to this term">¶</a></dt>' 381 '<dt id="term-term2">term2<a class="headerlink" href="#term-term2"' 382 ' title="Permalink to this term">¶</a></dt>' 383 '<dd>description</dd>\n</dl>'), 384 None, 385 ), 386]) 387def test_inline(get_verifier, type, rst, html_expected, latex_expected): 388 verifier = get_verifier(type) 389 verifier(rst, html_expected, latex_expected) 390 391 392@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [ 393 ( 394 'verify', 395 r'4 backslashes \\\\', 396 r'<p>4 backslashes \\</p>', 397 None, 398 ), 399]) 400@pytest.mark.skipif(docutils.__version_info__ < (0, 16), 401 reason='docutils-0.16 or above is required') 402def test_inline_docutils16(get_verifier, type, rst, html_expected, latex_expected): 403 verifier = get_verifier(type) 404 verifier(rst, html_expected, latex_expected) 405 406 407@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'}) 408@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [ 409 ( 410 # in verbatim code fragments 411 'verify', 412 '::\n\n @Γ\\∞${}', 413 None, 414 ('\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n' 415 '@Γ\\PYGZbs{}∞\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n' 416 '\\end{sphinxVerbatim}'), 417 ), 418]) 419def test_inline_for_unicode_latex_engine(get_verifier, type, rst, 420 html_expected, latex_expected): 421 verifier = get_verifier(type) 422 verifier(rst, html_expected, latex_expected) 423 424 425def test_samp_role(parse): 426 # no braces 427 text = ':samp:`a{b}c`' 428 doctree = parse(text) 429 assert_node(doctree[0], [nodes.paragraph, nodes.literal, ("a", 430 [nodes.emphasis, "b"], 431 "c")]) 432 # nested braces 433 text = ':samp:`a{{b}}c`' 434 doctree = parse(text) 435 assert_node(doctree[0], [nodes.paragraph, nodes.literal, ("a", 436 [nodes.emphasis, "{b"], 437 "}c")]) 438 439 # half-opened braces 440 text = ':samp:`a{bc`' 441 doctree = parse(text) 442 assert_node(doctree[0], [nodes.paragraph, nodes.literal, "a{bc"]) 443 444 # escaped braces 445 text = ':samp:`a\\\\{b}c`' 446 doctree = parse(text) 447 assert_node(doctree[0], [nodes.paragraph, nodes.literal, "a{b}c"]) 448 449 # no braces (whitespaces are keeped as is) 450 text = ':samp:`code sample`' 451 doctree = parse(text) 452 assert_node(doctree[0], [nodes.paragraph, nodes.literal, "code sample"]) 453 454 455def test_download_role(parse): 456 # implicit 457 text = ':download:`sphinx.rst`' 458 doctree = parse(text) 459 assert_node(doctree[0], [nodes.paragraph, addnodes.download_reference, 460 nodes.literal, "sphinx.rst"]) 461 assert_node(doctree[0][0], refdoc='dummy', refdomain='', reftype='download', 462 refexplicit=False, reftarget='sphinx.rst', refwarn=False) 463 assert_node(doctree[0][0][0], classes=['xref', 'download']) 464 465 # explicit 466 text = ':download:`reftitle <sphinx.rst>`' 467 doctree = parse(text) 468 assert_node(doctree[0], [nodes.paragraph, addnodes.download_reference, 469 nodes.literal, "reftitle"]) 470 assert_node(doctree[0][0], refdoc='dummy', refdomain='', reftype='download', 471 refexplicit=True, reftarget='sphinx.rst', refwarn=False) 472 assert_node(doctree[0][0][0], classes=['xref', 'download']) 473 474 475def test_XRefRole(inliner): 476 role = XRefRole() 477 478 # implicit 479 doctrees, errors = role('ref', 'rawtext', 'text', 5, inliner, {}, []) 480 assert len(doctrees) == 1 481 assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text']) 482 assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text', 483 refexplicit=False, refwarn=False) 484 assert errors == [] 485 486 # explicit 487 doctrees, errors = role('ref', 'rawtext', 'title <target>', 5, inliner, {}, []) 488 assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'title']) 489 assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='target', 490 refexplicit=True, refwarn=False) 491 492 # bang 493 doctrees, errors = role('ref', 'rawtext', '!title <target>', 5, inliner, {}, []) 494 assert_node(doctrees[0], [nodes.literal, 'title <target>']) 495 496 # refdomain 497 doctrees, errors = role('test:doc', 'rawtext', 'text', 5, inliner, {}, []) 498 assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text']) 499 assert_node(doctrees[0], refdoc='dummy', refdomain='test', reftype='doc', reftarget='text', 500 refexplicit=False, refwarn=False) 501 502 # fix_parens 503 role = XRefRole(fix_parens=True) 504 doctrees, errors = role('ref', 'rawtext', 'text()', 5, inliner, {}, []) 505 assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'text()']) 506 assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text', 507 refexplicit=False, refwarn=False) 508 509 # lowercase 510 role = XRefRole(lowercase=True) 511 doctrees, errors = role('ref', 'rawtext', 'TEXT', 5, inliner, {}, []) 512 assert_node(doctrees[0], [addnodes.pending_xref, nodes.literal, 'TEXT']) 513 assert_node(doctrees[0], refdoc='dummy', refdomain='', reftype='ref', reftarget='text', 514 refexplicit=False, refwarn=False) 515 516 517@pytest.mark.sphinx('dummy', testroot='prolog') 518def test_rst_prolog(app, status, warning): 519 app.builder.build_all() 520 rst = app.env.get_doctree('restructuredtext') 521 md = app.env.get_doctree('markdown') 522 523 # rst_prolog 524 assert_node(rst[0], nodes.paragraph) 525 assert_node(rst[0][0], nodes.emphasis) 526 assert_node(rst[0][0][0], nodes.Text) 527 assert rst[0][0][0] == 'Hello world' 528 529 # rst_epilog 530 assert_node(rst[-1], nodes.section) 531 assert_node(rst[-1][-1], nodes.paragraph) 532 assert_node(rst[-1][-1][0], nodes.emphasis) 533 assert_node(rst[-1][-1][0][0], nodes.Text) 534 assert rst[-1][-1][0][0] == 'Good-bye world' 535 536 # rst_prolog & rst_epilog on exlucding reST parser 537 assert not md.rawsource.startswith('*Hello world*.') 538 assert not md.rawsource.endswith('*Good-bye world*.\n') 539 540 541@pytest.mark.sphinx('dummy', testroot='keep_warnings') 542def test_keep_warnings_is_True(app, status, warning): 543 app.builder.build_all() 544 doctree = app.env.get_doctree('index') 545 assert_node(doctree[0], nodes.section) 546 assert len(doctree[0]) == 2 547 assert_node(doctree[0][1], nodes.system_message) 548 549 550@pytest.mark.sphinx('dummy', testroot='keep_warnings', 551 confoverrides={'keep_warnings': False}) 552def test_keep_warnings_is_False(app, status, warning): 553 app.builder.build_all() 554 doctree = app.env.get_doctree('index') 555 assert_node(doctree[0], nodes.section) 556 assert len(doctree[0]) == 1 557 558 559@pytest.mark.sphinx('dummy', testroot='refonly_bullet_list') 560def test_compact_refonly_bullet_list(app, status, warning): 561 app.builder.build_all() 562 doctree = app.env.get_doctree('index') 563 assert_node(doctree[0], nodes.section) 564 assert len(doctree[0]) == 5 565 566 assert doctree[0][1].astext() == 'List A:' 567 assert_node(doctree[0][2], nodes.bullet_list) 568 assert_node(doctree[0][2][0][0], addnodes.compact_paragraph) 569 assert doctree[0][2][0][0].astext() == 'genindex' 570 571 assert doctree[0][3].astext() == 'List B:' 572 assert_node(doctree[0][4], nodes.bullet_list) 573 assert_node(doctree[0][4][0][0], nodes.paragraph) 574 assert doctree[0][4][0][0].astext() == 'Hello' 575 576 577@pytest.mark.sphinx('dummy', testroot='default_role') 578def test_default_role1(app, status, warning): 579 app.builder.build_all() 580 581 # default-role: pep 582 doctree = app.env.get_doctree('index') 583 assert_node(doctree[0], nodes.section) 584 assert_node(doctree[0][1], nodes.paragraph) 585 assert_node(doctree[0][1][0], addnodes.index) 586 assert_node(doctree[0][1][1], nodes.target) 587 assert_node(doctree[0][1][2], nodes.reference, classes=["pep"]) 588 589 # no default-role 590 doctree = app.env.get_doctree('foo') 591 assert_node(doctree[0], nodes.section) 592 assert_node(doctree[0][1], nodes.paragraph) 593 assert_node(doctree[0][1][0], nodes.title_reference) 594 assert_node(doctree[0][1][1], nodes.Text) 595 596 597@pytest.mark.sphinx('dummy', testroot='default_role', 598 confoverrides={'default_role': 'guilabel'}) 599def test_default_role2(app, status, warning): 600 app.builder.build_all() 601 602 # default-role directive is stronger than configratuion 603 doctree = app.env.get_doctree('index') 604 assert_node(doctree[0], nodes.section) 605 assert_node(doctree[0][1], nodes.paragraph) 606 assert_node(doctree[0][1][0], addnodes.index) 607 assert_node(doctree[0][1][1], nodes.target) 608 assert_node(doctree[0][1][2], nodes.reference, classes=["pep"]) 609 610 # default_role changes the default behavior 611 doctree = app.env.get_doctree('foo') 612 assert_node(doctree[0], nodes.section) 613 assert_node(doctree[0][1], nodes.paragraph) 614 assert_node(doctree[0][1][0], nodes.inline, classes=["guilabel"]) 615 assert_node(doctree[0][1][1], nodes.Text) 616