Skip to content

Commit fe28261

Browse files
has2k1larsoner
authored andcommitted
Add cross-reference links to parameter types
Tokens of the type description that are determined to be "link-worthy" are enclosed in a new role called `xref_param_type`. This role when when processed adds a `pending_xref` node to the DOM. If these types cross-references are not resolved when the build ends, sphinx does not complain. This forgives errors made when deciding whether tokens are "link-worthy". And provided text from the type description is not lost in the processing, the only unwanted outcome is a type link (due to coincidence) when none was desired.
1 parent 40b3733 commit fe28261

File tree

6 files changed

+464
-2
lines changed

6 files changed

+464
-2
lines changed

doc/install.rst

+47
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,53 @@ numpydoc_use_blockquotes : bool
4242
Until version 0.8, parameter definitions were shown as blockquotes, rather
4343
than in a definition list. If your styling requires blockquotes, switch
4444
this config option to True. This option will be removed in version 0.10.
45+
numpydoc_xref_param_type : bool
46+
Whether to create cross-references for the parameter types in the
47+
``Parameters``, ``Other Parameters``, ``Returns`` and ``Yields``
48+
sections of the docstring.
49+
``True`` by default.
50+
numpydoc_xref_aliases : dict
51+
Mappings to fully qualified paths (or correct ReST references) for the
52+
aliases/shortcuts used when specifying the types of parameters.
53+
The keys should not have any spaces. Together with the ``intersphinx``
54+
extension, you can map to links in any documentation.
55+
The default is an empty ``dict``.
56+
57+
If you have the following ``intersphinx`` namespace configuration::
58+
59+
intersphinx_mapping = {
60+
'python': ('https://docs.python.org/3/', None),
61+
'numpy': ('https://docs.scipy.org/doc/numpy', None),
62+
}
63+
64+
A useful ``dict`` may look like the following::
65+
66+
numpydoc_xref_aliases = {
67+
# python
68+
'sequence': ':term:`python:sequence`',
69+
'iterable': ':term:`python:iterable`',
70+
'string': 'str',
71+
# numpy
72+
'array': 'numpy.ndarray',
73+
'dtype': 'numpy.dtype',
74+
'ndarray': 'numpy.ndarray',
75+
'matrix': 'numpy.matrix',
76+
'array-like': ':term:`numpy:array_like`',
77+
'array_like': ':term:`numpy:array_like`',
78+
}
79+
80+
This option depends on the ``numpydoc_xref_param_type`` option
81+
being ``True``.
82+
83+
numpydoc_xref_ignore : set
84+
Words not to cross-reference. Most likely, these are common words
85+
used in parameter type descriptions that may be confused for
86+
classes of the same name. For example::
87+
88+
numpydoc_xref_ignore = {'type', 'optional', 'default'}
89+
90+
The default is an empty set.
91+
4592
numpydoc_edit_link : bool
4693
.. deprecated:: edit your HTML template instead
4794

numpydoc/docscrape_sphinx.py

+15-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from sphinx.jinja2glue import BuiltinTemplateLoader
1818

1919
from .docscrape import NumpyDocString, FunctionDoc, ClassDoc
20+
from .xref import make_xref_param_type
2021

2122
if sys.version_info[0] >= 3:
2223
sixu = lambda s: s
@@ -36,6 +37,9 @@ def load_config(self, config):
3637
self.use_plots = config.get('use_plots', False)
3738
self.use_blockquotes = config.get('use_blockquotes', False)
3839
self.class_members_toctree = config.get('class_members_toctree', True)
40+
self.xref_param_type = config.get('xref_param_type', False)
41+
self.xref_aliases = config.get('xref_aliases', dict())
42+
self.xref_ignore = config.get('xref_ignore', set())
3943
self.template = config.get('template', None)
4044
if self.template is None:
4145
template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')]
@@ -79,7 +83,12 @@ def _str_returns(self, name='Returns'):
7983
out += ['']
8084
for param in self[name]:
8185
if param.type:
82-
out += self._str_indent([typed_fmt % (param.name.strip(),
86+
if self.xref_param_type:
87+
param.type = make_xref_param_type(
88+
param.type,
89+
self.xref_aliases,
90+
self.xref_ignore)
91+
out += self._str_indent([typed_fmt % (param.strip(),
8392
param.type)])
8493
else:
8594
out += self._str_indent([untyped_fmt % param.name.strip()])
@@ -211,6 +220,11 @@ def _str_param_list(self, name, fake_autosummary=False):
211220
fake_autosummary)
212221

213222
if param.type:
223+
if self.xref_param_type:
224+
param_type = make_xref_param_type(
225+
param_type,
226+
self.xref_aliases,
227+
self.xref_ignore)
214228
out += self._str_indent(['%s : %s' % (display_param,
215229
param.type)])
216230
else:

numpydoc/numpydoc.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
raise RuntimeError("Sphinx 1.0.1 or newer is required")
3737

3838
from .docscrape_sphinx import get_doc_object
39+
from .xref import xref_param_type_role
3940
from . import __version__
4041

4142
if sys.version_info[0] >= 3:
@@ -130,7 +131,11 @@ def mangle_docstrings(app, what, name, obj, options, lines):
130131
'show_class_members': app.config.numpydoc_show_class_members,
131132
'show_inherited_class_members':
132133
app.config.numpydoc_show_inherited_class_members,
133-
'class_members_toctree': app.config.numpydoc_class_members_toctree}
134+
'class_members_toctree': app.config.numpydoc_class_members_toctree,
135+
'xref_param_type': app.config.numpydoc_xref_param_type,
136+
'xref_aliases': app.config.numpydoc_xref_aliases,
137+
'xref_ignore': app.config.numpydoc_xref_ignore,
138+
}
134139

