diff options
Diffstat (limited to 'sphinx/ext/napoleon')
-rw-r--r-- | sphinx/ext/napoleon/__init__.py | 19 | ||||
-rw-r--r-- | sphinx/ext/napoleon/docstring.py | 66 |
2 files changed, 46 insertions, 39 deletions
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index 61aa3d8..581f3ea 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -2,13 +2,17 @@ from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any import sphinx from sphinx.application import Sphinx from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring from sphinx.util import inspect +if TYPE_CHECKING: + from sphinx.config import _ConfigRebuild + from sphinx.util.typing import ExtensionMetadata + class Config: """Sphinx napoleon extension settings in `conf.py`. @@ -261,8 +265,9 @@ class Config: Use the type annotations of class attributes that are documented in the docstring but do not have a type in the docstring. - """ - _config_values = { + """ # NoQA: D301 + + _config_values: dict[str, tuple[Any, _ConfigRebuild]] = { 'napoleon_google_docstring': (True, 'env'), 'napoleon_numpy_docstring': (True, 'env'), 'napoleon_include_init_with_doc': (False, 'env'), @@ -288,7 +293,7 @@ class Config: setattr(self, name, value) -def setup(app: Sphinx) -> dict[str, Any]: +def setup(app: Sphinx) -> ExtensionMetadata: """Sphinx extension setup function. When the extension is loaded, Sphinx imports this module and executes @@ -326,7 +331,7 @@ def setup(app: Sphinx) -> dict[str, Any]: def _patch_python_domain() -> None: - from sphinx.domains.python import PyObject, PyTypedField + from sphinx.domains.python._object import PyObject, PyTypedField from sphinx.locale import _ for doc_field in PyObject.doc_field_types: if doc_field.name == 'parameter': @@ -335,7 +340,7 @@ def _patch_python_domain() -> None: PyObject.doc_field_types.append( PyTypedField('keyword', label=_('Keyword Arguments'), names=('keyword', 'kwarg', 'kwparam'), - typerolename='obj', typenames=('paramtype', 'kwtype'), + typerolename='class', typenames=('paramtype', 'kwtype'), can_collapse=True)) @@ -386,7 +391,7 @@ def _process_docstring(app: Sphinx, what: str, name: str, obj: Any, docstring = GoogleDocstring(result_lines, app.config, app, what, name, obj, options) result_lines = docstring.lines() - lines[:] = result_lines[:] + lines[:] = result_lines.copy() def _skip_member(app: Sphinx, what: str, name: str, obj: Any, diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 2ffde39..2ce3b2d 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -7,6 +7,7 @@ import contextlib import inspect import re from functools import partial +from itertools import starmap from typing import TYPE_CHECKING, Any, Callable from sphinx.locale import _, __ @@ -14,6 +15,8 @@ from sphinx.util import logging from sphinx.util.typing import get_type_hints, stringify_annotation if TYPE_CHECKING: + from collections.abc import Iterator + from sphinx.application import Sphinx from sphinx.config import Config as SphinxConfig @@ -145,7 +148,7 @@ class GoogleDocstring: """ _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>~?[a-zA-Z0-9_.-]+)`|" - r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.X) + r" (?P<name2>~?[a-zA-Z0-9_.-]+))\s*", re.VERBOSE) def __init__( self, @@ -304,19 +307,18 @@ class GoogleDocstring: _type = _convert_type_spec(_type, self._config.napoleon_type_aliases or {}) indent = self._get_indent(line) + 1 - _descs = [_desc] + self._dedent(self._consume_indented_block(indent)) + _descs = [_desc, *self._dedent(self._consume_indented_block(indent))] _descs = self.__class__(_descs, self._config).lines() return _name, _type, _descs def _consume_fields(self, parse_type: bool = True, prefer_type: bool = False, multiple: bool = False) -> list[tuple[str, str, list[str]]]: self._consume_empty() - fields = [] + fields: list[tuple[str, str, list[str]]] = [] while not self._is_section_break(): _name, _type, _desc = self._consume_field(parse_type, prefer_type) if multiple and _name: - for name in _name.split(","): - fields.append((name.strip(), _type, _desc)) + fields.extend((name.strip(), _type, _desc) for name in _name.split(",")) elif _name or _type or _desc: fields.append((_name, _type, _desc)) return fields @@ -327,7 +329,7 @@ class GoogleDocstring: if not colon or not _desc: _type, _desc = _desc, _type _desc += colon - _descs = [_desc] + self._dedent(self._consume_to_end()) + _descs = [_desc, *self._dedent(self._consume_to_end())] _descs = self.__class__(_descs, self._config).lines() return _type, _descs @@ -399,15 +401,15 @@ class GoogleDocstring: def _fix_field_desc(self, desc: list[str]) -> list[str]: if self._is_list(desc): - desc = [''] + desc + desc = ['', *desc] elif desc[0].endswith('::'): desc_block = desc[1:] indent = self._get_indent(desc[0]) block_indent = self._get_initial_indent(desc_block) if block_indent > indent: - desc = [''] + desc + desc = ['', *desc] else: - desc = ['', desc[0]] + self._indent(desc_block, 4) + desc = ['', desc[0], *self._indent(desc_block, 4)] return desc def _format_admonition(self, admonition: str, lines: list[str]) -> list[str]: @@ -416,7 +418,7 @@ class GoogleDocstring: return [f'.. {admonition}:: {lines[0].strip()}', ''] elif lines: lines = self._indent(self._dedent(lines), 3) - return ['.. %s::' % admonition, ''] + lines + [''] + return ['.. %s::' % admonition, '', *lines, ''] else: return ['.. %s::' % admonition, ''] @@ -453,7 +455,7 @@ class GoogleDocstring: if _type: lines.append(f':{type_role} {_name}: {_type}') - return lines + [''] + return [*lines, ''] def _format_field(self, _name: str, _type: str, _desc: list[str]) -> list[str]: _desc = self._strip_empty(_desc) @@ -480,7 +482,7 @@ class GoogleDocstring: if _desc[0]: return [field + _desc[0]] + _desc[1:] else: - return [field] + _desc + return [field, *_desc] else: return [field] @@ -537,7 +539,7 @@ class GoogleDocstring: return [(' ' * n) + line for line in lines] def _is_indented(self, line: str, indent: int = 1) -> bool: - for i, s in enumerate(line): # noqa: SIM110 + for i, s in enumerate(line): # NoQA: SIM110 if i >= indent: return True elif not s.isspace(): @@ -623,7 +625,7 @@ class GoogleDocstring: self._is_in_section = True self._section_indent = self._get_current_indent() if _directive_regex.match(section): - lines = [section] + self._consume_to_next_section() + lines = [section, *self._consume_to_next_section()] else: lines = self._sections[section.lower()](section) finally: @@ -711,7 +713,7 @@ class GoogleDocstring: else: header = '.. rubric:: %s' % section if lines: - return [header, ''] + lines + [''] + return [header, '', *lines, ''] else: return [header, ''] @@ -733,7 +735,7 @@ class GoogleDocstring: if 'no-index' in self._opt or 'noindex' in self._opt: lines.append(' :no-index:') if _desc: - lines.extend([''] + self._indent(_desc, 3)) + lines.extend(['', *self._indent(_desc, 3)]) lines.append('') return lines @@ -888,7 +890,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]: token_queue = collections.deque(tokens) keywords = ("optional", "default") - def takewhile_set(tokens): + def takewhile_set(tokens: collections.deque[str]) -> Iterator[str]: open_braces = 0 previous_token = None while True: @@ -924,7 +926,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]: if open_braces == 0: break - def combine_set(tokens): + def combine_set(tokens: collections.deque[str]) -> Iterator[str]: while True: try: token = tokens.popleft() @@ -941,7 +943,7 @@ def _recombine_set_tokens(tokens: list[str]) -> list[str]: def _tokenize_type_spec(spec: str) -> list[str]: - def postprocess(item): + def postprocess(item: str) -> list[str]: if _default_regex.match(item): default = item[:7] # can't be separated by anything other than a single space @@ -962,7 +964,7 @@ def _tokenize_type_spec(spec: str) -> list[str]: def _token_type(token: str, location: str | None = None) -> str: - def is_numeric(token): + def is_numeric(token: str) -> bool: try: # use complex to make sure every numeric value is detected as literal complex(token) @@ -1026,7 +1028,7 @@ def _convert_numpy_type_spec( if translations is None: translations = {} - def convert_obj(obj, translations, default_translation): + def convert_obj(obj: str, translations: dict[str, str], default_translation: str) -> str: translation = translations.get(obj, obj) # use :class: (the default) only if obj is not a standard singleton @@ -1155,6 +1157,7 @@ class NumpyDocstring(GoogleDocstring): The lines of the docstring in a list. """ + def __init__( self, docstring: str | list[str], @@ -1180,13 +1183,13 @@ class NumpyDocstring(GoogleDocstring): elif filepath is None: filepath = "" - return ":".join([filepath, "docstring of %s" % name]) + return f"{filepath}:docstring of {name}" def _escape_args_and_kwargs(self, name: str) -> str: func = super()._escape_args_and_kwargs if ", " in name: - return ", ".join(func(param) for param in name.split(", ")) + return ", ".join(map(func, name.split(", "))) else: return func(name) @@ -1233,7 +1236,7 @@ class NumpyDocstring(GoogleDocstring): line1, line2 = self._lines.get(0), self._lines.get(1) return (not self._lines or self._is_section_header() or - ['', ''] == [line1, line2] or + (line1 == line2 == '') or (self._is_in_section and line1 and not self._is_indented(line1, self._section_indent))) @@ -1269,7 +1272,7 @@ class NumpyDocstring(GoogleDocstring): func_name1, func_name2, :meth:`func_name`, func_name3 """ - items = [] + items: list[tuple[str, list[str], str | None]] = [] def parse_item_name(text: str) -> tuple[str, str | None]: """Match ':role:`name`' or 'name'""" @@ -1286,10 +1289,12 @@ class NumpyDocstring(GoogleDocstring): if not name: return name, role = parse_item_name(name) - items.append((name, list(rest), role)) - del rest[:] + items.append((name, rest.copy(), role)) + rest.clear() - def translate(func, description, role): + def translate( + func: str, description: list[str], role: str | None, + ) -> tuple[str, list[str], str | None]: translations = self._config.napoleon_type_aliases if role is not None or not translations: return func, description, role @@ -1336,10 +1341,7 @@ class NumpyDocstring(GoogleDocstring): return [] # apply type aliases - items = [ - translate(func, description, role) - for func, description, role in items - ] + items = list(starmap(translate, items)) lines: list[str] = [] last_had_desc = True |