diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 84dfc1e70c5..1eaff99e028 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -154,7 +154,7 @@ def postprocess(item: str) -> list[str]: return tokens -def _token_type(token: str, location: str | None = None) -> str: +def _token_type(token: str, debug_location: str | None = None) -> str: def is_numeric(token: str) -> bool: try: # use complex to make sure every numeric value is detected as literal @@ -177,28 +177,28 @@ def is_numeric(token: str) -> bool: logger.warning( __('invalid value set (missing closing brace): %s'), token, - location=location, + location=debug_location, ) type_ = 'literal' elif token.endswith('}'): logger.warning( __('invalid value set (missing opening brace): %s'), token, - location=location, + location=debug_location, ) type_ = 'literal' elif token.startswith(("'", '"')): logger.warning( __('malformed string literal (missing closing quote): %s'), token, - location=location, + location=debug_location, ) type_ = 'literal' elif token.endswith(("'", '"')): logger.warning( __('malformed string literal (missing opening quote): %s'), token, - location=location, + location=debug_location, ) type_ = 'literal' elif token in {'optional', 'default'}: @@ -213,10 +213,10 @@ def is_numeric(token: str) -> bool: return type_ -def _convert_numpy_type_spec( +def _convert_type_spec( _type: str, - location: str | None = None, translations: dict[str, str] | None = None, + debug_location: str | None = None, ) -> str: if translations is None: translations = {} @@ -240,7 +240,7 @@ def convert_obj( tokens = _tokenize_type_spec(_type) combined_tokens = _recombine_set_tokens(tokens) - types = [(token, _token_type(token, location)) for token in combined_tokens] + types = [(token, _token_type(token, debug_location)) for token in combined_tokens] converters = { 'literal': lambda x: '``%s``' % x, @@ -258,15 +258,6 @@ def convert_obj( return converted -def _convert_type_spec(_type: str, translations: dict[str, str] | None = None) -> str: - """Convert type specification to reference in reST.""" - if translations is not None and _type in translations: - return translations[_type] - if _type == 'None': - return ':py:obj:`None`' - return f':py:class:`{_type}`' - - class GoogleDocstring: """Convert Google style docstrings to reStructuredText. @@ -433,6 +424,20 @@ def __str__(self) -> str: """ return '\n'.join(self.lines()) + def _get_location(self) -> str | None: + try: + filepath = inspect.getfile(self._obj) if self._obj is not None else None + except TypeError: + filepath = None + name = self._name + + if filepath is None and name is None: + return None + elif filepath is None: + filepath = '' + + return f'{filepath}:docstring of {name}' + def lines(self) -> list[str]: """Return the parsed lines of the docstring in reStructuredText format. @@ -490,7 +495,11 @@ def _consume_field( _type, _name = _name, _type if _type and self._config.napoleon_preprocess_types: - _type = _convert_type_spec(_type, self._config.napoleon_type_aliases or {}) + _type = _convert_type_spec( + _type, + translations=self._config.napoleon_type_aliases or {}, + debug_location=self._get_location(), + ) indent = self._get_indent(line) + 1 _descs = [_desc, *self._dedent(self._consume_indented_block(indent))] @@ -538,7 +547,9 @@ def _consume_returns_section( if _type and preprocess_types and self._config.napoleon_preprocess_types: _type = _convert_type_spec( - _type, self._config.napoleon_type_aliases or {} + _type, + translations=self._config.napoleon_type_aliases or {}, + debug_location=self._get_location(), ) _desc = self.__class__(_desc, self._config).lines() @@ -1202,20 +1213,6 @@ def __init__( self._directive_sections = ['.. index::'] super().__init__(docstring, config, app, what, name, obj, options) - def _get_location(self) -> str | None: - try: - filepath = inspect.getfile(self._obj) if self._obj is not None else None - except TypeError: - filepath = None - name = self._name - - if filepath is None and name is None: - return None - elif filepath is None: - filepath = '' - - return f'{filepath}:docstring of {name}' - def _escape_args_and_kwargs(self, name: str) -> str: func = super()._escape_args_and_kwargs @@ -1242,10 +1239,10 @@ def _consume_field( _type, _name = _name, _type if self._config.napoleon_preprocess_types: - _type = _convert_numpy_type_spec( + _type = _convert_type_spec( _type, - location=self._get_location(), translations=self._config.napoleon_type_aliases or {}, + debug_location=self._get_location(), ) indent = self._get_indent(line) + 1 diff --git a/tests/test_extensions/test_ext_napoleon_docstring.py b/tests/test_extensions/test_ext_napoleon_docstring.py index 1e0cf3ae023..dcf43b92bf3 100644 --- a/tests/test_extensions/test_ext_napoleon_docstring.py +++ b/tests/test_extensions/test_ext_napoleon_docstring.py @@ -15,7 +15,7 @@ from sphinx.ext.napoleon.docstring import ( GoogleDocstring, NumpyDocstring, - _convert_numpy_type_spec, + _convert_type_spec, _recombine_set_tokens, _token_type, _tokenize_type_spec, @@ -1330,7 +1330,7 @@ def test_preprocess_types(self): expected = """\ Do as you please -:Yields: :py:class:`str` -- Extended +:Yields: :class:`str` -- Extended """ assert str(actual) == expected @@ -2675,7 +2675,7 @@ def test_convert_numpy_type_spec(self): ) for spec, expected in zip(specs, converted, strict=True): - actual = _convert_numpy_type_spec(spec, translations=translations) + actual = _convert_type_spec(spec, translations=translations) assert actual == expected def test_parameter_types(self):