From f6f6eac20c05be50f463d48d3540774580baa4ce Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 6 Mar 2017 16:21:13 +0900 Subject: [PATCH] Add OnlyNodeTransform as a tranform --- sphinx/environment/__init__.py | 21 +++++++++++------- sphinx/environment/adapters/toctree.py | 17 ++++++++++++--- sphinx/transforms/__init__.py | 7 ++++++ sphinx/transforms/post_transforms.py | 30 ++++++++++++++++++++++++++ sphinx/util/nodes.py | 6 ++++++ 5 files changed, 70 insertions(+), 11 deletions(-) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 8b08e4915..c7c177411 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -35,7 +35,7 @@ from sphinx import addnodes from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput from sphinx.util import logging from sphinx.util import get_matching_docs, FilenameUniqDict, status_iterator -from sphinx.util.nodes import WarningStream, is_translatable, process_only_nodes +from sphinx.util.nodes import WarningStream, is_translatable from sphinx.util.osutil import SEP, ensuredir from sphinx.util.i18n import find_catalog_files from sphinx.util.console import bold # type: ignore @@ -46,7 +46,7 @@ from sphinx.util.websupport import is_commentable from sphinx.errors import SphinxError, ExtensionError from sphinx.transforms import SphinxTransformer from sphinx.versioning import add_uids, merge_doctrees -from sphinx.deprecation import RemovedInSphinx20Warning +from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.toctree import TocTree @@ -921,10 +921,18 @@ class BuildEnvironment(object): def resolve_references(self, doctree, fromdocname, builder): # type: (nodes.Node, unicode, Builder) -> None - # apply all post-transforms + warnings.warn('env.resolve_references() is deprecated. ' + 'Use apply_post_transforms() instead.', + RemovedInSphinx17Warning) + + self.apply_post_transforms(doctree, fromdocname) + + def apply_post_transforms(self, doctree, docname): + # type: (nodes.Node, unicode) -> None + """Apply all post-transforms.""" try: # set env.docname during applying post-transforms - self.temp_data['docname'] = fromdocname + self.temp_data['docname'] = docname transformer = SphinxTransformer(doctree) transformer.add_transforms(self.app.post_transforms) @@ -932,11 +940,8 @@ class BuildEnvironment(object): finally: self.temp_data.clear() - # remove only-nodes that do not belong to our builder - process_only_nodes(doctree, builder.tags) - # allow custom references to be resolved - builder.app.emit('doctree-resolved', doctree, fromdocname) + self.app.emit('doctree-resolved', doctree, docname) def _warn_missing_reference(self, refdoc, typ, target, node, domain): # type: (unicode, unicode, unicode, nodes.Node, Domain) -> None diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py index 458eefd77..ab36318dd 100644 --- a/sphinx/environment/adapters/toctree.py +++ b/sphinx/environment/adapters/toctree.py @@ -15,7 +15,7 @@ from docutils import nodes from sphinx import addnodes from sphinx.util import url_re, logging -from sphinx.util.nodes import clean_astext, process_only_nodes +from sphinx.util.nodes import clean_astext if False: # For type annotation @@ -158,7 +158,7 @@ class TocTree(object): maxdepth = self.env.metadata[ref].get('tocdepth', 0) if ref not in toctree_ancestors or (prune and maxdepth > 0): self._toctree_prune(toc, 2, maxdepth, collapse) - process_only_nodes(toc, builder.tags) + self.process_only_nodes(toc) if title and toc.children and len(toc.children) == 1: child = toc.children[0] for refnode in child.traverse(nodes.reference): @@ -298,7 +298,7 @@ class TocTree(object): # the document does not exist anymore: return a dummy node that # renders to nothing return nodes.paragraph() - process_only_nodes(toc, builder.tags) + self.process_only_nodes(toc) for node in toc.traverse(nodes.reference): node['refuri'] = node['anchorname'] or '#' return toc @@ -323,3 +323,14 @@ class TocTree(object): for toctree in toctrees[1:]: result.extend(toctree.children) return result + + def process_only_nodes(self, doctree): + # type: (nodes.Node) -> None + # Lazy loading + from sphinx.transforms import SphinxTransformer + from sphinx.transforms.post_transforms import OnlyNodeTransform + + transformer = SphinxTransformer(doctree) + transformer.set_environment(self.env) + transformer.add_transform(OnlyNodeTransform) + transformer.apply_transforms() diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index 7c35e78a1..b7306bb2d 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -76,6 +76,11 @@ class SphinxTransformer(Transformer): """ document = None # type: nodes.Node + env = None # type: BuildEnvironment + + def set_environment(self, env): + # type: (BuildEnvironment) -> None + self.env = env def apply_transforms(self): # type: () -> None @@ -85,6 +90,8 @@ class SphinxTransformer(Transformer): # wrap the target node by document node during transforming try: document = new_document('') + if self.env: + document.settings.env = self.env document += self.document self.document = document Transformer.apply_transforms(self) diff --git a/sphinx/transforms/post_transforms.py b/sphinx/transforms/post_transforms.py index 9177b0308..ed4276f5d 100644 --- a/sphinx/transforms/post_transforms.py +++ b/sphinx/transforms/post_transforms.py @@ -9,9 +9,12 @@ :license: BSD, see LICENSE for details. """ +from docutils import nodes + from sphinx import addnodes from sphinx.environment import NoUri from sphinx.transforms import SphinxTransform +from sphinx.util import logging if False: # For type annotation @@ -19,6 +22,9 @@ if False: from sphinx.application import Sphinx # NOQA +logger = logging.getLogger(__name__) + + class ReferencesResolver(SphinxTransform): """ Resolves cross-references on doctrees. @@ -63,9 +69,33 @@ class ReferencesResolver(SphinxTransform): node.replace_self(newnode or contnode) +class OnlyNodeTransform(SphinxTransform): + default_priority = 50 + + def apply(self): + # type: () -> 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 + # the only node, so we make sure docutils can transfer the id to + # something, even if it's just a comment and will lose the id anyway... + for node in self.document.traverse(addnodes.only): + try: + ret = self.app.builder.tags.eval_condition(node['expr']) + except Exception as err: + logger.warning('exception while evaluating only directive expression: %s', err, + location=node) + node.replace_self(node.children or nodes.comment()) + else: + if ret: + node.replace_self(node.children or nodes.comment()) + else: + node.replace_self(nodes.comment()) + + def setup(app): # type: (Sphinx) -> Dict[unicode, Any] app.add_post_transform(ReferencesResolver) + app.add_post_transform(OnlyNodeTransform) return { 'version': 'builtin', diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 99826f657..df9aa1cef 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -11,12 +11,14 @@ from __future__ import absolute_import import re +import warnings from six import text_type from docutils import nodes from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx17Warning from sphinx.locale import pairindextypes from sphinx.util import logging @@ -358,6 +360,10 @@ def process_only_nodes(doctree, tags): # result in a "Losing ids" exception if there is a target node before # the only node, so we make sure docutils can transfer the id to # something, even if it's just a comment and will lose the id anyway... + warnings.warn('process_only_nodes() is deprecated. ' + 'Use sphinx.environment.apply_post_transforms() instead.', + RemovedInSphinx17Warning) + for node in doctree.traverse(addnodes.only): try: ret = tags.eval_condition(node['expr'])