File: C:/Users/fred/anaconda3/Lib/site-packages/numpydoc/xref.py
import re
# 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 :obj: role.
# Note: we never split on commas that are not followed by a space
# You risk creating bad rst markup if you do so.
QUALIFIED_NAME_RE = re.compile(
# 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_SPLIT_RE = re.compile(
# splits dict(str, int) into
# ['dict', '[', 'str', ', ', 'int', ']', '']
r"(\s*[\[\]\(\)\{\}]\s*|,\s+)"
)
CONTAINER_SPLIT_REJECT_RE = re.compile(
# Leads to bad markup e.g.
# {int}qualified_name
r"[\]\)\}]\w"
)
DOUBLE_QUOTE_SPLIT_RE = re.compile(
# splits 'callable ``f(x0, *args)`` or ``f(x0, y0, *args)``' into
# ['callable ', '``f(x0, *args)``', ' or ', '``f(x0, y0, *args)``', '']
r"(``.+?``)"
)
ROLE_SPLIT_RE = re.compile(
# splits to preserve ReST roles
r"(:\w+:`.+?(?<!\\)`)"
)
SINGLE_QUOTE_SPLIT_RE = re.compile(
# splits to preserve quoted expressions roles
r"(`.+?`)"
)
TEXT_SPLIT_RE = re.compile(
# splits on ' or ', ' | ', ', ' and ' '
r"(\s+or\s+|\s+\|\s+|,\s+|\s+)"
)
CONTAINER_CHARS = set("[](){}")
# Save people some time and add some common standard aliases
DEFAULT_LINKS = {
# Python
"None": ":data:`python:None`",
"bool": ":ref:`bool <python:bltin-boolean-values>`",
"boolean": ":ref:`bool <python:bltin-boolean-values>`",
"True": ":data:`python:True`",
"False": ":data:`python:False`",
"list": ":class:`python:list`",
"tuple": ":class:`python:tuple`",
"str": ":class:`python:str`",
"string": ":class:`python:str`",
"dict": ":class:`python:dict`",
"float": ":class:`python:float`",
"int": ":class:`python:int`",
"callable": ":func:`python:callable`",
"iterable": ":term:`python:iterable`",
"sequence": ":term:`python:sequence`",
"contextmanager": ":func:`python:contextlib.contextmanager`",
"namedtuple": ":func:`python:collections.namedtuple`",
"generator": ":term:`python:generator`",
# NumPy
"array": "numpy.ndarray",
"ndarray": "numpy.ndarray",
"np.ndarray": "numpy.ndarray",
"array-like": ":term:`numpy:array_like`",
"array_like": ":term:`numpy:array_like`",
"scalar": ":ref:`scalar <numpy:arrays.scalars>`",
"RandomState": "numpy.random.RandomState",
"np.random.RandomState": "numpy.random.RandomState",
"np.inf": ":data:`numpy.inf`",
"np.nan": ":data:`numpy.nan`",
"numpy": ":mod:`numpy`",
}
def make_xref(param_type, xref_aliases, xref_ignore):
"""Parse and apply appropriate sphinx role(s) to `param_type`.
The :obj: role is the default.
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.
xref_ignore : set or "all"
A set containing words not to cross-reference. Instead of a set, the
string 'all' can be given to ignore all unrecognized terms.
Unrecognized terms include those that are not in `xref_aliases` and
are not already wrapped in a reST role.
Returns
-------
out : str
Text with fully-qualified names and terms that may be wrapped in a
``:obj:`` role.
"""
ignore_set = xref_ignore
wrap_unknown = True
if isinstance(xref_ignore, str):
if xref_ignore.lower() == "all":
wrap_unknown = False
ignore_set = set()
else:
raise TypeError(f"xref_ignore must be a set or 'all', got {xref_ignore}")
if param_type in xref_aliases:
link, title = xref_aliases[param_type], param_type
param_type = link
else:
link = title = param_type
if QUALIFIED_NAME_RE.match(link) and link not in ignore_set:
if link != title:
return f":obj:`{title} <{link}>`"
if wrap_unknown:
return f":obj:`{link}`"
return link
def _split_and_apply_re(s, pattern):
"""
Split string using the regex pattern,
apply main function to the parts that do not match the pattern,
combine the results
"""
results = []
tokens = pattern.split(s)
n = len(tokens)
if n > 1:
for i, tok in enumerate(tokens):
if pattern.match(tok):
results.append(tok)
else:
res = make_xref(tok, xref_aliases, xref_ignore)
# Opening brackets immediately after a role is
# bad markup. Detect that and add backslash.
# :role:`type`( to :role:`type`\(
if res and res[-1] == "`" and i < n - 1:
next_char = tokens[i + 1][0]
if next_char in "([{":
res += "\\"
results.append(res)
return "".join(results)
return s
# The cases are dealt with in an order the prevents
# conflict.
# Then the strategy is:
# - Identify a pattern we are not interested in
# - split off the pattern
# - re-apply the function to the other parts
# - join the results with the pattern
# Unsplittable literal
if "``" in param_type:
return _split_and_apply_re(param_type, DOUBLE_QUOTE_SPLIT_RE)
# Any roles
if ":`" in param_type:
return _split_and_apply_re(param_type, ROLE_SPLIT_RE)
# Any quoted expressions
if "`" in param_type:
return _split_and_apply_re(param_type, SINGLE_QUOTE_SPLIT_RE)
# Any sort of bracket '[](){}'
if any(c in CONTAINER_CHARS for c in param_type):
if CONTAINER_SPLIT_REJECT_RE.search(param_type):
return param_type
return _split_and_apply_re(param_type, CONTAINER_SPLIT_RE)
# Common splitter tokens
return _split_and_apply_re(param_type, TEXT_SPLIT_RE)