mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-10-24 22:27:52 -05:00
Fix tests for Docutils 1.0 pre-releases
This commit is contained in:
1 parent
378a332a8f
commit
d19cbb5083
14 files changed
+163
-26
No files matched your search
@@ -58,6 +58,7 @@ from inspect import Parameter
|
||||
from types import ModuleType
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import docutils
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst.states import RSTStateMachine, state_classes
|
||||
@@ -398,8 +399,9 @@ class Autosummary(SphinxDirective):
|
||||
table.append(real_table)
|
||||
group = nodes.tgroup('', cols=2)
|
||||
real_table.append(group)
|
||||
group.append(nodes.colspec('', colwidth=10))
|
||||
group.append(nodes.colspec('', colwidth=90))
|
||||
for width in (10, 90):
|
||||
colwidth = str(width) if docutils.__version_info__[:2] >= (1, 0) else width
|
||||
group.append(nodes.colspec('', colwidth=colwidth))
|
||||
body = nodes.tbody('')
|
||||
group.append(body)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from sphinx.builders import Builder
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.nodes import _is_doctest_block
|
||||
from sphinx.util.osutil import relpath
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -661,4 +662,4 @@ def _condition_default(node: Node) -> bool:
|
||||
|
||||
|
||||
def _condition_with_doctest(node: Node) -> bool:
|
||||
return _condition_default(node) or isinstance(node, nodes.doctest_block)
|
||||
return _condition_default(node) or _is_doctest_block(node)
|
||||
+5
-2
@@ -5,9 +5,10 @@ from __future__ import annotations
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import docutils
|
||||
from docutils.io import FileInput
|
||||
from docutils.readers import standalone
|
||||
from docutils.transforms.references import DanglingReferences
|
||||
from docutils.transforms import references
|
||||
from docutils.writers import UnfilteredWriter
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx10Warning
|
||||
@@ -52,7 +53,9 @@ class SphinxBaseReader(standalone.Reader): # type: ignore[type-arg]
|
||||
transforms = super().get_transforms() + self.transforms
|
||||
|
||||
# remove transforms which is not needed for Sphinx
|
||||
unused = [DanglingReferences]
|
||||
unused: list[type[Transform]] = [references.DanglingReferences]
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
unused.append(references.ReportUnreferencedTargets) # type: ignore[attr-defined]
|
||||
for transform in unused:
|
||||
if transform in transforms:
|
||||
transforms.remove(transform)
|
||||
|
||||
@@ -6,6 +6,7 @@ import re
|
||||
import unicodedata
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import docutils
|
||||
from docutils import nodes
|
||||
from docutils.transforms import Transform, Transformer
|
||||
from docutils.transforms.parts import ContentsFilter
|
||||
@@ -19,7 +20,11 @@ from sphinx.deprecation import _deprecation_warning
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.i18n import format_date
|
||||
from sphinx.util.nodes import apply_source_workaround, is_smartquotable
|
||||
from sphinx.util.nodes import (
|
||||
_is_doctest_block,
|
||||
apply_source_workaround,
|
||||
is_smartquotable,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterable, Iterator
|
||||
@@ -183,7 +188,7 @@ class HandleCodeBlocks(SphinxTransform):
|
||||
def apply(self, **kwargs: Any) -> None:
|
||||
# move doctest blocks out of blockquotes
|
||||
for node in self.document.findall(nodes.block_quote):
|
||||
if all(isinstance(child, nodes.doctest_block) for child in node.children):
|
||||
if all(_is_doctest_block(child) for child in node.children):
|
||||
node.replace_self(node.children)
|
||||
# combine successive doctest blocks
|
||||
# for node in self.document.findall(nodes.doctest_block):
|
||||
@@ -278,6 +283,8 @@ class ExtraTranslatableNodes(SphinxTransform):
|
||||
return
|
||||
|
||||
def is_translatable_node(node: Node) -> TypeIs[nodes.Element]:
|
||||
if _is_doctest_block(node):
|
||||
return 'doctest-block' in targets
|
||||
return isinstance(node, target_nodes)
|
||||
|
||||
for node in self.document.findall(is_translatable_node):
|
||||
@@ -325,13 +332,18 @@ class UnreferencedFootnotesDetector(SphinxTransform):
|
||||
|
||||
|
||||
class DoctestTransform(SphinxTransform):
|
||||
"""Set "doctest" style to each doctest_block node"""
|
||||
"""Set the style and highlighting language of doctest blocks."""
|
||||
|
||||
default_priority = 500
|
||||
|
||||
def apply(self, **kwargs: Any) -> None:
|
||||
for node in self.document.findall(nodes.doctest_block):
|
||||
node['classes'].append('doctest')
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
# Docutils 1.0 represents doctests as literal blocks.
|
||||
for literal in self.document.findall(nodes.literal_block):
|
||||
if _is_doctest_block(literal):
|
||||
literal.setdefault('language', 'pycon')
|
||||
|
||||
|
||||
class FilterSystemMessages(SphinxTransform):
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import docutils
|
||||
from docutils.transforms.references import DanglingReferences
|
||||
|
||||
from sphinx.transforms import SphinxTransform
|
||||
@@ -42,7 +43,10 @@ class SphinxDomains(SphinxTransform):
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> ExtensionMetadata:
|
||||
app.add_transform(SphinxDanglingReferences)
|
||||
# Docutils 1.0 splits reference matching and reporting into separate
|
||||
# transforms, which are already provided by the standalone reader.
|
||||
if docutils.__version_info__[:2] < (1, 0):
|
||||
app.add_transform(SphinxDanglingReferences)
|
||||
app.add_transform(SphinxDomains)
|
||||
|
||||
return {
|
||||
|
||||
+13
-6
@@ -18,7 +18,7 @@ from docutils.io import FileOutput
|
||||
from docutils.parsers.rst import Directive, directives, roles
|
||||
from docutils.readers import standalone
|
||||
from docutils.statemachine import StateMachine
|
||||
from docutils.transforms.references import DanglingReferences
|
||||
from docutils.transforms import references
|
||||
from docutils.utils import Reporter, unescape
|
||||
|
||||
from sphinx.errors import SphinxError
|
||||
@@ -77,11 +77,18 @@ if TYPE_CHECKING:
|
||||
) -> tuple[RoleFunction | None, list[system_message]]: ...
|
||||
|
||||
|
||||
_READER_TRANSFORMS = [
|
||||
transform
|
||||
for transform in standalone.Reader().get_transforms()
|
||||
if transform is not DanglingReferences
|
||||
]
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
_READER_TRANSFORMS = [
|
||||
transform
|
||||
for transform in standalone.Reader().get_transforms()
|
||||
if transform is not references.ReportUnreferencedTargets # type: ignore[attr-defined]
|
||||
]
|
||||
else:
|
||||
_READER_TRANSFORMS = [
|
||||
transform
|
||||
for transform in standalone.Reader().get_transforms()
|
||||
if transform is not references.DanglingReferences
|
||||
]
|
||||
|
||||
|
||||
additional_nodes: set[type[Element]] = set()
|
||||
|
||||
@@ -8,6 +8,7 @@ import unicodedata
|
||||
from io import StringIO
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
import docutils
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
|
||||
@@ -287,6 +288,15 @@ IMAGE_TYPE_NODES = (
|
||||
) # fmt: skip
|
||||
|
||||
|
||||
def _is_doctest_block(node: Node) -> bool:
|
||||
if docutils.__version_info__[:2] >= (1, 0) and isinstance(
|
||||
node, nodes.literal_block
|
||||
):
|
||||
return 'doctest' in node['classes']
|
||||
# Also accept doctest_block nodes from third-party parsers on Docutils 1.0.
|
||||
return isinstance(node, nodes.doctest_block)
|
||||
|
||||
|
||||
def _clean_extracted_message(text: str) -> str:
|
||||
"""Remove trailing backslashes from each line of *text*."""
|
||||
if '\\' in text:
|
||||
|
||||
@@ -7,6 +7,7 @@ import re
|
||||
import urllib.parse
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import docutils
|
||||
from docutils import nodes
|
||||
from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator
|
||||
|
||||
@@ -672,6 +673,15 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
def depart_block_quote(self, node: Element) -> None:
|
||||
self.body.append('</div></blockquote>\n')
|
||||
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
|
||||
def visit_attribution(self, node: Element) -> None:
|
||||
# Keep attributions inside our blockquote wrapper. Docutils 1.0's
|
||||
# visitor closes the blockquote before emitting the attribution.
|
||||
prefix, suffix = self.attribution_formats[self.settings.attribution]
|
||||
self.context.append(suffix)
|
||||
self.body.append(self.starttag(node, 'p', prefix, CLASS='attribution'))
|
||||
|
||||
# overwritten
|
||||
def visit_literal(self, node: Element) -> None:
|
||||
if 'kbd' in node['classes']:
|
||||
|
||||
@@ -11,6 +11,7 @@ from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import docutils
|
||||
from docutils import nodes, writers
|
||||
from roman_numerals import RomanNumeral
|
||||
|
||||
@@ -1249,7 +1250,12 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
assert self.table is not None
|
||||
self.table.colcount += 1
|
||||
if 'colwidth' in node:
|
||||
self.table.colwidths.append(node['colwidth'])
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
# breaking change in Docutils 1.0
|
||||
# https://sourceforge.net/p/docutils/code/10397
|
||||
self.table.colwidths.append(int(node['colwidth']))
|
||||
else:
|
||||
self.table.colwidths.append(node['colwidth'])
|
||||
if 'stub' in node:
|
||||
self.table.stubs.append(self.table.colcount - 1)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import re
|
||||
import textwrap
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import docutils
|
||||
from docutils import nodes, writers
|
||||
|
||||
from sphinx import __display_version__, addnodes
|
||||
@@ -1041,7 +1042,12 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
pass
|
||||
|
||||
def visit_colspec(self, node: Element) -> None:
|
||||
self.colwidths.append(node['colwidth'])
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
# breaking change in Docutils 1.0
|
||||
# https://sourceforge.net/p/docutils/code/10397
|
||||
self.colwidths.append(int(node['colwidth']))
|
||||
else:
|
||||
self.colwidths.append(node['colwidth'])
|
||||
if len(self.colwidths) != self.n_cols:
|
||||
return
|
||||
self.body.append('\n\n@multitable ')
|
||||
|
||||
@@ -9,6 +9,7 @@ import textwrap
|
||||
from itertools import chain, groupby, pairwise
|
||||
from typing import TYPE_CHECKING, cast
|
||||
|
||||
import docutils
|
||||
from docutils import nodes, writers
|
||||
from docutils.utils import column_width
|
||||
|
||||
@@ -904,7 +905,12 @@ class TextTranslator(SphinxTranslator):
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_colspec(self, node: Element) -> None:
|
||||
self.table.colwidth.append(node['colwidth'])
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
# breaking change in Docutils 1.0
|
||||
# https://sourceforge.net/p/docutils/code/10397
|
||||
self.table.colwidth.append(int(node['colwidth']))
|
||||
else:
|
||||
self.table.colwidth.append(node['colwidth'])
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_tgroup(self, node: Element) -> None:
|
||||
|
||||
@@ -5,11 +5,17 @@ from __future__ import annotations
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import docutils
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
|
||||
from tests.test_builders.xpath_util import check_xpath
|
||||
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
_RFC_1_URL = 'https://datatracker.ietf.org/doc/html/rfc1/'
|
||||
else:
|
||||
_RFC_1_URL = 'https://datatracker.ietf.org/doc/html/rfc1.html'
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Iterable
|
||||
from pathlib import Path
|
||||
@@ -147,14 +153,12 @@ def tail_check(check: str) -> Callable[[Iterable[Element]], Literal[True]]:
|
||||
),
|
||||
(
|
||||
'markup.html',
|
||||
".//a[@href='https://datatracker.ietf.org/doc/html/rfc1.html']"
|
||||
"[@class='rfc reference external']/strong",
|
||||
f".//a[@href='{_RFC_1_URL}'][@class='rfc reference external']/strong",
|
||||
'RFC 1',
|
||||
),
|
||||
(
|
||||
'markup.html',
|
||||
".//a[@href='https://datatracker.ietf.org/doc/html/rfc1.html']"
|
||||
"[@class='rfc reference external']/strong",
|
||||
f".//a[@href='{_RFC_1_URL}'][@class='rfc reference external']/strong",
|
||||
'Request for Comments #1',
|
||||
),
|
||||
(
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import re
|
||||
from types import SimpleNamespace
|
||||
|
||||
import docutils
|
||||
import pytest
|
||||
from docutils import nodes, utils
|
||||
from docutils.parsers.rst import Parser as RstParser
|
||||
@@ -23,6 +24,11 @@ from sphinx.writers.latex import LaTeXTranslator, LaTeXWriter
|
||||
|
||||
from tests.utils import extract_node
|
||||
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
_RFC_2324_URL = 'https://datatracker.ietf.org/doc/html/rfc2324/'
|
||||
else:
|
||||
_RFC_2324_URL = 'https://datatracker.ietf.org/doc/html/rfc2324.html'
|
||||
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.environment import BuildEnvironment
|
||||
@@ -212,12 +218,12 @@ def rst_to_latex(rst: str, *, app: SphinxTestApp) -> str:
|
||||
':rfc:`2324`',
|
||||
(
|
||||
'<p><span class="target" id="index-0"></span><a class="rfc reference external" '
|
||||
'href="https://datatracker.ietf.org/doc/html/rfc2324.html"><strong>RFC 2324</strong></a></p>'
|
||||
f'href="{_RFC_2324_URL}"><strong>RFC 2324</strong></a></p>'
|
||||
),
|
||||
(
|
||||
'\\sphinxAtStartPar\n'
|
||||
'\\index{RFC@\\spxentry{RFC}!RFC 2324@\\spxentry{RFC 2324}}'
|
||||
'\\sphinxhref{https://datatracker.ietf.org/doc/html/rfc2324.html}'
|
||||
rf'\sphinxhref{{{_RFC_2324_URL}}}'
|
||||
'{\\sphinxstylestrong{RFC 2324}}'
|
||||
),
|
||||
),
|
||||
@@ -226,13 +232,13 @@ def rst_to_latex(rst: str, *, app: SphinxTestApp) -> str:
|
||||
':rfc:`2324#section-1`',
|
||||
(
|
||||
'<p><span class="target" id="index-0"></span><a class="rfc reference external" '
|
||||
'href="https://datatracker.ietf.org/doc/html/rfc2324.html#section-1">'
|
||||
f'href="{_RFC_2324_URL}#section-1">'
|
||||
'<strong>RFC 2324 Section 1</strong></a></p>'
|
||||
),
|
||||
(
|
||||
'\\sphinxAtStartPar\n'
|
||||
'\\index{RFC@\\spxentry{RFC}!RFC 2324 Section 1@\\spxentry{RFC 2324 Section 1}}'
|
||||
'\\sphinxhref{https://datatracker.ietf.org/doc/html/rfc2324.html\\#section-1}'
|
||||
rf'\sphinxhref{{{_RFC_2324_URL}\#section-1}}'
|
||||
'{\\sphinxstylestrong{RFC 2324 Section 1}}'
|
||||
),
|
||||
),
|
||||
@@ -462,6 +468,16 @@ def test_inline(
|
||||
assert latex_expected == latex_translated, f'from {rst!r}'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='_blank')
|
||||
def test_block_quote_attribution(app: SphinxTestApp) -> None:
|
||||
html = rst_to_html(' A quotation.\n\n -- The author\n', app=app)
|
||||
assert html == (
|
||||
'<blockquote>\n<div><p>A quotation.</p>\n'
|
||||
'<p class="attribution">—The author</p>\n'
|
||||
'</div></blockquote>'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('rst', 'html_expected', 'latex_expected'),
|
||||
[
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import docutils
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.testing import restructuredtext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.testing.util import SphinxTestApp
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='_blank')
|
||||
def test_indented_doctest(app: SphinxTestApp) -> None:
|
||||
doctree = restructuredtext.parse(app, ' >>> 1 + 1\n 2\n')
|
||||
|
||||
assert len(doctree) == 1
|
||||
node = doctree[0]
|
||||
if docutils.__version_info__[:2] >= (1, 0):
|
||||
assert isinstance(node, nodes.literal_block)
|
||||
assert node['language'] == 'pycon'
|
||||
else:
|
||||
assert isinstance(node, nodes.doctest_block)
|
||||
assert node.astext() == '>>> 1 + 1\n2'
|
||||
assert 'doctest' in node['classes']
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('target', 'translate_doctest', 'translate_literal'),
|
||||
[('doctest-block', True, False), ('literal-block', False, True)],
|
||||
)
|
||||
@pytest.mark.sphinx('dummy', testroot='_blank')
|
||||
def test_doctest_translation(
|
||||
app: SphinxTestApp,
|
||||
target: str,
|
||||
translate_doctest: bool,
|
||||
translate_literal: bool,
|
||||
) -> None:
|
||||
app.config.gettext_additional_targets = [target]
|
||||
doctree = restructuredtext.parse(
|
||||
app, '>>> 1 + 1\n2\n\n::\n\n an ordinary literal block\n'
|
||||
)
|
||||
|
||||
doctest, literal = doctree.children
|
||||
assert isinstance(doctest, nodes.Element)
|
||||
assert doctest.get('translatable', False) is translate_doctest
|
||||
assert isinstance(literal, nodes.Element)
|
||||
assert literal.get('translatable', False) is translate_literal
|
||||
Reference in new issue
Block a user