Skip to content

Commit 53cf0ff

Browse files
committed
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. Added two options: 1. numpydoc_xref_param_type 2. numpydoc_xref_aliases
1 parent d5cd548 commit 53cf0ff

File tree

6 files changed

+408
-1
lines changed

6 files changed

+408
-1
lines changed

doc/install.rst

+38
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,44 @@ numpydoc_use_blockqutoes : 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+
Together with the ``intersphinx`` extension, you can map to links
54+
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.array',
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+
4583
numpydoc_edit_link : bool
4684
.. deprecated:: edit your HTML template instead
4785

numpydoc/docscrape_sphinx.py

+11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from sphinx.jinja2glue import BuiltinTemplateLoader
1515

1616
from .docscrape import NumpyDocString, FunctionDoc, ClassDoc
17+
from .xref import make_xref_param_type
1718

1819
if sys.version_info[0] >= 3:
1920
sixu = lambda s: s
@@ -33,6 +34,8 @@ def load_config(self, config):
3334
self.use_plots = config.get('use_plots', False)
3435
self.use_blockquotes = config.get('use_blockquotes', False)
3536
self.class_members_toctree = config.get('class_members_toctree', True)
37+
self.xref_param_type = config.get('xref_param_type', False)
38+
self.xref_aliases = config.get('xref_aliases', dict())
3639
self.template = config.get('template', None)
3740
if self.template is None:
3841
template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')]
@@ -80,6 +83,10 @@ def _str_returns(self, name='Returns'):
8083
out += ['']
8184
for param, param_type, desc in self[name]:
8285
if param_type:
86+
if self.xref_param_type:
87+
param_type = make_xref_param_type(
88+
param_type,
89+
self.xref_aliases)
8390
out += self._str_indent([typed_fmt % (param.strip(),
8491
param_type)])
8592
else:
@@ -197,6 +204,10 @@ def _str_param_list(self, name, fake_autosummary=False):
197204
fake_autosummary)
198205

199206
if param_type:
207+
if self.xref_param_type:
208+
param_type = make_xref_param_type(
209+
param_type,
210+
self.xref_aliases)
200211
out += self._str_indent(['%s : %s' % (display_param,
201212
param_type)])
202213
else:

numpydoc/numpydoc.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
raise RuntimeError("Sphinx 1.0.1 or newer is required")
3030

3131
from .docscrape_sphinx import get_doc_object, SphinxDocString
32+
from .xref import xref_param_type_role
3233
from . import __version__
3334

3435
if sys.version_info[0] >= 3:
@@ -76,7 +77,10 @@ def mangle_docstrings(app, what, name, obj, options, lines):
7677
'show_class_members': app.config.numpydoc_show_class_members,
7778
'show_inherited_class_members':
7879
app.config.numpydoc_show_inherited_class_members,
79-
'class_members_toctree': app.config.numpydoc_class_members_toctree}
80+
'class_members_toctree': app.config.numpydoc_class_members_toctree,
81+
'xref_param_type': app.config.numpydoc_xref_param_type,
82+
'xref_aliases': app.config.numpydoc_xref_aliases,
83+
}
8084

8185
u_NL = sixu('\n')
8286
if what == 'module':
@@ -137,6 +141,7 @@ def setup(app, get_doc_object_=get_doc_object):
137141
global get_doc_object
138142
get_doc_object = get_doc_object_
139143

144+
app.add_role('xref_param_type', xref_param_type_role)
140145
app.connect('autodoc-process-docstring', mangle_docstrings)
141146
app.connect('autodoc-process-signature', mangle_signature)
142147
app.add_config_value('numpydoc_edit_link', None, False)
@@ -146,6 +151,8 @@ def setup(app, get_doc_object_=get_doc_object):
146151
app.add_config_value('numpydoc_show_inherited_class_members', True, True)
147152
app.add_config_value('numpydoc_class_members_toctree', True, True)
148153
app.add_config_value('numpydoc_citation_re', '[a-z0-9_.-]+', True)
154+
app.add_config_value('numpydoc_xref_param_type', True, True)
155+
app.add_config_value('numpydoc_xref_aliases', dict(), True)
149156

150157
# Extra mangling domains
151158
app.add_domain(NumpyPythonDomain)

numpydoc/tests/test_docscrape.py

