Skip to content

Commit

Permalink
Fix #9240: Unknown node error for pending_xref_condition is raised
Browse files Browse the repository at this point in the history
Unknown node error for pending_xref_condition is raised if an extension
that does not support the node installs a missing-reference handler.
  • Loading branch information
tk0miya committed May 17, 2021
1 parent 630e5bd commit 2d3d668
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------
Expand Down
28 changes: 19 additions & 9 deletions sphinx/transforms/post_transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,10 +26,6 @@

logger = logging.getLogger(__name__)

if False:
# For type annotation
from docutils.nodes import Node


class SphinxPostTransform(SphinxTransform):
"""A base class of post-transforms.
Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -108,9 +109,9 @@ def run(self, **kwargs: Any) -> None:
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)
Expand Down Expand Up @@ -193,6 +194,15 @@ def warn_missing_reference(self, refdoc: str, typ: str, target: str,
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nitpicky = True
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
transforms-post_transforms-missing-reference
============================================

:class:`io.StringIO`

58 changes: 58 additions & 0 deletions tests/test_transforms_post_transforms.py
Original file line number Diff line number Diff line change
@@ -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 ('<p><code class="xref py py-class docutils literal notranslate"><span class="pre">'
'io.StringIO</span></code></p>' 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 '<p><span>missing-reference.StringIO</span></p>' 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 '<span class="n"><span class="pre">Age</span></span>' in content

0 comments on commit 2d3d668

Please sign in to comment.