mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add SphinxPostTransform class (#6154)
* Add SphinxPostTransform * Apply SphinxPostTransform to latex transforms
This commit is contained in:
2
CHANGES
2
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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user