Skip to content

Commit

Permalink
Fix sphinx-doc#7199: py domain: Add a new confval: python_smart_refer…
Browse files Browse the repository at this point in the history
…ence

Add a new config variable: python_smart_reference.  If enabled, it goes
to suppress the module name of the python reference if it can be
resolved.
  • Loading branch information
tk0miya committed Nov 15, 2020
1 parent aa77cdd commit 00981ae
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Features added
* autodoc: Add ``Documenter.config`` as a shortcut to access the config object
* autodoc: Add Optional[t] to annotation of function and method if a default
value equal to None is set.
* #7199: py domain: Add :confval:`python_smart_reference` to suppress the module
name of the python reference if it can be resolved (experimental)
* #6914: Add a new event :event:`warn-missing-reference` to custom warning
messages when failed to resolve a cross-reference
* #6914: Emit a detailed warning when failed to resolve a ``:ref:`` reference
Expand Down
11 changes: 11 additions & 0 deletions doc/usage/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2615,6 +2615,17 @@ Options for the C++ domain

.. versionadded:: 1.5

Options for the Python domain
-----------------------------

.. confval:: python_smart_reference

If true, suppress the module name of the python reference if it can be
resolved. The default is ``False``.

.. versionadded:: 3.4

.. note:: This configuration is still in experimental

Example of configuration file
=============================
Expand Down
32 changes: 28 additions & 4 deletions sphinx/domains/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective
from sphinx.util.inspect import signature_from_str
from sphinx.util.nodes import make_id, make_refnode
from sphinx.util.nodes import find_pending_xref_condition, make_id, make_refnode
from sphinx.util.typing import TextlikeNode

if False:
Expand Down Expand Up @@ -91,7 +91,14 @@ def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xr
else:
kwargs = {}

return pending_xref('', nodes.Text(text),
if env.config.python_smart_reference:
shortname = text.split('.')[-1]
contnodes = [addnodes.pending_xref_condition('', shortname, condition='resolved'),
addnodes.pending_xref_condition('', text, condition='*')]
else:
contnodes = [nodes.Text(text)]

return pending_xref('', *contnodes,
refdomain='py', reftype=reftype, reftarget=text, **kwargs)


Expand Down Expand Up @@ -1313,7 +1320,15 @@ def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder
if obj[2] == 'module':
return self._make_module_refnode(builder, fromdocname, name, contnode)
else:
return make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name)
# determine the content of the reference by conditions
content = find_pending_xref_condition(node, 'resolved')
if content:
children = content.children
else:
# if not found, use contnode
children = contnode

return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)

def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element
Expand All @@ -1330,9 +1345,17 @@ def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Bui
self._make_module_refnode(builder, fromdocname,
name, contnode)))
else:
# determine the content of the reference by conditions
content = find_pending_xref_condition(node, 'resolved')
if content:
children = content.children
else:
# if not found, use contnode
children = contnode

results.append(('py:' + self.role_for_objtype(obj[2]),
make_refnode(builder, fromdocname, obj[0], obj[1],
contnode, name)))
children, name)))
return results

def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
Expand Down Expand Up @@ -1395,6 +1418,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.setup_extension('sphinx.directives')

app.add_domain(PythonDomain)
app.add_config_value('python_smart_reference', False, 'env')
app.connect('object-description-transform', filter_meta_fields)
app.connect('missing-reference', builtin_resolver, priority=900)

Expand Down
6 changes: 3 additions & 3 deletions sphinx/util/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import re
import unicodedata
import warnings
from typing import Any, Callable, Iterable, List, Set, Tuple, cast
from typing import Any, Callable, Iterable, List, Set, Tuple, Union, cast

from docutils import nodes
from docutils.nodes import Element, Node
Expand Down Expand Up @@ -549,7 +549,7 @@ def find_pending_xref_condition(node: addnodes.pending_xref, condition: str) ->


def make_refnode(builder: "Builder", fromdocname: str, todocname: str, targetid: str,
child: Node, title: str = None) -> nodes.reference:
child: Union[Node, List[Node]], title: str = None) -> nodes.reference:
"""Shortcut to create a reference node."""
node = nodes.reference('', '', internal=True)
if fromdocname == todocname and targetid:
Expand All @@ -562,7 +562,7 @@ def make_refnode(builder: "Builder", fromdocname: str, todocname: str, targetid:
node['refuri'] = builder.get_relative_uri(fromdocname, todocname)
if title:
node['reftitle'] = title
node.append(child)
node += child
return node


Expand Down
19 changes: 19 additions & 0 deletions tests/test_domain_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,25 @@ def test_noindexentry(app):
assert_node(doctree[2], addnodes.index, entries=[])


@pytest.mark.sphinx('html', testroot='domain-py-smart_reference')
def test_python_smart_reference(app, status, warning):
app.build()
content = (app.outdir / 'index.html').read_text()
assert ('<span class="n"><a class="reference internal" href="#foo.Name" title="foo.Name">'
'Name</a></span>' in content)
assert '<span class="n">foo.Age</span>' in content


@pytest.mark.sphinx('html', testroot='domain-py-smart_reference',
confoverrides={'python_smart_reference': False})
def test_python_smart_reference_disabled(app, status, warning):
app.build()
content = (app.outdir / 'index.html').read_text()
assert ('<span class="n"><a class="reference internal" href="#foo.Name" title="foo.Name">'
'foo.Name</a></span>' in content)
assert '<span class="n">foo.Age</span>' in content


@pytest.mark.sphinx('dummy', testroot='domain-py-xref-warning')
def test_warn_missing_reference(app, status, warning):
app.build()
Expand Down

0 comments on commit 00981ae

Please sign in to comment.