mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Enable automatic formatting for `sphinx/ext/napoleon/
` (#12972)
This commit is contained in:
parent
a6e449094a
commit
f6590c5a33
@ -470,8 +470,6 @@ exclude = [
|
|||||||
"sphinx/ext/inheritance_diagram.py",
|
"sphinx/ext/inheritance_diagram.py",
|
||||||
"sphinx/ext/linkcode.py",
|
"sphinx/ext/linkcode.py",
|
||||||
"sphinx/ext/mathjax.py",
|
"sphinx/ext/mathjax.py",
|
||||||
"sphinx/ext/napoleon/__init__.py",
|
|
||||||
"sphinx/ext/napoleon/docstring.py",
|
|
||||||
"sphinx/ext/todo.py",
|
"sphinx/ext/todo.py",
|
||||||
"sphinx/ext/viewcode.py",
|
"sphinx/ext/viewcode.py",
|
||||||
"sphinx/registry.py",
|
"sphinx/registry.py",
|
||||||
|
@ -333,19 +333,26 @@ def setup(app: Sphinx) -> ExtensionMetadata:
|
|||||||
def _patch_python_domain() -> None:
|
def _patch_python_domain() -> None:
|
||||||
from sphinx.domains.python._object import PyObject, PyTypedField
|
from sphinx.domains.python._object import PyObject, PyTypedField
|
||||||
from sphinx.locale import _
|
from sphinx.locale import _
|
||||||
|
|
||||||
for doc_field in PyObject.doc_field_types:
|
for doc_field in PyObject.doc_field_types:
|
||||||
if doc_field.name == 'parameter':
|
if doc_field.name == 'parameter':
|
||||||
doc_field.names = ('param', 'parameter', 'arg', 'argument')
|
doc_field.names = ('param', 'parameter', 'arg', 'argument')
|
||||||
break
|
break
|
||||||
PyObject.doc_field_types.append(
|
PyObject.doc_field_types.append(
|
||||||
PyTypedField('keyword', label=_('Keyword Arguments'),
|
PyTypedField(
|
||||||
names=('keyword', 'kwarg', 'kwparam'),
|
'keyword',
|
||||||
typerolename='class', typenames=('paramtype', 'kwtype'),
|
label=_('Keyword Arguments'),
|
||||||
can_collapse=True))
|
names=('keyword', 'kwarg', 'kwparam'),
|
||||||
|
typerolename='class',
|
||||||
|
typenames=('paramtype', 'kwtype'),
|
||||||
|
can_collapse=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
|
def _process_docstring(
|
||||||
options: Any, lines: list[str]) -> None:
|
app: Sphinx, what: str, name: str, obj: Any, options: Any, lines: list[str]
|
||||||
|
) -> None:
|
||||||
"""Process the docstring for a given python object.
|
"""Process the docstring for a given python object.
|
||||||
|
|
||||||
Called when autodoc has read and processed a docstring. `lines` is a list
|
Called when autodoc has read and processed a docstring. `lines` is a list
|
||||||
@ -384,18 +391,21 @@ def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
|
|||||||
result_lines = lines
|
result_lines = lines
|
||||||
docstring: GoogleDocstring
|
docstring: GoogleDocstring
|
||||||
if app.config.napoleon_numpy_docstring:
|
if app.config.napoleon_numpy_docstring:
|
||||||
docstring = NumpyDocstring(result_lines, app.config, app, what, name,
|
docstring = NumpyDocstring(
|
||||||
obj, options)
|
result_lines, app.config, app, what, name, obj, options
|
||||||
|
)
|
||||||
result_lines = docstring.lines()
|
result_lines = docstring.lines()
|
||||||
if app.config.napoleon_google_docstring:
|
if app.config.napoleon_google_docstring:
|
||||||
docstring = GoogleDocstring(result_lines, app.config, app, what, name,
|
docstring = GoogleDocstring(
|
||||||
obj, options)
|
result_lines, app.config, app, what, name, obj, options
|
||||||
|
)
|
||||||
result_lines = docstring.lines()
|
result_lines = docstring.lines()
|
||||||
lines[:] = result_lines.copy()
|
lines[:] = result_lines.copy()
|
||||||
|
|
||||||
|
|
||||||
def _skip_member(app: Sphinx, what: str, name: str, obj: Any,
|
def _skip_member(
|
||||||
skip: bool, options: Any) -> bool | None:
|
app: Sphinx, what: str, name: str, obj: Any, skip: bool, options: Any
|
||||||
|
) -> bool | None:
|
||||||
"""Determine if private and special class members are included in docs.
|
"""Determine if private and special class members are included in docs.
|
||||||
|
|
||||||
The following settings in conf.py determine if private and special class
|
The following settings in conf.py determine if private and special class
|
||||||
@ -458,22 +468,25 @@ def _skip_member(app: Sphinx, what: str, name: str, obj: Any,
|
|||||||
except Exception:
|
except Exception:
|
||||||
cls_is_owner = False
|
cls_is_owner = False
|
||||||
else:
|
else:
|
||||||
cls_is_owner = (cls and hasattr(cls, name) and # type: ignore[assignment]
|
cls_is_owner = (
|
||||||
name in cls.__dict__)
|
cls # type: ignore[assignment]
|
||||||
|
and hasattr(cls, name)
|
||||||
|
and name in cls.__dict__
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
cls_is_owner = False
|
cls_is_owner = False
|
||||||
|
|
||||||
if what == 'module' or cls_is_owner:
|
if what == 'module' or cls_is_owner:
|
||||||
is_init = (name == '__init__')
|
is_init = name == '__init__'
|
||||||
is_special = (not is_init and name.startswith('__') and
|
is_special = not is_init and name.startswith('__') and name.endswith('__')
|
||||||
name.endswith('__'))
|
is_private = not is_init and not is_special and name.startswith('_')
|
||||||
is_private = (not is_init and not is_special and
|
|
||||||
name.startswith('_'))
|
|
||||||
inc_init = app.config.napoleon_include_init_with_doc
|
inc_init = app.config.napoleon_include_init_with_doc
|
||||||
inc_special = app.config.napoleon_include_special_with_doc
|
inc_special = app.config.napoleon_include_special_with_doc
|
||||||
inc_private = app.config.napoleon_include_private_with_doc
|
inc_private = app.config.napoleon_include_private_with_doc
|
||||||
if ((is_special and inc_special) or
|
if (
|
||||||
(is_private and inc_private) or
|
(is_special and inc_special)
|
||||||
(is_init and inc_init)):
|
or (is_private and inc_private)
|
||||||
|
or (is_init and inc_init)
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
return None
|
return None
|
||||||
|
@ -31,7 +31,8 @@ _xref_or_code_regex = re.compile(
|
|||||||
r'((?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:`.+?`)|'
|
r'((?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:`.+?`)|'
|
||||||
r'(?:``.+?``)|'
|
r'(?:``.+?``)|'
|
||||||
r'(?::meta .+:.*)|'
|
r'(?::meta .+:.*)|'
|
||||||
r'(?:`.+?\s*(?<!\x00)<.*?>`))')
|
r'(?:`.+?\s*(?<!\x00)<.*?>`))'
|
||||||
|
)
|
||||||
_xref_regex = re.compile(
|
_xref_regex = re.compile(
|
||||||
r'(?:(?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:)?`.+?`)',
|
r'(?:(?::(?:[a-zA-Z0-9]+[\-_+:.])*[a-zA-Z0-9]+:)?`.+?`)',
|
||||||
)
|
)
|
||||||
@ -39,17 +40,18 @@ _bullet_list_regex = re.compile(r'^(\*|\+|\-)(\s+\S|\s*$)')
|
|||||||
_enumerated_list_regex = re.compile(
|
_enumerated_list_regex = re.compile(
|
||||||
r'^(?P<paren>\()?'
|
r'^(?P<paren>\()?'
|
||||||
r'(\d+|#|[ivxlcdm]+|[IVXLCDM]+|[a-zA-Z])'
|
r'(\d+|#|[ivxlcdm]+|[IVXLCDM]+|[a-zA-Z])'
|
||||||
r'(?(paren)\)|\.)(\s+\S|\s*$)')
|
r'(?(paren)\)|\.)(\s+\S|\s*$)'
|
||||||
|
)
|
||||||
_token_regex = re.compile(
|
_token_regex = re.compile(
|
||||||
r"(,\sor\s|\sor\s|\sof\s|:\s|\sto\s|,\sand\s|\sand\s|,\s"
|
r'(,\sor\s|\sor\s|\sof\s|:\s|\sto\s|,\sand\s|\sand\s|,\s'
|
||||||
r"|[{]|[}]"
|
r'|[{]|[}]'
|
||||||
r'|"(?:\\"|[^"])*"'
|
r'|"(?:\\"|[^"])*"'
|
||||||
r"|'(?:\\'|[^'])*')",
|
r"|'(?:\\'|[^'])*')",
|
||||||
)
|
)
|
||||||
_default_regex = re.compile(
|
_default_regex = re.compile(
|
||||||
r"^default[^_0-9A-Za-z].*$",
|
r'^default[^_0-9A-Za-z].*$',
|
||||||
)
|
)
|
||||||
_SINGLETONS = ("None", "True", "False", "Ellipsis")
|
_SINGLETONS = ('None', 'True', 'False', 'Ellipsis')
|
||||||
|
|
||||||
|
|
||||||
class Deque(collections.deque[Any]):
|
class Deque(collections.deque[Any]):
|
||||||
@ -147,8 +149,11 @@ class GoogleDocstring:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>~?[a-zA-Z0-9_.-]+)`|"
|
_name_rgx = re.compile(
|
||||||
r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.VERBOSE)
|
r'^\s*((?::(?P<role>\S+):)?`(?P<name>~?[a-zA-Z0-9_.-]+)`|'
|
||||||
|
r' (?P<name2>~?[a-zA-Z0-9_.-]+))\s*',
|
||||||
|
re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -261,9 +266,8 @@ class GoogleDocstring:
|
|||||||
def _consume_indented_block(self, indent: int = 1) -> list[str]:
|
def _consume_indented_block(self, indent: int = 1) -> list[str]:
|
||||||
lines = []
|
lines = []
|
||||||
line = self._lines.get(0)
|
line = self._lines.get(0)
|
||||||
while (
|
while not self._is_section_break() and (
|
||||||
not self._is_section_break() and
|
not line or self._is_indented(line, indent)
|
||||||
(not line or self._is_indented(line, indent))
|
|
||||||
):
|
):
|
||||||
lines.append(self._lines.next())
|
lines.append(self._lines.next())
|
||||||
line = self._lines.get(0)
|
line = self._lines.get(0)
|
||||||
@ -271,9 +275,7 @@ class GoogleDocstring:
|
|||||||
|
|
||||||
def _consume_contiguous(self) -> list[str]:
|
def _consume_contiguous(self) -> list[str]:
|
||||||
lines = []
|
lines = []
|
||||||
while (self._lines and
|
while self._lines and self._lines.get(0) and not self._is_section_header():
|
||||||
self._lines.get(0) and
|
|
||||||
not self._is_section_header()):
|
|
||||||
lines.append(self._lines.next())
|
lines.append(self._lines.next())
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
@ -285,8 +287,11 @@ class GoogleDocstring:
|
|||||||
line = self._lines.get(0)
|
line = self._lines.get(0)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _consume_field(self, parse_type: bool = True, prefer_type: bool = False,
|
def _consume_field(
|
||||||
) -> tuple[str, str, list[str]]:
|
self,
|
||||||
|
parse_type: bool = True,
|
||||||
|
prefer_type: bool = False,
|
||||||
|
) -> tuple[str, str, list[str]]:
|
||||||
line = self._lines.next()
|
line = self._lines.next()
|
||||||
|
|
||||||
before, colon, after = self._partition_field_on_colon(line)
|
before, colon, after = self._partition_field_on_colon(line)
|
||||||
@ -311,14 +316,15 @@ class GoogleDocstring:
|
|||||||
_descs = self.__class__(_descs, self._config).lines()
|
_descs = self.__class__(_descs, self._config).lines()
|
||||||
return _name, _type, _descs
|
return _name, _type, _descs
|
||||||
|
|
||||||
def _consume_fields(self, parse_type: bool = True, prefer_type: bool = False,
|
def _consume_fields(
|
||||||
multiple: bool = False) -> list[tuple[str, str, list[str]]]:
|
self, parse_type: bool = True, prefer_type: bool = False, multiple: bool = False
|
||||||
|
) -> list[tuple[str, str, list[str]]]:
|
||||||
self._consume_empty()
|
self._consume_empty()
|
||||||
fields: list[tuple[str, str, list[str]]] = []
|
fields: list[tuple[str, str, list[str]]] = []
|
||||||
while not self._is_section_break():
|
while not self._is_section_break():
|
||||||
_name, _type, _desc = self._consume_field(parse_type, prefer_type)
|
_name, _type, _desc = self._consume_field(parse_type, prefer_type)
|
||||||
if multiple and _name:
|
if multiple and _name:
|
||||||
fields.extend((name.strip(), _type, _desc) for name in _name.split(","))
|
fields.extend((name.strip(), _type, _desc) for name in _name.split(','))
|
||||||
elif _name or _type or _desc:
|
elif _name or _type or _desc:
|
||||||
fields.append((_name, _type, _desc))
|
fields.append((_name, _type, _desc))
|
||||||
return fields
|
return fields
|
||||||
@ -333,8 +339,9 @@ class GoogleDocstring:
|
|||||||
_descs = self.__class__(_descs, self._config).lines()
|
_descs = self.__class__(_descs, self._config).lines()
|
||||||
return _type, _descs
|
return _type, _descs
|
||||||
|
|
||||||
def _consume_returns_section(self, preprocess_types: bool = False,
|
def _consume_returns_section(
|
||||||
) -> list[tuple[str, str, list[str]]]:
|
self, preprocess_types: bool = False
|
||||||
|
) -> list[tuple[str, str, list[str]]]:
|
||||||
lines = self._dedent(self._consume_to_next_section())
|
lines = self._dedent(self._consume_to_next_section())
|
||||||
if lines:
|
if lines:
|
||||||
before, colon, after = self._partition_field_on_colon(lines[0])
|
before, colon, after = self._partition_field_on_colon(lines[0])
|
||||||
@ -348,9 +355,10 @@ class GoogleDocstring:
|
|||||||
|
|
||||||
_type = before
|
_type = before
|
||||||
|
|
||||||
if (_type and preprocess_types and
|
if _type and preprocess_types and self._config.napoleon_preprocess_types:
|
||||||
self._config.napoleon_preprocess_types):
|
_type = _convert_type_spec(
|
||||||
_type = _convert_type_spec(_type, self._config.napoleon_type_aliases or {})
|
_type, self._config.napoleon_type_aliases or {}
|
||||||
|
)
|
||||||
|
|
||||||
_desc = self.__class__(_desc, self._config).lines()
|
_desc = self.__class__(_desc, self._config).lines()
|
||||||
return [(_name, _type, _desc)]
|
return [(_name, _type, _desc)]
|
||||||
@ -389,7 +397,9 @@ class GoogleDocstring:
|
|||||||
return [line[min_indent:] for line in lines]
|
return [line[min_indent:] for line in lines]
|
||||||
|
|
||||||
def _escape_args_and_kwargs(self, name: str) -> str:
|
def _escape_args_and_kwargs(self, name: str) -> str:
|
||||||
if name.endswith('_') and getattr(self._config, 'strip_signature_backslash', False):
|
if name.endswith('_') and getattr(
|
||||||
|
self._config, 'strip_signature_backslash', False
|
||||||
|
):
|
||||||
name = name[:-1] + r'\_'
|
name = name[:-1] + r'\_'
|
||||||
|
|
||||||
if name[:2] == '**':
|
if name[:2] == '**':
|
||||||
@ -423,7 +433,10 @@ class GoogleDocstring:
|
|||||||
return ['.. %s::' % admonition, '']
|
return ['.. %s::' % admonition, '']
|
||||||
|
|
||||||
def _format_block(
|
def _format_block(
|
||||||
self, prefix: str, lines: list[str], padding: str | None = None,
|
self,
|
||||||
|
prefix: str,
|
||||||
|
lines: list[str],
|
||||||
|
padding: str | None = None,
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
if lines:
|
if lines:
|
||||||
if padding is None:
|
if padding is None:
|
||||||
@ -440,9 +453,12 @@ class GoogleDocstring:
|
|||||||
else:
|
else:
|
||||||
return [prefix]
|
return [prefix]
|
||||||
|
|
||||||
def _format_docutils_params(self, fields: list[tuple[str, str, list[str]]],
|
def _format_docutils_params(
|
||||||
field_role: str = 'param', type_role: str = 'type',
|
self,
|
||||||
) -> list[str]:
|
fields: list[tuple[str, str, list[str]]],
|
||||||
|
field_role: str = 'param',
|
||||||
|
type_role: str = 'type',
|
||||||
|
) -> list[str]:
|
||||||
lines = []
|
lines = []
|
||||||
for _name, _type, _desc in fields:
|
for _name, _type, _desc in fields:
|
||||||
_desc = self._strip_empty(_desc)
|
_desc = self._strip_empty(_desc)
|
||||||
@ -486,8 +502,11 @@ class GoogleDocstring:
|
|||||||
else:
|
else:
|
||||||
return [field]
|
return [field]
|
||||||
|
|
||||||
def _format_fields(self, field_type: str, fields: list[tuple[str, str, list[str]]],
|
def _format_fields(
|
||||||
) -> list[str]:
|
self,
|
||||||
|
field_type: str,
|
||||||
|
fields: list[tuple[str, str, list[str]]],
|
||||||
|
) -> list[str]:
|
||||||
field_type = ':%s:' % field_type.strip()
|
field_type = ':%s:' % field_type.strip()
|
||||||
padding = ' ' * len(field_type)
|
padding = ' ' * len(field_type)
|
||||||
multi = len(fields) > 1
|
multi = len(fields) > 1
|
||||||
@ -579,11 +598,15 @@ class GoogleDocstring:
|
|||||||
|
|
||||||
def _is_section_break(self) -> bool:
|
def _is_section_break(self) -> bool:
|
||||||
line = self._lines.get(0)
|
line = self._lines.get(0)
|
||||||
return (not self._lines or
|
return (
|
||||||
self._is_section_header() or
|
not self._lines
|
||||||
(self._is_in_section and
|
or self._is_section_header()
|
||||||
line and
|
or (
|
||||||
not self._is_indented(line, self._section_indent)))
|
self._is_in_section
|
||||||
|
and line
|
||||||
|
and not self._is_indented(line, self._section_indent)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _load_custom_sections(self) -> None:
|
def _load_custom_sections(self) -> None:
|
||||||
if self._config.napoleon_custom_sections is not None:
|
if self._config.napoleon_custom_sections is not None:
|
||||||
@ -594,18 +617,20 @@ class GoogleDocstring:
|
|||||||
self._sections[entry.lower()] = self._parse_custom_generic_section
|
self._sections[entry.lower()] = self._parse_custom_generic_section
|
||||||
else:
|
else:
|
||||||
# otherwise, assume entry is container;
|
# otherwise, assume entry is container;
|
||||||
if entry[1] == "params_style":
|
if entry[1] == 'params_style':
|
||||||
self._sections[entry[0].lower()] = \
|
self._sections[entry[0].lower()] = (
|
||||||
self._parse_custom_params_style_section
|
self._parse_custom_params_style_section
|
||||||
elif entry[1] == "returns_style":
|
)
|
||||||
self._sections[entry[0].lower()] = \
|
elif entry[1] == 'returns_style':
|
||||||
|
self._sections[entry[0].lower()] = (
|
||||||
self._parse_custom_returns_style_section
|
self._parse_custom_returns_style_section
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# [0] is new section, [1] is the section to alias.
|
# [0] is new section, [1] is the section to alias.
|
||||||
# in the case of key mismatch, just handle as generic section.
|
# in the case of key mismatch, just handle as generic section.
|
||||||
self._sections[entry[0].lower()] = \
|
self._sections[entry[0].lower()] = self._sections.get(
|
||||||
self._sections.get(entry[1].lower(),
|
entry[1].lower(), self._parse_custom_generic_section
|
||||||
self._parse_custom_generic_section)
|
)
|
||||||
|
|
||||||
def _parse(self) -> None:
|
def _parse(self) -> None:
|
||||||
self._parsed_lines = self._consume_empty()
|
self._parsed_lines = self._consume_empty()
|
||||||
@ -721,9 +746,8 @@ class GoogleDocstring:
|
|||||||
fields = self._consume_fields()
|
fields = self._consume_fields()
|
||||||
if self._config.napoleon_use_keyword:
|
if self._config.napoleon_use_keyword:
|
||||||
return self._format_docutils_params(
|
return self._format_docutils_params(
|
||||||
fields,
|
fields, field_role='keyword', type_role='kwtype'
|
||||||
field_role="keyword",
|
)
|
||||||
type_role="kwtype")
|
|
||||||
else:
|
else:
|
||||||
return self._format_fields(_('Keyword Arguments'), fields)
|
return self._format_fields(_('Keyword Arguments'), fields)
|
||||||
|
|
||||||
@ -770,7 +794,7 @@ class GoogleDocstring:
|
|||||||
_type = m.group('name')
|
_type = m.group('name')
|
||||||
elif _xref_regex.match(_type):
|
elif _xref_regex.match(_type):
|
||||||
pos = _type.find('`')
|
pos = _type.find('`')
|
||||||
_type = _type[pos + 1:-1]
|
_type = _type[pos + 1 : -1]
|
||||||
_type = ' ' + _type if _type else ''
|
_type = ' ' + _type if _type else ''
|
||||||
_desc = self._strip_empty(_desc)
|
_desc = self._strip_empty(_desc)
|
||||||
_descs = ' ' + '\n '.join(_desc) if any(_desc) else ''
|
_descs = ' ' + '\n '.join(_desc) if any(_desc) else ''
|
||||||
@ -840,15 +864,13 @@ class GoogleDocstring:
|
|||||||
m = _single_colon_regex.search(source)
|
m = _single_colon_regex.search(source)
|
||||||
if (i % 2) == 0 and m:
|
if (i % 2) == 0 and m:
|
||||||
found_colon = True
|
found_colon = True
|
||||||
colon = source[m.start(): m.end()]
|
colon = source[m.start() : m.end()]
|
||||||
before_colon.append(source[:m.start()])
|
before_colon.append(source[: m.start()])
|
||||||
after_colon.append(source[m.end():])
|
after_colon.append(source[m.end() :])
|
||||||
else:
|
else:
|
||||||
before_colon.append(source)
|
before_colon.append(source)
|
||||||
|
|
||||||
return ("".join(before_colon).strip(),
|
return ''.join(before_colon).strip(), colon, ''.join(after_colon).strip()
|
||||||
colon,
|
|
||||||
"".join(after_colon).strip())
|
|
||||||
|
|
||||||
def _strip_empty(self, lines: list[str]) -> list[str]:
|
def _strip_empty(self, lines: list[str]) -> list[str]:
|
||||||
if lines:
|
if lines:
|
||||||
@ -866,29 +888,35 @@ class GoogleDocstring:
|
|||||||
end = i
|
end = i
|
||||||
break
|
break
|
||||||
if start > 0 or end + 1 < len(lines):
|
if start > 0 or end + 1 < len(lines):
|
||||||
lines = lines[start:end + 1]
|
lines = lines[start : end + 1]
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def _lookup_annotation(self, _name: str) -> str:
|
def _lookup_annotation(self, _name: str) -> str:
|
||||||
if self._config.napoleon_attr_annotations:
|
if self._config.napoleon_attr_annotations:
|
||||||
if self._what in ("module", "class", "exception") and self._obj:
|
if self._what in ('module', 'class', 'exception') and self._obj:
|
||||||
# cache the class annotations
|
# cache the class annotations
|
||||||
if not hasattr(self, "_annotations"):
|
if not hasattr(self, '_annotations'):
|
||||||
localns = getattr(self._config, "autodoc_type_aliases", {})
|
localns = getattr(self._config, 'autodoc_type_aliases', {})
|
||||||
localns.update(getattr(
|
localns.update(
|
||||||
self._config, "napoleon_type_aliases", {},
|
getattr(
|
||||||
) or {})
|
self._config,
|
||||||
|
'napoleon_type_aliases',
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
or {}
|
||||||
|
)
|
||||||
self._annotations = get_type_hints(self._obj, None, localns)
|
self._annotations = get_type_hints(self._obj, None, localns)
|
||||||
if _name in self._annotations:
|
if _name in self._annotations:
|
||||||
return stringify_annotation(self._annotations[_name],
|
return stringify_annotation(
|
||||||
'fully-qualified-except-typing')
|
self._annotations[_name], 'fully-qualified-except-typing'
|
||||||
|
)
|
||||||
# No annotation found
|
# No annotation found
|
||||||
return ""
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def _recombine_set_tokens(tokens: list[str]) -> list[str]:
|
def _recombine_set_tokens(tokens: list[str]) -> list[str]:
|
||||||
token_queue = collections.deque(tokens)
|
token_queue = collections.deque(tokens)
|
||||||
keywords = ("optional", "default")
|
keywords = ('optional', 'default')
|
||||||
|
|
||||||
def takewhile_set(tokens: collections.deque[str]) -> Iterator[str]:
|
def takewhile_set(tokens: collections.deque[str]) -> Iterator[str]:
|
||||||
open_braces = 0
|
open_braces = 0
|
||||||
@ -899,7 +927,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
break
|
break
|
||||||
|
|
||||||
if token == ", ":
|
if token == ', ':
|
||||||
previous_token = token
|
previous_token = token
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -916,9 +944,9 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]:
|
|||||||
yield previous_token
|
yield previous_token
|
||||||
previous_token = None
|
previous_token = None
|
||||||
|
|
||||||
if token == "{":
|
if token == '{':
|
||||||
open_braces += 1
|
open_braces += 1
|
||||||
elif token == "}":
|
elif token == '}':
|
||||||
open_braces -= 1
|
open_braces -= 1
|
||||||
|
|
||||||
yield token
|
yield token
|
||||||
@ -933,9 +961,9 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
break
|
break
|
||||||
|
|
||||||
if token == "{":
|
if token == '{':
|
||||||
tokens.appendleft("{")
|
tokens.appendleft('{')
|
||||||
yield "".join(takewhile_set(tokens))
|
yield ''.join(takewhile_set(tokens))
|
||||||
else:
|
else:
|
||||||
yield token
|
yield token
|
||||||
|
|
||||||
@ -950,7 +978,7 @@ def _tokenize_type_spec(spec: str) -> list[str]:
|
|||||||
# for now
|
# for now
|
||||||
other = item[8:]
|
other = item[8:]
|
||||||
|
|
||||||
return [default, " ", other]
|
return [default, ' ', other]
|
||||||
else:
|
else:
|
||||||
return [item]
|
return [item]
|
||||||
|
|
||||||
@ -973,70 +1001,74 @@ def _token_type(token: str, location: str | None = None) -> str:
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if token.startswith(" ") or token.endswith(" "):
|
if token.startswith(' ') or token.endswith(' '):
|
||||||
type_ = "delimiter"
|
type_ = 'delimiter'
|
||||||
elif (
|
elif (
|
||||||
is_numeric(token) or
|
is_numeric(token)
|
||||||
(token.startswith("{") and token.endswith("}")) or
|
or (token.startswith('{') and token.endswith('}'))
|
||||||
(token.startswith('"') and token.endswith('"')) or
|
or (token.startswith('"') and token.endswith('"'))
|
||||||
(token.startswith("'") and token.endswith("'"))
|
or (token.startswith("'") and token.endswith("'"))
|
||||||
):
|
):
|
||||||
type_ = "literal"
|
type_ = 'literal'
|
||||||
elif token.startswith("{"):
|
elif token.startswith('{'):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
__("invalid value set (missing closing brace): %s"),
|
__('invalid value set (missing closing brace): %s'),
|
||||||
token,
|
token,
|
||||||
location=location,
|
location=location,
|
||||||
)
|
)
|
||||||
type_ = "literal"
|
type_ = 'literal'
|
||||||
elif token.endswith("}"):
|
elif token.endswith('}'):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
__("invalid value set (missing opening brace): %s"),
|
__('invalid value set (missing opening brace): %s'),
|
||||||
token,
|
token,
|
||||||
location=location,
|
location=location,
|
||||||
)
|
)
|
||||||
type_ = "literal"
|
type_ = 'literal'
|
||||||
elif token.startswith(("'", '"')):
|
elif token.startswith(("'", '"')):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
__("malformed string literal (missing closing quote): %s"),
|
__('malformed string literal (missing closing quote): %s'),
|
||||||
token,
|
token,
|
||||||
location=location,
|
location=location,
|
||||||
)
|
)
|
||||||
type_ = "literal"
|
type_ = 'literal'
|
||||||
elif token.endswith(("'", '"')):
|
elif token.endswith(("'", '"')):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
__("malformed string literal (missing opening quote): %s"),
|
__('malformed string literal (missing opening quote): %s'),
|
||||||
token,
|
token,
|
||||||
location=location,
|
location=location,
|
||||||
)
|
)
|
||||||
type_ = "literal"
|
type_ = 'literal'
|
||||||
elif token in ("optional", "default"):
|
elif token in ('optional', 'default'):
|
||||||
# default is not a official keyword (yet) but supported by the
|
# default is not a official keyword (yet) but supported by the
|
||||||
# reference implementation (numpydoc) and widely used
|
# reference implementation (numpydoc) and widely used
|
||||||
type_ = "control"
|
type_ = 'control'
|
||||||
elif _xref_regex.match(token):
|
elif _xref_regex.match(token):
|
||||||
type_ = "reference"
|
type_ = 'reference'
|
||||||
else:
|
else:
|
||||||
type_ = "obj"
|
type_ = 'obj'
|
||||||
|
|
||||||
return type_
|
return type_
|
||||||
|
|
||||||
|
|
||||||
def _convert_numpy_type_spec(
|
def _convert_numpy_type_spec(
|
||||||
_type: str, location: str | None = None, translations: dict[str, str] | None = None,
|
_type: str,
|
||||||
|
location: str | None = None,
|
||||||
|
translations: dict[str, str] | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
if translations is None:
|
if translations is None:
|
||||||
translations = {}
|
translations = {}
|
||||||
|
|
||||||
def convert_obj(obj: str, translations: dict[str, str], default_translation: str) -> str:
|
def convert_obj(
|
||||||
|
obj: str, translations: dict[str, str], default_translation: str
|
||||||
|
) -> str:
|
||||||
translation = translations.get(obj, obj)
|
translation = translations.get(obj, obj)
|
||||||
|
|
||||||
# use :class: (the default) only if obj is not a standard singleton
|
# use :class: (the default) only if obj is not a standard singleton
|
||||||
if translation in _SINGLETONS and default_translation == ":class:`%s`":
|
if translation in _SINGLETONS and default_translation == ':class:`%s`':
|
||||||
default_translation = ":obj:`%s`"
|
default_translation = ':obj:`%s`'
|
||||||
elif translation == "..." and default_translation == ":class:`%s`":
|
elif translation == '...' and default_translation == ':class:`%s`':
|
||||||
# allow referencing the builtin ...
|
# allow referencing the builtin ...
|
||||||
default_translation = ":obj:`%s <Ellipsis>`"
|
default_translation = ':obj:`%s <Ellipsis>`'
|
||||||
|
|
||||||
if _xref_regex.match(translation) is None:
|
if _xref_regex.match(translation) is None:
|
||||||
translation = default_translation % translation
|
translation = default_translation % translation
|
||||||
@ -1045,21 +1077,20 @@ def _convert_numpy_type_spec(
|
|||||||
|
|
||||||
tokens = _tokenize_type_spec(_type)
|
tokens = _tokenize_type_spec(_type)
|
||||||
combined_tokens = _recombine_set_tokens(tokens)
|
combined_tokens = _recombine_set_tokens(tokens)
|
||||||
types = [
|
types = [(token, _token_type(token, location)) for token in combined_tokens]
|
||||||
(token, _token_type(token, location))
|
|
||||||
for token in combined_tokens
|
|
||||||
]
|
|
||||||
|
|
||||||
converters = {
|
converters = {
|
||||||
"literal": lambda x: "``%s``" % x,
|
'literal': lambda x: '``%s``' % x,
|
||||||
"obj": lambda x: convert_obj(x, translations, ":class:`%s`"),
|
'obj': lambda x: convert_obj(x, translations, ':class:`%s`'),
|
||||||
"control": lambda x: "*%s*" % x,
|
'control': lambda x: '*%s*' % x,
|
||||||
"delimiter": lambda x: x,
|
'delimiter': lambda x: x,
|
||||||
"reference": lambda x: x,
|
'reference': lambda x: x,
|
||||||
}
|
}
|
||||||
|
|
||||||
converted = "".join(converters.get(type_)(token) # type: ignore[misc]
|
converted = ''.join(
|
||||||
for token, type_ in types)
|
converters.get(type_)(token) # type: ignore[misc]
|
||||||
|
for token, type_ in types
|
||||||
|
)
|
||||||
|
|
||||||
return converted
|
return converted
|
||||||
|
|
||||||
@ -1181,20 +1212,21 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
if filepath is None and name is None:
|
if filepath is None and name is None:
|
||||||
return None
|
return None
|
||||||
elif filepath is None:
|
elif filepath is None:
|
||||||
filepath = ""
|
filepath = ''
|
||||||
|
|
||||||
return f"{filepath}:docstring of {name}"
|
return f'{filepath}:docstring of {name}'
|
||||||
|
|
||||||
def _escape_args_and_kwargs(self, name: str) -> str:
|
def _escape_args_and_kwargs(self, name: str) -> str:
|
||||||
func = super()._escape_args_and_kwargs
|
func = super()._escape_args_and_kwargs
|
||||||
|
|
||||||
if ", " in name:
|
if ', ' in name:
|
||||||
return ", ".join(map(func, name.split(", ")))
|
return ', '.join(map(func, name.split(', ')))
|
||||||
else:
|
else:
|
||||||
return func(name)
|
return func(name)
|
||||||
|
|
||||||
def _consume_field(self, parse_type: bool = True, prefer_type: bool = False,
|
def _consume_field(
|
||||||
) -> tuple[str, str, list[str]]:
|
self, parse_type: bool = True, prefer_type: bool = False
|
||||||
|
) -> tuple[str, str, list[str]]:
|
||||||
line = self._lines.next()
|
line = self._lines.next()
|
||||||
if parse_type:
|
if parse_type:
|
||||||
_name, _, _type = self._partition_field_on_colon(line)
|
_name, _, _type = self._partition_field_on_colon(line)
|
||||||
@ -1221,8 +1253,9 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
_desc = self.__class__(_desc, self._config).lines()
|
_desc = self.__class__(_desc, self._config).lines()
|
||||||
return _name, _type, _desc
|
return _name, _type, _desc
|
||||||
|
|
||||||
def _consume_returns_section(self, preprocess_types: bool = False,
|
def _consume_returns_section(
|
||||||
) -> list[tuple[str, str, list[str]]]:
|
self, preprocess_types: bool = False
|
||||||
|
) -> list[tuple[str, str, list[str]]]:
|
||||||
return self._consume_fields(prefer_type=True)
|
return self._consume_fields(prefer_type=True)
|
||||||
|
|
||||||
def _consume_section_header(self) -> str:
|
def _consume_section_header(self) -> str:
|
||||||
@ -1234,12 +1267,16 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
|
|
||||||
def _is_section_break(self) -> bool:
|
def _is_section_break(self) -> bool:
|
||||||
line1, line2 = self._lines.get(0), self._lines.get(1)
|
line1, line2 = self._lines.get(0), self._lines.get(1)
|
||||||
return (not self._lines or
|
return (
|
||||||
self._is_section_header() or
|
not self._lines
|
||||||
(line1 == line2 == '') or
|
or self._is_section_header()
|
||||||
(self._is_in_section and
|
or (line1 == line2 == '')
|
||||||
line1 and
|
or (
|
||||||
not self._is_indented(line1, self._section_indent)))
|
self._is_in_section
|
||||||
|
and line1
|
||||||
|
and not self._is_indented(line1, self._section_indent)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _is_section_header(self) -> bool:
|
def _is_section_header(self) -> bool:
|
||||||
section, underline = self._lines.get(0), self._lines.get(1)
|
section, underline = self._lines.get(0), self._lines.get(1)
|
||||||
@ -1312,7 +1349,7 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
return g[3], None
|
return g[3], None
|
||||||
else:
|
else:
|
||||||
return g[2], g[1]
|
return g[2], g[1]
|
||||||
raise ValueError("%s is not a item name" % text)
|
raise ValueError('%s is not a item name' % text)
|
||||||
|
|
||||||
def push_item(name: str | None, rest: list[str]) -> None:
|
def push_item(name: str | None, rest: list[str]) -> None:
|
||||||
if not name:
|
if not name:
|
||||||
@ -1322,7 +1359,9 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
rest.clear()
|
rest.clear()
|
||||||
|
|
||||||
def translate(
|
def translate(
|
||||||
func: str, description: list[str], role: str | None,
|
func: str,
|
||||||
|
description: list[str],
|
||||||
|
role: str | None,
|
||||||
) -> tuple[str, list[str], str | None]:
|
) -> tuple[str, list[str], str | None]:
|
||||||
translations = self._config.napoleon_type_aliases
|
translations = self._config.napoleon_type_aliases
|
||||||
if role is not None or not translations:
|
if role is not None or not translations:
|
||||||
@ -1334,8 +1373,8 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
return translated, description, role
|
return translated, description, role
|
||||||
|
|
||||||
groups = match.groupdict()
|
groups = match.groupdict()
|
||||||
role = groups["role"]
|
role = groups['role']
|
||||||
new_func = groups["name"] or groups["name2"]
|
new_func = groups['name'] or groups['name2']
|
||||||
|
|
||||||
return new_func, description, role
|
return new_func, description, role
|
||||||
|
|
||||||
@ -1347,9 +1386,9 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
m = self._name_rgx.match(line)
|
m = self._name_rgx.match(line)
|
||||||
if m and line[m.end():].strip().startswith(':'):
|
if m and line[m.end() :].strip().startswith(':'):
|
||||||
push_item(current_func, rest)
|
push_item(current_func, rest)
|
||||||
current_func, line = line[:m.end()], line[m.end():]
|
current_func, line = line[: m.end()], line[m.end() :]
|
||||||
rest = [line.split(':', 1)[1].strip()]
|
rest = [line.split(':', 1)[1].strip()]
|
||||||
if not rest[0]:
|
if not rest[0]:
|
||||||
rest = []
|
rest = []
|
||||||
@ -1383,7 +1422,7 @@ class NumpyDocstring(GoogleDocstring):
|
|||||||
lines += ['']
|
lines += ['']
|
||||||
lines += [link]
|
lines += [link]
|
||||||
else:
|
else:
|
||||||
lines[-1] += ", %s" % link
|
lines[-1] += ', %s' % link
|
||||||
if desc:
|
if desc:
|
||||||
lines += self._indent([' '.join(desc)])
|
lines += self._indent([' '.join(desc)])
|
||||||
last_had_desc = True
|
last_had_desc = True
|
||||||
|
Loading…
Reference in New Issue
Block a user