+76
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,82 @@ def test_templated_sections():
11991199
""")
12001200

12011201

1202+
xref_doc_txt = """
1203+
Test xref in Parameters, Other Parameters and Returns
1204+
1205+
Parameters
1206+
----------
1207+
p1 : int
1208+
Integer value
1209+
1210+
p2 : float, optional
1211+
Integer value
1212+
1213+
Other Parameters
1214+
----------------
1215+
p3 : list[int]
1216+
List of integers
1217+
p4 : :class:`pandas.DataFrame`
1218+
A dataframe
1219+
p5 : sequence of int
1220+
A sequence
1221+
1222+
Returns
1223+
-------
1224+
out : array
1225+
Numerical return value
1226+
"""
1227+
1228+
1229+
xref_doc_txt_expected = """
1230+
Test xref in Parameters, Other Parameters and Returns
1231+
1232+
1233+
:Parameters:
1234+
1235+
p1 : :xref_param_type:`int`
1236+
Integer value
1237+
1238+
p2 : :xref_param_type:`float`, optional
1239+
Integer value
1240+
1241+
:Returns:
1242+
1243+
out : :xref_param_type:`~numpy.array`
1244+
Numerical return value
1245+
1246+
1247+
:Other Parameters:
1248+
1249+
p3 : :xref_param_type:`list`[:xref_param_type:`int`]
1250+
List of integers
1251+
1252+
p4 : :class:`pandas.DataFrame`
1253+
A dataframe
1254+
1255+
p5 : :term:`python:sequence` of :xref_param_type:`int`
1256+
A sequence
1257+
"""
1258+
1259+
1260+
def test_xref():
1261+
xref_aliases = {
1262+
'sequence': ':term:`python:sequence`',
1263+
'iterable': ':term:`python:iterable`',
1264+
'array': '~numpy.array',
1265+
}
1266+
1267+
doc = SphinxDocString(
1268+
xref_doc_txt,
1269+
config=dict(
1270+
xref_param_type=True,
1271+
xref_aliases=xref_aliases
1272+
)
1273+
)
1274+
1275+
line_by_line_compare(str(doc), xref_doc_txt_expected)
1276+
1277+
12021278
if __name__ == "__main__":
12031279
import nose
12041280
nose.run()

numpydoc/tests/test_xref.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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.array',
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:`~numpy.ndarray`
28+
29+
(...,M,N) array_like
30+
(...,M,N) :term:`numpy:array_like`
31+
32+
(float, float), optional
33+
(:xref_param_type:`float`, :xref_param_type:`float`), optional
34+
35+
1-D array or sequence
36+
1-D :xref_param_type:`~numpy.array` or :term:`python:sequence`
37+
38+
array of str or unicode-like
39+
:xref_param_type:`~numpy.array` of :xref_param_type:`str` or unicode-like
40+
41+
array_like of float
42+
:term:`numpy:array_like` of :xref_param_type:`float`
43+
44+
bool or callable
45+
:xref_param_type:`bool` or :xref_param_type:`callable`
46+
47+
int in [0, 255]
48+
:xref_param_type:`int` in [0, 255]
49+
50+
int or None, optional
51+
:xref_param_type:`int` or :xref_param_type:`None`, optional
52+
53+
list of str or array_like
54+
:xref_param_type:`list` of :xref_param_type:`str` or :term:`numpy:array_like`
55+
56+
sequence of array_like
57+
:term:`python:sequence` of :term:`numpy:array_like`
58+
59+
str or pathlib.Path
60+
:xref_param_type:`str` or :xref_param_type:`pathlib.Path`
61+
62+
{'', string}, optional
63+
{'', :xref_param_type:`str`}, optional
64+
65+
{'C', 'F', 'A', or 'K'}, optional
66+
{'C', 'F', 'A', or 'K'}, optional
67+
68+
{'linear', 'lower', 'higher', 'midpoint', 'nearest'}
69+
{'linear', 'lower', 'higher', 'midpoint', 'nearest'}
70+
71+
{False, True, 'greedy', 'optimal'}
72+
{:xref_param_type:`False`, :xref_param_type:`True`, 'greedy', 'optimal'}
73+
74+
{{'begin', 1}, {'end', 0}}, {string, int}
75+
{{'begin', 1}, {'end', 0}}, {string, :xref_param_type:`int`}
76+
77+
callable f'(x,*args)
78+
:xref_param_type:`callable` f'(x,*args)
79+
80+
callable ``fhess(x, *args)``, optional
81+
:xref_param_type:`callable` ``fhess(x, *args)``, optional
82+
83+
spmatrix (format: ``csr``, ``bsr``, ``dia`` or coo``)
84+
:xref_param_type:`spmatrix` (format: ``csr``, ``bsr``, ``dia`` or coo``)
85+
86+
list(int)
87+
:xref_param_type:`list`(:xref_param_type:`int`)
88+
89+
list[int]
90+
:xref_param_type:`list`[:xref_param_type:`int`]
91+
92+
dict(str, int)
93+
:xref_param_type:`dict`(:xref_param_type:`str`, :xref_param_type:`int`)
94+
95+
dict[str, int]
96+
:xref_param_type:`dict`[:xref_param_type:`str`, :xref_param_type:`int`]
97+
98+
tuple(float, float)
99+
:xref_param_type:`tuple`(:xref_param_type:`float`, :xref_param_type:`float`)
100+
101+
dict[tuple(str, str), int]
102+
:xref_param_type:`dict`[:xref_param_type:`tuple`(:xref_param_type:`str`, :xref_param_type:`str`), :xref_param_type:`int`]
103+
""" # noqa: E501
104+
105+
106+
def test_make_xref_param_type():
107+
for s in data.strip().split('\n\n'):
108+
param_type, expected_result = s.split('\n')
109+
result = make_xref_param_type(param_type, xref_aliases)
110+
assert_equal(result, expected_result)

0 commit comments

Comments
 (0)