diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2019-03-17 18:27:00 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-17 18:27:00 +0900 |
commit | b5959ca230cbd954f32ced01ef2164d37a5ef6eb (patch) | |
tree | 5b410a9b1ef2d898457f06a448a60336e3213ec2 | |
parent | fadab68ffc6a79de2d285eca930e58cddece4c45 (diff) | |
download | sphinx-git-b5959ca230cbd954f32ced01ef2164d37a5ef6eb.tar.gz |
Add SphinxPostTransform class (#6154)
* Add SphinxPostTransform
* Apply SphinxPostTransform to latex transforms
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | doc/extdev/deprecated.rst | 5 | ||||
-rw-r--r-- | doc/extdev/utils.rst | 3 | ||||
-rw-r--r-- | sphinx/builders/latex/__init__.py | 25 | ||||
-rw-r--r-- | sphinx/builders/latex/transforms.py | 70 | ||||
-rw-r--r-- | sphinx/transforms/post_transforms/__init__.py | 42 |
6 files changed, 97 insertions, 50 deletions
@@ -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 |