Skip to content

Commit

Permalink
Changes after review
Browse files Browse the repository at this point in the history
- Also changed the role used to create links from `obj` to `class`.
  • Loading branch information
has2k1 committed Jan 8, 2018
1 parent 616c4b9 commit 0f5c33c
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 65 deletions.
15 changes: 12 additions & 3 deletions numpydoc/tests/test_xref.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
(2,) :xref_param_type:`~numpy.ndarray`
(...,M,N) array_like
(...,M,N) :term:`numpy:array_like`
(...,:xref_param_type:`M`,:xref_param_type:`N`) :term:`numpy:array_like`
(float, float), optional
(:xref_param_type:`float`, :xref_param_type:`float`), optional
Expand Down Expand Up @@ -72,17 +72,26 @@
{:xref_param_type:`False`, :xref_param_type:`True`, 'greedy', 'optimal'}
{{'begin', 1}, {'end', 0}}, {string, int}
{{'begin', 1}, {'end', 0}}, {string, :xref_param_type:`int`}
{{'begin', 1}, {'end', 0}}, {:xref_param_type:`str`, :xref_param_type:`int`}
callable f'(x,*args)
:xref_param_type:`callable` f'(x,*args)
:xref_param_type:`callable` f'(:xref_param_type:`x`,*args)
callable ``fhess(x, *args)``, optional
:xref_param_type:`callable` ``fhess(x, *args)``, optional
spmatrix (format: ``csr``, ``bsr``, ``dia`` or coo``)
:xref_param_type:`spmatrix` (format: ``csr``, ``bsr``, ``dia`` or coo``)
:ref:`strftime <strftime-strptime-behavior>`
:ref:`strftime <strftime-strptime-behavior>`
callable or :ref:`strftime <strftime-strptime-behavior>`
:xref_param_type:`callable` or :ref:`strftime <strftime-strptime-behavior>`
callable or :ref:`strftime behavior <strftime-strptime-behavior>`
:xref_param_type:`callable` or :ref:`strftime behavior <strftime-strptime-behavior>`
list(int)
:xref_param_type:`list`(:xref_param_type:`int`)
Expand Down
99 changes: 37 additions & 62 deletions numpydoc/xref.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import re
from sphinx import addnodes

# When sphinx (including the napoleon extension) parses the parameters
# section of a docstring, it converts the information into field lists.
# Some items in the list are for the parameter type. When the type fields
# are processed, the text is split and some tokens are turned into
# pending_xref nodes. These nodes are responsible for creating links.
#
# numpydoc does not create field lists, so the type information is
# not placed into fields that can be processed to make links. Instead,
# when parsing the type information we identify tokens that are link
# worthy and wrap them around a special role (xref_param_type_role).
# When the role is processed, we create pending_xref nodes which are
# later turned into links.


QUALIFIED_NAME_RE = re.compile(
# e.g int, numpy.array, ~numpy.array
# e.g int, numpy.array, ~numpy.array, .class_in_current_module
r'^'
r'[~\.]?'
r'[a-zA-Z_]\w*'
r'(?:\.[a-zA-Z_]\w*)*'
r'$'
)

CONTAINER_TYPE_RE = re.compile(
# e.g.
# - list[int]
# - dict(str, int)
# - dict[str, int]'
# - tuple(float, float)
# - dict[tuple(str, str), int]'
r'^'
r'(dict|list|tuple)'
r'[\[\(]'
r'(.+?(?:,\s*)?)+'
r'[\]\)]'
r'$'
)

CONTAINER_SPLIT_RE = re.compile(
# splits dict(str, int) into
# ['dict', '[', 'str', ', ', 'int', ']', '']
r'(\s*[\[\]\(\),]\s*)'
r'(\s*[\[\]\(\)\{\},]\s*)'
)

DOUBLE_QUOTE_SPLIT_RE = re.compile(
Expand All @@ -38,27 +36,34 @@
r'(``.+?``)'
)

