diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 40d6bfa04..ae0ab31db 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -506,33 +506,31 @@ class GoogleDocstring(UnicodeMixin): return self._format_fields('Parameters', fields) def _parse_raises_section(self, section): - fields = self._consume_fields() + fields = self._consume_fields(parse_type=False, prefer_type=True) field_type = ':raises:' padding = ' ' * len(field_type) multi = len(fields) > 1 lines = [] - for _name, _type, _desc in fields: - sep = _desc and ' -- ' or '' - if _name: - if ' ' in _name: - _name = '**%s**' % _name + for _, _type, _desc in fields: + has_desc = any(_desc) + sep = (_desc and _desc[0]) and ' -- ' or '' + if _type: + has_refs = '`' in _type or ':' in _type + has_space = any(c in ' \t\n\v\f ' for c in _type) + + if not has_refs and not has_space: + _type = ':exc:`%s`%s' % (_type, sep) + elif has_desc and has_space: + _type = '*%s*%s' % (_type, sep) else: - _name = ':exc:`%s`' % _name - if _type: - if '`' in _type: - field = ['%s (%s)%s' % (_name, _type, sep)] - else: - field = ['%s (*%s*)%s' % (_name, _type, sep)] + _type = '%s%s' % (_type, sep) + + if has_desc: + field = [_type] + _desc else: - field = ['%s%s' % (_name, sep)] - elif _type: - if '`' in _type: - field = ['%s%s' % (_type, sep)] - else: - field = ['*%s*%s' % (_type, sep)] + field = [_type] else: - field = [] - field = field + _desc + field = _desc if multi: if lines: lines.extend(self._format_block(padding + ' * ', field)) @@ -540,6 +538,8 @@ class GoogleDocstring(UnicodeMixin): lines.extend(self._format_block(field_type + ' * ', field)) else: lines.extend(self._format_block(field_type + ' ', field)) + if lines and lines[-1]: + lines.append('') return lines def _parse_references_section(self, section): @@ -736,8 +736,6 @@ class NumpyDocstring(GoogleDocstring): line = next(self._line_iter) if parse_type: _name, _, _type = self._partition_field_on_colon(line) - if not _name: - _type = line else: _name, _type = line, '' _name, _type = _name.strip(), _type.strip() diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 5cb5f1627..0543d0846 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -376,6 +376,175 @@ Returns: actual = str(GoogleDocstring(docstring)) self.assertEqual(expected, actual) + def test_raises_types(self): + docstrings = [(""" +Example Function + +Raises: + RuntimeError: + A setting wasn't specified, or was invalid. + ValueError: + Something something value error. + +""", """ +Example Function + +:raises: * :exc:`RuntimeError` + + A setting wasn't specified, or was invalid. + * :exc:`ValueError` + + Something something value error. +"""), + ################################ + (""" +Example Function + +Raises: + InvalidDimensionsError + +""", """ +Example Function + +:raises: :exc:`InvalidDimensionsError` +"""), + ################################ + (""" +Example Function + +Raises: + Invalid Dimensions Error + +""", """ +Example Function + +:raises: Invalid Dimensions Error +"""), + ################################ + (""" +Example Function + +Raises: + Invalid Dimensions Error: With description + +""", """ +Example Function + +:raises: *Invalid Dimensions Error* -- + With description +"""), + ################################ + (""" +Example Function + +Raises: + InvalidDimensionsError: If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: :exc:`InvalidDimensionsError` -- + If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises: + Invalid Dimensions Error: If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: *Invalid Dimensions Error* -- + If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises: + If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises: + :class:`exc.InvalidDimensionsError` + +""", """ +Example Function + +:raises: :class:`exc.InvalidDimensionsError` +"""), + ################################ + (""" +Example Function + +Raises: + :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: :class:`exc.InvalidDimensionsError` -- + If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises: + :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed, + then a :class:`exc.InvalidDimensionsError` will be raised. + +""", """ +Example Function + +:raises: :class:`exc.InvalidDimensionsError` -- + If the dimensions couldn't be parsed, + then a :class:`exc.InvalidDimensionsError` will be raised. +"""), + ################################ + (""" +Example Function + +Raises: + :class:`exc.InvalidDimensionsError`: If the dimensions couldn't be parsed. + :class:`exc.InvalidArgumentsError`: If the arguments are invalid. + +""", """ +Example Function + +:raises: * :class:`exc.InvalidDimensionsError` -- + If the dimensions couldn't be parsed. + * :class:`exc.InvalidArgumentsError` -- + If the arguments are invalid. +"""), + ################################ + (""" +Example Function + +Raises: + :class:`exc.InvalidDimensionsError` + :class:`exc.InvalidArgumentsError` + +""", """ +Example Function + +:raises: * :class:`exc.InvalidDimensionsError` + * :class:`exc.InvalidArgumentsError` +""")] + for docstring, expected in docstrings: + actual = str(GoogleDocstring(docstring)) + self.assertEqual(expected, actual) + def test_kwargs_in_arguments(self): docstring = """Allows to create attributes binded to this device. @@ -564,7 +733,7 @@ class NumpyDocstringTest(BaseDocstringTest): :Yields: *str* -- Extended description of yielded value""" - )] + )] def test_docstrings(self): config = Config(napoleon_use_param=False, napoleon_use_rtype=False) @@ -713,6 +882,198 @@ arg_ : type self.assertEqual(expected, actual) + def test_raises_types(self): + docstrings = [(""" +Example Function + +Raises +------ + RuntimeError + + A setting wasn't specified, or was invalid. + ValueError + + Something something value error. + +""", """ +Example Function + +:raises: * :exc:`RuntimeError` + + A setting wasn't specified, or was invalid. + * :exc:`ValueError` + + Something something value error. +"""), + ################################ + (""" +Example Function + +Raises +------ +InvalidDimensionsError + +""", """ +Example Function + +:raises: :exc:`InvalidDimensionsError` +"""), + ################################ + (""" +Example Function + +Raises +------ +Invalid Dimensions Error + +""", """ +Example Function + +:raises: Invalid Dimensions Error +"""), + ################################ + (""" +Example Function + +Raises +------ +Invalid Dimensions Error + With description + +""", """ +Example Function + +:raises: *Invalid Dimensions Error* -- + With description +"""), + ################################ + (""" +Example Function + +Raises +------ +InvalidDimensionsError + If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: :exc:`InvalidDimensionsError` -- + If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises +------ +Invalid Dimensions Error + If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: *Invalid Dimensions Error* -- + If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises +------ +If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises +------ +:class:`exc.InvalidDimensionsError` + +""", """ +Example Function + +:raises: :class:`exc.InvalidDimensionsError` +"""), + ################################ + (""" +Example Function + +Raises +------ +:class:`exc.InvalidDimensionsError` + If the dimensions couldn't be parsed. + +""", """ +Example Function + +:raises: :class:`exc.InvalidDimensionsError` -- + If the dimensions couldn't be parsed. +"""), + ################################ + (""" +Example Function + +Raises +------ +:class:`exc.InvalidDimensionsError` + If the dimensions couldn't be parsed, + then a :class:`exc.InvalidDimensionsError` will be raised. + +""", """ +Example Function + +:raises: :class:`exc.InvalidDimensionsError` -- + If the dimensions couldn't be parsed, + then a :class:`exc.InvalidDimensionsError` will be raised. +"""), + ################################ + (""" +Example Function + +Raises +------ +:class:`exc.InvalidDimensionsError` + If the dimensions couldn't be parsed. +:class:`exc.InvalidArgumentsError` + If the arguments are invalid. + +""", """ +Example Function + +:raises: * :class:`exc.InvalidDimensionsError` -- + If the dimensions couldn't be parsed. + * :class:`exc.InvalidArgumentsError` -- + If the arguments are invalid. +"""), + ################################ + (""" +Example Function + +Raises +------ +:class:`exc.InvalidDimensionsError` +:class:`exc.InvalidArgumentsError` + +""", """ +Example Function + +:raises: * :class:`exc.InvalidDimensionsError` + * :class:`exc.InvalidArgumentsError` +""")] + for docstring, expected in docstrings: + config = Config() + app = mock.Mock() + actual = str(NumpyDocstring(docstring, config, app, "method")) + self.assertEqual(expected, actual) + def test_xrefs_in_return_type(self): docstring = """ Example Function