summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2019-03-17 18:27:00 +0900
committerGitHub <noreply@github.com>2019-03-17 18:27:00 +0900
commitb5959ca230cbd954f32ced01ef2164d37a5ef6eb (patch)
tree5b410a9b1ef2d898457f06a448a60336e3213ec2
parentfadab68ffc6a79de2d285eca930e58cddece4c45 (diff)
downloadsphinx-git-b5959ca230cbd954f32ced01ef2164d37a5ef6eb.tar.gz
Add SphinxPostTransform class (#6154)
* Add SphinxPostTransform * Apply SphinxPostTransform to latex transforms
-rw-r--r--CHANGES2
-rw-r--r--doc/extdev/deprecated.rst5
-rw-r--r--doc/extdev/utils.rst3
-rw-r--r--sphinx/builders/latex/__init__.py25
-rw-r--r--sphinx/builders/latex/transforms.py70
-rw-r--r--sphinx/transforms/post_transforms/__init__.py42
6 files changed, 97 insertions, 50 deletions
diff --git a/CHANGES b/CHANGES
index 4a6b59840..e0aa4532b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,7 @@ Incompatible changes
Deprecated
----------
+* ``sphinx.builders.latex.LaTeXBuilder.apply_transforms()``
* ``sphinx.environment.NoUri``
* ``sphinx.ext.autodoc.importer.MockFinder``
* ``sphinx.ext.autodoc.importer.MockLoader``
@@ -28,6 +29,7 @@ For more details, see :ref:`deprecation APIs list <dev-deprecated-apis>`.
Features added
--------------
+* Add a helper class ``sphinx.transforms.post_transforms.SphinxPostTransform``
* Add a helper method ``SphinxDirective.set_source_info()``
* #6180: Support ``--keep-going`` with BuildDoc setup command
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index e57140420..b28c70214 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -26,6 +26,11 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives
+ * - ``sphinx.builders.latex.LaTeXBuilder.apply_transforms()``
+ - 2.1
+ - 4.0
+ - N/A
+
* - ``sphinx.environment.NoUri``
- 2.1
- 4.0
diff --git a/doc/extdev/utils.rst b/doc/extdev/utils.rst
index 3aac51ed9..2a94a34bb 100644
--- a/doc/extdev/utils.rst
+++ b/doc/extdev/utils.rst
@@ -15,6 +15,9 @@ components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily.
.. autoclass:: sphinx.transforms.SphinxTransform
:members:
+.. autoclass:: sphinx.transforms.post_transforms.SphinxPostTransform
+ :members:
+
.. autoclass:: sphinx.util.docutils.SphinxDirective
:members:
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index c2218fd15..e6467601d 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -9,6 +9,7 @@
"""
import os
+import warnings
from os import path
from docutils.frontend import OptionParser
@@ -16,17 +17,12 @@ from docutils.frontend import OptionParser
import sphinx.builders.latex.nodes # NOQA # Workaround: import this before writer to avoid ImportError
from sphinx import package_dir, addnodes, highlighting
from sphinx.builders import Builder
-from sphinx.builders.latex.transforms import (
- BibliographyTransform, CitationReferenceTransform, MathReferenceTransform,
- FootnoteDocnameUpdater, LaTeXFootnoteTransform, LiteralBlockTransform,
- ShowUrlsTransform, DocumentTargetTransform, IndexInSectionTitleTransform,
-)
from sphinx.builders.latex.util import ExtBabel
from sphinx.config import ENUM
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import NoUri, SphinxError
from sphinx.locale import _, __
-from sphinx.transforms import SphinxTransformer
from sphinx.util import texescape, logging, progress_message, status_iterator
from sphinx.util.console import bold, darkgreen # type: ignore
from sphinx.util.docutils import SphinxFileOutput, new_document
@@ -264,7 +260,6 @@ class LaTeXBuilder(Builder):
docname, toctree_only,
appendices=((docclass != 'howto') and self.config.latex_appendices or []))
doctree['tocdepth'] = tocdepth
- self.apply_transforms(doctree)
self.post_process_images(doctree)
self.update_doc_context(title, author)
@@ -340,15 +335,8 @@ class LaTeXBuilder(Builder):
def apply_transforms(self, doctree):
# type: (nodes.document) -> None
- transformer = SphinxTransformer(doctree)
- transformer.set_environment(self.env)
- transformer.add_transforms([BibliographyTransform,
- ShowUrlsTransform,
- LaTeXFootnoteTransform,
- LiteralBlockTransform,
- DocumentTargetTransform,
- IndexInSectionTitleTransform])
- transformer.apply_transforms()
+ warnings.warn('LaTeXBuilder.apply_transforms() is deprecated.',
+ RemovedInSphinx40Warning)
def finish(self):
# type: () -> None
@@ -485,11 +473,10 @@ def default_latex_documents(config):
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
+ app.setup_extension('sphinx.builders.latex.transforms')
+
app.add_builder(LaTeXBuilder)
- app.add_post_transform(CitationReferenceTransform)
- app.add_post_transform(MathReferenceTransform)
app.connect('config-inited', validate_config_values)
- app.add_transform(FootnoteDocnameUpdater)
app.add_config_value('latex_engine', default_latex_engine, None,
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex'))
diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py
index 52d5bc9ea..746446fbc 100644
--- a/sphinx/builders/latex/transforms.py
+++ b/sphinx/builders/latex/transforms.py
@@ -17,11 +17,13 @@ from sphinx.builders.latex.nodes import (
captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
)
from sphinx.transforms import SphinxTransform
+from sphinx.transforms.post_transforms import SphinxPostTransform
from sphinx.util.nodes import NodeMatcher
if False:
# For type annotation
- from typing import Any, List, Set, Tuple # NOQA
+ from typing import Any, Dict, List, Set, Tuple # NOQA
+ from sphinx.application import Sphinx # NOQA
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
@@ -38,7 +40,7 @@ class FootnoteDocnameUpdater(SphinxTransform):
node['docname'] = self.env.docname
-class ShowUrlsTransform(SphinxTransform):
+class ShowUrlsTransform(SphinxPostTransform):
"""Expand references to inline text or footnotes.
For more information, see :confval:`latex_show_urls`.
@@ -46,11 +48,12 @@ class ShowUrlsTransform(SphinxTransform):
.. note:: This transform is used for integrated doctree
"""
default_priority = 400
+ builders = ('latex',)
# references are expanded to footnotes (or not)
expanded = False
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
try:
# replace id_prefix temporarily
@@ -177,7 +180,7 @@ class FootnoteCollector(nodes.NodeVisitor):
self.footnote_refs.append(node)
-class LaTeXFootnoteTransform(SphinxTransform):
+class LaTeXFootnoteTransform(SphinxPostTransform):
"""Convert footnote definitions and references to appropriate form to LaTeX.
* Replace footnotes on restricted zone (e.g. headings) by footnotemark node.
@@ -345,8 +348,9 @@ class LaTeXFootnoteTransform(SphinxTransform):
"""
default_priority = 600
+ builders = ('latex',)
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
footnotes = list(self.document.traverse(nodes.footnote))
for node in footnotes:
@@ -486,7 +490,7 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
return None
-class BibliographyTransform(SphinxTransform):
+class BibliographyTransform(SphinxPostTransform):
"""Gather bibliography entries to tail of document.
Before::
@@ -517,8 +521,9 @@ class BibliographyTransform(SphinxTransform):
...
"""
default_priority = 750
+ builders = ('latex',)
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
citations = thebibliography()
for node in self.document.traverse(nodes.citation):
@@ -529,19 +534,17 @@ class BibliographyTransform(SphinxTransform):
self.document += citations
-class CitationReferenceTransform(SphinxTransform):
+class CitationReferenceTransform(SphinxPostTransform):
"""Replace pending_xref nodes for citation by citation_reference.
To handle citation reference easily on LaTeX writer, this converts
pending_xref nodes to citation_reference.
"""
default_priority = 5 # before ReferencesResolver
+ builders = ('latex',)
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
- if self.app.builder.name != 'latex':
- return
-
matcher = NodeMatcher(addnodes.pending_xref, refdomain='std', reftype='citation')
citations = self.env.get_domain('std').data['citations']
for node in self.document.traverse(matcher): # type: addnodes.pending_xref
@@ -552,19 +555,17 @@ class CitationReferenceTransform(SphinxTransform):
node.replace_self(citation_ref)
-class MathReferenceTransform(SphinxTransform):
+class MathReferenceTransform(SphinxPostTransform):
"""Replace pending_xref nodes for math by math_reference.
To handle math reference easily on LaTeX writer, this converts pending_xref
nodes to math_reference.
"""
default_priority = 5 # before ReferencesResolver
+ builders = ('latex',)
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
- if self.app.builder.name != 'latex':
- return
-
equations = self.env.get_domain('math').data['objects']
for node in self.document.traverse(addnodes.pending_xref):
if node['refdomain'] == 'math' and node['reftype'] in ('eq', 'numref'):
@@ -574,30 +575,26 @@ class MathReferenceTransform(SphinxTransform):
node.replace_self(refnode)
-class LiteralBlockTransform(SphinxTransform):
+class LiteralBlockTransform(SphinxPostTransform):
"""Replace container nodes for literal_block by captioned_literal_block."""
default_priority = 400
+ builders = ('latex',)
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
- if self.app.builder.name != 'latex':
- return
-
matcher = NodeMatcher(nodes.container, literal_block=True)
for node in self.document.traverse(matcher): # type: nodes.container
newnode = captioned_literal_block('', *node.children, **node.attributes)
node.replace_self(newnode)
-class DocumentTargetTransform(SphinxTransform):
+class DocumentTargetTransform(SphinxPostTransform):
"""Add :doc label to the first section of each document."""
default_priority = 400
+ builders = ('latex',)
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
- if self.app.builder.name != 'latex':
- return
-
for node in self.document.traverse(addnodes.start_of_file):
section = node.next_node(nodes.section)
if section:
@@ -639,3 +636,22 @@ class IndexInSectionTitleTransform(SphinxTransform):
# move the index node next to the section title
node.remove(index)
node.parent.insert(i + 1, index)
+
+
+def setup(app):
+ # type: (Sphinx) -> Dict[str, Any]
+ app.add_transform(FootnoteDocnameUpdater)
+ app.add_post_transform(BibliographyTransform)
+ app.add_post_transform(CitationReferenceTransform)
+ app.add_post_transform(DocumentTargetTransform)
+ app.add_post_transform(IndexInSectionTitleTransform)
+ app.add_post_transform(LaTeXFootnoteTransform)
+ app.add_post_transform(LiteralBlockTransform)
+ app.add_post_transform(MathReferenceTransform)
+ app.add_post_transform(ShowUrlsTransform)
+
+ return {
+ 'version': 'builtin',
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }
diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py
index be144b793..8f64266d0 100644
--- a/sphinx/transforms/post_transforms/__init__.py
+++ b/sphinx/transforms/post_transforms/__init__.py
@@ -29,14 +29,48 @@ if False:
logger = logging.getLogger(__name__)
-class ReferencesResolver(SphinxTransform):
+class SphinxPostTransform(SphinxTransform):
+ """A base class of post-transforms.
+
+ Post transforms are invoked to modify the document to restructure it for outputting.
+ They do resolving references, convert images, special transformation for each output
+ formats and so on. This class helps to implement these post transforms.
+ """
+ builders = () # type: Tuple[str, ...]
+ formats = () # type: Tuple[str, ...]
+
+ def apply(self, **kwargs):
+ # type: (Any) -> None
+ if self.is_supported():
+ self.run(**kwargs)
+
+ def is_supported(self):
+ # type: () -> bool
+ """Check this transform working for current builder."""
+ if self.builders and self.app.builder.name not in self.builders:
+ return False
+ if self.formats and self.app.builder.format not in self.formats:
+ return False
+
+ return True
+
+ def run(self, **kwargs):
+ # type: (Any) -> None
+ """main method of post transforms.
+
+ Subclasses should override this method instead of ``apply()``.
+ """
+ raise NotImplementedError
+
+
+class ReferencesResolver(SphinxPostTransform):
"""
Resolves cross-references on doctrees.
"""
default_priority = 10
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
for node in self.document.traverse(addnodes.pending_xref):
contnode = cast(nodes.TextElement, node[0].deepcopy())
@@ -147,10 +181,10 @@ class ReferencesResolver(SphinxTransform):
location=node, type='ref', subtype=typ)
-class OnlyNodeTransform(SphinxTransform):
+class OnlyNodeTransform(SphinxPostTransform):
default_priority = 50
- def apply(self, **kwargs):
+ def run(self, **kwargs):
# type: (Any) -> None
# A comment on the comment() nodes being inserted: replacing by [] would
# result in a "Losing ids" exception if there is a target node before