diff --git a/CHANGES b/CHANGES index 67ef71dd7..143e236a4 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,8 @@ Incompatible changes * #9222: Update Underscore.js to 1.13.1 * #9217: manpage: Stop creating a section directory on build manpage by default (see :confval:`man_make_section_directory`) +* #9240: Unknown node error for pending_xref_condition is raised if an extension + that does not support the node installs a missing-reference handler Deprecated ---------- diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index e2899d994..dde06fae1 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -8,10 +8,10 @@ :license: BSD, see LICENSE for details. """ -from typing import Any, Dict, List, Optional, Tuple, Type, cast +from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, cast from docutils import nodes -from docutils.nodes import Element +from docutils.nodes import Element, Node from sphinx import addnodes from sphinx.addnodes import pending_xref @@ -26,10 +26,6 @@ from sphinx.util.nodes import find_pending_xref_condition, process_only_nodes logger = logging.getLogger(__name__) -if False: - # For type annotation - from docutils.nodes import Node - class SphinxPostTransform(SphinxTransform): """A base class of post-transforms. @@ -71,7 +67,12 @@ class ReferencesResolver(SphinxPostTransform): def run(self, **kwargs: Any) -> None: for node in self.document.traverse(addnodes.pending_xref): - contnode = cast(nodes.TextElement, node[0].deepcopy()) + content = self.find_pending_xref_condition(node, ("resolved", "*")) + if content: + contnode = cast(Element, content[0].deepcopy()) + else: + contnode = cast(Element, node[0].deepcopy()) + newnode = None typ = node['reftype'] @@ -108,9 +109,9 @@ class ReferencesResolver(SphinxPostTransform): else: newnodes = [contnode] if newnode is None and isinstance(node[0], addnodes.pending_xref_condition): - matched = find_pending_xref_condition(node, "*") + matched = self.find_pending_xref_condition(node, ("*",)) if matched: - newnodes = matched.children + newnodes = matched else: logger.warning(__('Could not determine the fallback text for the ' 'cross-reference. Might be a bug.'), location=node) @@ -193,6 +194,15 @@ class ReferencesResolver(SphinxPostTransform): msg = __('%r reference target not found: %s') % (typ, target) logger.warning(msg, location=node, type='ref', subtype=typ) + def find_pending_xref_condition(self, node: pending_xref, conditions: Sequence[str] + ) -> Optional[List[Node]]: + for condition in conditions: + matched = find_pending_xref_condition(node, condition) + if matched: + return matched.children + else: + return None + class OnlyNodeTransform(SphinxPostTransform): default_priority = 50 diff --git a/tests/roots/test-transforms-post_transforms-missing-reference/conf.py b/tests/roots/test-transforms-post_transforms-missing-reference/conf.py new file mode 100644 index 000000000..2db221cc6 --- /dev/null +++ b/tests/roots/test-transforms-post_transforms-missing-reference/conf.py @@ -0,0 +1 @@ +nitpicky = True diff --git a/tests/roots/test-transforms-post_transforms-missing-reference/index.rst b/tests/roots/test-transforms-post_transforms-missing-reference/index.rst new file mode 100644 index 000000000..7180978b2 --- /dev/null +++ b/tests/roots/test-transforms-post_transforms-missing-reference/index.rst @@ -0,0 +1,5 @@ +transforms-post_transforms-missing-reference +============================================ + +:class:`io.StringIO` + diff --git a/tests/test_transforms_post_transforms.py b/tests/test_transforms_post_transforms.py new file mode 100644 index 000000000..26af55031 --- /dev/null +++ b/tests/test_transforms_post_transforms.py @@ -0,0 +1,58 @@ +""" + test_transforms_post_transforms + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Tests the post_transforms + + :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import pytest +from docutils import nodes + + +@pytest.mark.sphinx('html', testroot='transforms-post_transforms-missing-reference') +def test_nitpicky_warning(app, status, warning): + app.build() + assert ('index.rst:4: WARNING: py:class reference target ' + 'not found: io.StringIO' in warning.getvalue()) + + content = (app.outdir / 'index.html').read_text() + assert ('

' + 'io.StringIO

' in content) + + +@pytest.mark.sphinx('html', testroot='transforms-post_transforms-missing-reference', + freshenv=True) +def test_missing_reference(app, status, warning): + def missing_reference(app, env, node, contnode): + assert app is app + assert env is app.env + assert node['reftarget'] == 'io.StringIO' + assert contnode.astext() == 'io.StringIO' + + return nodes.inline('', 'missing-reference.StringIO') + + warning.truncate(0) + app.connect('missing-reference', missing_reference) + app.build() + assert warning.getvalue() == '' + + content = (app.outdir / 'index.html').read_text() + assert '

missing-reference.StringIO

' in content + + +@pytest.mark.sphinx('html', testroot='domain-py-python_use_unqualified_type_names', + freshenv=True) +def test_missing_reference_conditional_pending_xref(app, status, warning): + def missing_reference(app, env, node, contnode): + return contnode + + warning.truncate(0) + app.connect('missing-reference', missing_reference) + app.build() + assert warning.getvalue() == '' + + content = (app.outdir / 'index.html').read_text() + assert 'Age' in content