Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Features added
* #8070: html search: Support searching for 2characters word
* #7830: Add debug logs for change detection of sources and templates
* #8201: Emit a warning if toctree contains duplicated entries
* #66: py domain: add :confval:`python_docstring_variable_xrefs` to control
automatic cross-references for ``:var:``, ``:ivar:`` and ``:cvar:`` fields

Bugs fixed
----------
Expand Down
18 changes: 18 additions & 0 deletions doc/usage/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,24 @@ General configuration
If the value is a fully-qualified name of a custom Pygments style class,
this is then used as custom style.

.. confval:: python_docstring_variable_xrefs

A boolean that decides whether variable names documented with the Python
domain docstring fields ``:var:``, ``:ivar:`` and ``:cvar:`` are turned into
cross-references. The default is ``True`` which preserves the historical
behaviour of linking these names to the closest matching Python object. If
you prefer them to render as plain literal text, set the value to
``False``::

python_docstring_variable_xrefs = False

Turning this off still allows the corresponding ``:vartype:`` entries to use
the ``class`` role so that type information remains linked. Explicit roles
(for example ``:py:attr:``) inside docstrings continue to work regardless of
the setting.

.. versionadded:: 4.0

.. confval:: add_function_parentheses

A boolean that decides whether parentheses are appended to function and
Expand Down
6 changes: 6 additions & 0 deletions doc/usage/extensions/autodoc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,

.. versionadded:: 3.5

* Variable docstring fields ``:var:``, ``:ivar:`` and ``:cvar:`` link to the
nearest matching Python object by default. Set
:confval:`python_docstring_variable_xrefs` to ``False`` if you would prefer
these names to render as literal text while still allowing ``:vartype:``
entries and explicit roles such as ``:py:attr:`` to resolve normally.

* Python "special" members (that is, those named like ``__special__``) will
be included if the ``special-members`` flag option is given::

Expand Down
12 changes: 12 additions & 0 deletions sphinx/domains/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,17 @@ class PyObject(ObjectDescription):

allow_nesting = False

def get_field_type_map(self) -> Dict[str, Tuple[Field, bool]]:
for field in self.doc_field_types:
if isinstance(field, TypedField) and field.name == 'variable':
if self.config.python_docstring_variable_xrefs:
field.rolename = 'obj'
else:
field.rolename = None
break

return super().get_field_type_map()

def get_signature_prefix(self, sig: str) -> str:
"""May return a prefix to put before the object name in the
signature.
Expand Down Expand Up @@ -1287,6 +1298,7 @@ def istyping(s: str) -> bool:
def setup(app: Sphinx) -> Dict[str, Any]:
app.setup_extension('sphinx.directives')

app.add_config_value('python_docstring_variable_xrefs', True, 'env')
app.add_domain(PythonDomain)
app.connect('object-description-transform', filter_meta_fields)
app.connect('missing-reference', builtin_resolver, priority=900)
Expand Down
4 changes: 4 additions & 0 deletions tests/roots/test-variable-xrefs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project = 'variable-xrefs'
extensions = []
master_doc = 'index'
html_theme = 'alabaster'
7 changes: 7 additions & 0 deletions tests/roots/test-variable-xrefs/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. py:function:: foo()


.. py:class:: Sample

:ivar foo: link me if enabled
:vartype foo: int
40 changes: 40 additions & 0 deletions tests/test_domain_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,46 @@ def find_obj(modname, prefix, obj_name, obj_type, searchmode=0):
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))])


@pytest.mark.sphinx('dummy', testroot='variable-xrefs')
def test_variable_docfield_uses_xref_by_default(app, status, warning):
app.builder.build_all()

doctree = app.env.get_doctree('index')
variable_field = next(iter(doctree.traverse(nodes.field)))
xrefs = list(variable_field.traverse(pending_xref))

assert len(xrefs) == 2
assert_node(xrefs[0], pending_xref, refdomain='py', reftype='obj', reftarget='foo')
assert_node(xrefs[1], pending_xref, refdomain='py', reftype='class', reftarget='int')


@pytest.mark.sphinx('dummy', testroot='variable-xrefs',
confoverrides={'python_docstring_variable_xrefs': False})
def test_variable_docfield_renders_literal_when_disabled(app, status, warning):
app.builder.build_all()

doctree = app.env.get_doctree('index')
variable_field = next(iter(doctree.traverse(nodes.field)))
xrefs = list(variable_field.traverse(pending_xref))

assert len(xrefs) == 1
assert_node(xrefs[0], pending_xref, refdomain='py', reftype='class', reftarget='int')

literal = variable_field.traverse(addnodes.literal_strong)[0]
assert literal.astext() == 'foo'
assert not isinstance(literal.parent, pending_xref)


@pytest.mark.sphinx('html', testroot='variable-xrefs',
confoverrides={'python_docstring_variable_xrefs': False})
def test_variable_docfield_html_has_no_links_when_disabled(app, status, warning):
app.builder.build_all()

html = (app.outdir / 'index.html').read_text()
assert '<dd class="field-odd"><p><strong>foo</strong>' in html
assert '<dd class="field-odd"><p><a class="reference internal" href="#foo"' not in html


def test_get_full_qualified_name():
env = Mock(domaindata={})
domain = PythonDomain(env)
Expand Down