IGNORE = {'of', ' of ', 'either', 'or', 'with', 'in', 'default'}
ROLE_SPLIT_RE = re.compile(
# splits to preserve ReST roles
r'(:\w+:`.+?(?<!\\)`)'
)

TEXT_SPLIT_RE = re.compile(
# splits on ' or ', ' | ', ', ' and ' '
r'(\s+or\s+|\s+\|\s+|,\s+|\s+)'
)

IGNORE = {'of', 'either', 'or', 'with', 'in', 'default', 'optional'}
CONTAINER_CHARS = set('[](){}')


def make_xref_param_type(param_type, xref_aliases):
"""
Enclose str in a role that creates a cross-reference
The role ``xref_param_type`` *may be* added to any token
that looks like type information and no other. The
function tries to be clever and catch type information
in different disguises.
Parameters
----------
param_type : str
text
xref_aliases : dict
Mapping used to resolve common abbreviations and aliases
to fully qualified names that can be cross-referenced.
Returns
-------
out : str
Expand All @@ -72,9 +77,6 @@ def make_xref_param_type(param_type, xref_aliases):
param_type not in IGNORE):
return ':xref_param_type:`%s`' % param_type

# Clever stuff below (except the last return)
# can be removed without affecting the basic functionality.

def _split_and_apply_re(s, pattern):
"""
Split string using the regex pattern,
Expand All @@ -94,16 +96,6 @@ def _split_and_apply_re(s, pattern):
return ''.join(results)
return s

def _split_and_apply_str(s, on):
"""
Split string s, at the substring on,
apply main function to the splits,
combine the results
"""
return on.join(
make_xref_param_type(s, xref_aliases)
for s in s.split(on))

# The cases are dealt with in an order the prevents
# conflict.
# Then the strategy is:
Expand All @@ -112,37 +104,20 @@ def _split_and_apply_str(s, on):
# - re-apply the function to the other parts
# - join the results with the pattern

# endswith ', optional'
if param_type.endswith(', optional'):
return '%s, optional' % make_xref_param_type(
param_type[:-10],
xref_aliases)

# Any sort of bracket '[](){}'
has_container = any(c in CONTAINER_CHARS for c in param_type)
if has_container:
# of the form 'dict[int, float]'
if CONTAINER_TYPE_RE.match(param_type):
return _split_and_apply_re(param_type, CONTAINER_SPLIT_RE)
else:
# of the form '[int, float]'
for start, end in ['[]', '()', '{}']:
if param_type.startswith(start) and param_type.endswith(end):
return '%s%s%s' % (
start,
make_xref_param_type(param_type[1:-1], xref_aliases),
end)

# May have an unsplittable literal
# Unsplittable literal
if '``' in param_type:
return _split_and_apply_re(param_type, DOUBLE_QUOTE_SPLIT_RE)

# Is splittable
for splitter in [' or ', ', ', ' ']:
if splitter in param_type:
return _split_and_apply_str(param_type, splitter)
# Any roles
if ':`' in param_type:
return _split_and_apply_re(param_type, ROLE_SPLIT_RE)

# Any sort of bracket '[](){}'
if any(c in CONTAINER_CHARS for c in param_type):
return _split_and_apply_re(param_type, CONTAINER_SPLIT_RE)

return param_type
# Common splitter tokens
return _split_and_apply_re(param_type, TEXT_SPLIT_RE)


def xref_param_type_role(role, rawtext, text, lineno, inliner,
Expand All @@ -164,6 +139,6 @@ def xref_param_type_role(role, rawtext, text, lineno, inliner,

contnode = addnodes.literal_emphasis(text, text)
node = addnodes.pending_xref('', refdomain='py', refexplicit=False,
reftype='obj', reftarget=target)
reftype='class', reftarget=target)
node += contnode
return [node], []

0 comments on commit 0f5c33c

Please sign in to comment.