135140
u_NL = sixu('\n')
136141
if what == 'module':
@@ -193,6 +198,7 @@ def setup(app, get_doc_object_=get_doc_object):
193198

194199
app.setup_extension('sphinx.ext.autosummary')
195200

201+
app.add_role('xref_param_type', xref_param_type_role)
196202
app.connect('autodoc-process-docstring', mangle_docstrings)
197203
app.connect('autodoc-process-signature', mangle_signature)
198204
app.connect('doctree-read', relabel_references)
@@ -204,6 +210,9 @@ def setup(app, get_doc_object_=get_doc_object):
204210
app.add_config_value('numpydoc_show_inherited_class_members', True, True)
205211
app.add_config_value('numpydoc_class_members_toctree', True, True)
206212
app.add_config_value('numpydoc_citation_re', '[a-z0-9_.-]+', True)
213+
app.add_config_value('numpydoc_xref_param_type', True, True)
214+
app.add_config_value('numpydoc_xref_aliases', dict(), True)
215+
app.add_config_value('numpydoc_xref_ignore', set(), True)
207216

208217
# Extra mangling domains
209218
app.add_domain(NumpyPythonDomain)

numpydoc/tests/test_docscrape.py

+79
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,85 @@ def test_args_and_kwargs():
12681268
""")
12691269

12701270

1271+
xref_doc_txt = """
1272+
Test xref in Parameters, Other Parameters and Returns
1273+
1274+
Parameters
1275+
----------
1276+
p1 : int
1277+
Integer value
1278+
1279+
p2 : float, optional
1280+
Integer value
1281+
1282+
Other Parameters
1283+
----------------
1284+
p3 : list[int]
1285+
List of integers
1286+
p4 : :class:`pandas.DataFrame`
1287+
A dataframe
1288+
p5 : sequence of int
1289+
A sequence
1290+
1291+
Returns
1292+
-------
1293+
out : array
1294+
Numerical return value
1295+
"""
1296+
1297+
1298+
xref_doc_txt_expected = """
1299+
Test xref in Parameters, Other Parameters and Returns
1300+
1301+
1302+
:Parameters:
1303+
1304+
p1 : :xref_param_type:`int`
1305+
Integer value
1306+
1307+
p2 : :xref_param_type:`float`, optional
1308+
Integer value
1309+
1310+
:Returns:
1311+
1312+
out : :xref_param_type:`array <numpy.ndarray>`
1313+
Numerical return value
1314+
1315+
1316+
:Other Parameters:
1317+
1318+
p3 : :xref_param_type:`list`\[:xref_param_type:`int`]
1319+
List of integers
1320+
1321+
p4 : :class:`pandas.DataFrame`
1322+
A dataframe
1323+
1324+
p5 : :term:`python:sequence` of :xref_param_type:`int`
1325+
A sequence
1326+
"""
1327+
1328+
1329+
def test_xref():
1330+
xref_aliases = {
1331+
'sequence': ':term:`python:sequence`',
1332+
'iterable': ':term:`python:iterable`',
1333+
'array': 'numpy.ndarray',
1334+
}
1335+
1336+
xref_ignore = {'of', 'default', 'optional'}
1337+
1338+
doc = SphinxDocString(
1339+
xref_doc_txt,
1340+
config=dict(
1341+
xref_param_type=True,
1342+
xref_aliases=xref_aliases,
1343+
xref_ignore=xref_ignore
1344+
)
1345+
)
1346+
1347+
line_by_line_compare(str(doc), xref_doc_txt_expected)
1348+
1349+
12711350
if __name__ == "__main__":
12721351
import pytest
12731352
pytest.main()

numpydoc/tests/test_xref.py

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# -*- encoding:utf-8 -*-
2+
from __future__ import division, absolute_import, print_function
3+
4+
from nose.tools import assert_equal
5+
from numpydoc.xref import make_xref_param_type
6+
7+
xref_aliases = {
8+
# python
9+
'sequence': ':term:`python:sequence`',
10+
'iterable': ':term:`python:iterable`',
11+
'string': 'str',
12+
# numpy
13+
'array': 'numpy.ndarray',
14+
'dtype': 'numpy.dtype',
15+
'ndarray': 'numpy.ndarray',
16+
'matrix': 'numpy.matrix',
17+
'array-like': ':term:`numpy:array_like`',
18+
'array_like': ':term:`numpy:array_like`',
19+
}
20+
21+
# Comes mainly from numpy
22+
data = """
23+
(...) array_like, float, optional
24+
(...) :term:`numpy:array_like`, :xref_param_type:`float`, optional
25+
26+
(2,) ndarray
27+
(2,) :xref_param_type:`ndarray <numpy.ndarray>`
28+
29+
(...,M,N) array_like
30+
(...,M,N) :term:`numpy:array_like`
31+
32+
(..., M, N) array_like
33+
(..., :xref_param_type:`M`, :xref_param_type:`N`) :term:`numpy:array_like`
34+
35+
(float, float), optional
36+
(:xref_param_type:`float`, :xref_param_type:`float`), optional
37+
38+
1-D array or sequence
39+
1-D :xref_param_type:`array <numpy.ndarray>` or :term:`python:sequence`
40+
41+
array of str or unicode-like
42+
:xref_param_type:`array <numpy.ndarray>` of :xref_param_type:`str` or unicode-like
43+
44+
array_like of float
45+
:term:`numpy:array_like` of :xref_param_type:`float`
46+
47+
bool or callable
48+
:xref_param_type:`bool` or :xref_param_type:`callable`
49+
50+
int in [0, 255]
51+
:xref_param_type:`int` in [0, 255]
52+
53+
int or None, optional
54+
:xref_param_type:`int` or :xref_param_type:`None`, optional
55+
56+
list of str or array_like
57+
:xref_param_type:`list` of :xref_param_type:`str` or :term:`numpy:array_like`
58+
59+
sequence of array_like
60+
:term:`python:sequence` of :term:`numpy:array_like`
61+
62+
str or pathlib.Path
63+
:xref_param_type:`str` or :xref_param_type:`pathlib.Path`
64+
65+
{'', string}, optional
66+
{'', :xref_param_type:`string <str>`}, optional
67+
68+
{'C', 'F', 'A', or 'K'}, optional
69+
{'C', 'F', 'A', or 'K'}, optional
70+
71+
{'linear', 'lower', 'higher', 'midpoint', 'nearest'}
72+
{'linear', 'lower', 'higher', 'midpoint', 'nearest'}
73+
74+
{False, True, 'greedy', 'optimal'}
75+
{:xref_param_type:`False`, :xref_param_type:`True`, 'greedy', 'optimal'}
76+
77+
{{'begin', 1}, {'end', 0}}, {string, int}
78+
{{'begin', 1}, {'end', 0}}, {:xref_param_type:`string <str>`, :xref_param_type:`int`}
79+
80+
callable f'(x,*args)
81+
:xref_param_type:`callable` f'(x,*args)
82+
83+
callable ``fhess(x, *args)``, optional
84+
:xref_param_type:`callable` ``fhess(x, *args)``, optional
85+
86+
spmatrix (format: ``csr``, ``bsr``, ``dia`` or coo``)
87+
:xref_param_type:`spmatrix` (format: ``csr``, ``bsr``, ``dia`` or coo``)
88+
89+
:ref:`strftime <strftime-strptime-behavior>`
90+
:ref:`strftime <strftime-strptime-behavior>`
91+
92+
callable or :ref:`strftime <strftime-strptime-behavior>`
93+
:xref_param_type:`callable` or :ref:`strftime <strftime-strptime-behavior>`
94+
95+
callable or :ref:`strftime behavior <strftime-strptime-behavior>`
96+
:xref_param_type:`callable` or :ref:`strftime behavior <strftime-strptime-behavior>`
97+
98+
list(int)
99+
:xref_param_type:`list`\(:xref_param_type:`int`)
100+
101+
list[int]
102+
:xref_param_type:`list`\[:xref_param_type:`int`]
103+
104+
dict(str, int)
105+
:xref_param_type:`dict`\(:xref_param_type:`str`, :xref_param_type:`int`)
106+
107+
dict[str, int]
108+
:xref_param_type:`dict`\[:xref_param_type:`str`, :xref_param_type:`int`]
109+
110+
tuple(float, float)
111+
:xref_param_type:`tuple`\(:xref_param_type:`float`, :xref_param_type:`float`)
112+
113+
dict[tuple(str, str), int]
114+
:xref_param_type:`dict`\[:xref_param_type:`tuple`\(:xref_param_type:`str`, :xref_param_type:`str`), :xref_param_type:`int`]
115+
""" # noqa: E501
116+
117+
xref_ignore = {'or', 'in', 'of', 'default', 'optional'}
118+
119+
120+
def test_make_xref_param_type():
121+
for s in data.strip().split('\n\n'):
122+
param_type, expected_result = s.split('\n')
123+
result = make_xref_param_type(
124+
param_type,
125+
xref_aliases,
126+
xref_ignore
127+
)
128+
assert_equal(result, expected_result)

0 commit comments

Comments
 (0)