diff --git a/CHANGELOG.md b/CHANGELOG.md index a0576846..12f7a86a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - General: **Breaking:** Leave attributes out of `Docstring.params`, and list them in the new `Docstring.attrs` - General: **Breaking:** Leave yields out of `Docstring.returns` and `many_returns`; read them from the new `yields` and `many_yields` - General: **Breaking:** Make `is_optional` of a parsed param always a bool, `False` instead of `None` when it has no type +- General: **Breaking:** Give an item with no description `None` as its description, never `""` - General: **Breaking:** Read the whole first paragraph as the short description, not only its first line - Google, Numpydoc: **Breaking:** Remove customising parsing by overriding internals such as `titles_re`, `title_pattern`, `_build_meta` and `KV_REGEX`; use `SectionSpec` instead - Google, Numpydoc: **Breaking:** Split examples as doctest reads them: an example's `description` is the text before its snippet, and its `snippet` holds the `>>>` lines, their `...` continuations and the expected output; text after the last snippet is an example with no snippet diff --git a/docstring_parser/common.py b/docstring_parser/common.py index beb38f49..572b7f32 100644 --- a/docstring_parser/common.py +++ b/docstring_parser/common.py @@ -188,7 +188,8 @@ class DocstringMeta: :param arg: description :raises ValueError: if something happens - :ivar description: the item's text + :ivar description: the item's text, or ``None`` if it has none; never + an empty string :ivar kind: what the item documents. Each class sets it, so that the class and the kind cannot disagree. :ivar key: the field or section key it was written with, such as @@ -211,7 +212,7 @@ def __init__( name: T.Optional[str] = None, ) -> None: """Initialize self.""" - self.description = description + self.description = description or None self.kind = Kind.TEXT self.key = key self.name = name diff --git a/docstring_parser/epydoc.py b/docstring_parser/epydoc.py index ddc5d21e..1e87177a 100644 --- a/docstring_parser/epydoc.py +++ b/docstring_parser/epydoc.py @@ -387,15 +387,17 @@ def process_desc(desc: "T.Optional[str]", is_type: bool) -> str: if meta.is_generator else ("return", "rtype") ) - if meta.description is None: - # A type field alone keeps the name it was read from. + # A type field alone keeps the name it was read from, and is + # written for a return with a type and no description. + alone = meta.description is None and bool(meta.type_name) + if alone: type_key = _own_key(meta, type_key) else: arg_key = _own_key(meta, arg_key) if meta.type_name: text = f"@{type_key}:" + process_desc(meta.type_name, True) parts.append(text) - if meta.description is not None: + if not alone: text = f"@{arg_key}:" + process_desc(meta.description, False) parts.append(text) elif isinstance(meta, DocstringRaises): diff --git a/docstring_parser/google.py b/docstring_parser/google.py index 33b2db28..60b150ce 100644 --- a/docstring_parser/google.py +++ b/docstring_parser/google.py @@ -733,9 +733,13 @@ def process_one( head = "" if isinstance(one, DocstringRaises): - if one.type_name and one.description is None: - # A type alone reads back with no description, where a - # colon after it gives an empty one. + if ( + one.type_name + and one.description is None + and one.type_name == one.type_name.rstrip() + ): + # A type alone reads back with no description, as does one + # with a colon after it, which keeps the spaces before it. parts.append(indent + one.type_name) return if ( diff --git a/docstring_parser/rest.py b/docstring_parser/rest.py index ac3d5057..6223b999 100644 --- a/docstring_parser/rest.py +++ b/docstring_parser/rest.py @@ -673,10 +673,13 @@ def process_desc(desc: "T.Optional[str]") -> str: # own, as its value. parts.append(_attribute_directive(meta, indent)) elif meta.description is None and ( - type_name or meta.key in ("type", "vartype") + meta.key in ("type", "vartype") + or (type_name and meta.key is None) ): # A type field alone, which reads back as a param, or an - # attribute, with no description, even with no type. + # attribute, with no description, even with no type. A + # field of its own kind, such as ``:param int x:``, also + # has none. head = f"{_type_key(meta)} {meta.arg_name}" parts.append( _type_field(indent, head, type_name) diff --git a/docstring_parser/tests/cases/auto/google_01.txt b/docstring_parser/tests/cases/auto/google_01.txt index 19d00b45..d37722f2 100644 --- a/docstring_parser/tests/cases/auto/google_01.txt +++ b/docstring_parser/tests/cases/auto/google_01.txt @@ -44,7 +44,6 @@ style: google type_name: 'int' is_optional: False - DocstringParam - description: '' kind: param key: 'param' arg_name: 'yay' diff --git a/docstring_parser/tests/cases/auto/rest_01.txt b/docstring_parser/tests/cases/auto/rest_01.txt index 5b3665e1..1ce62a08 100644 --- a/docstring_parser/tests/cases/auto/rest_01.txt +++ b/docstring_parser/tests/cases/auto/rest_01.txt @@ -40,7 +40,6 @@ style: rest type_name: 'int' is_optional: False - DocstringParam - description: '' kind: param key: 'param' arg_name: 'yay' diff --git a/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.finalize_request.txt b/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.finalize_request.txt index 74fa7b14..24a13061 100644 --- a/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.finalize_request.txt +++ b/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.finalize_request.txt @@ -27,7 +27,6 @@ blank_after_short_description: True blank_after_long_description: True style: rest - DocstringMeta - description: '' kind: text key: 'internal' === detect diff --git a/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.raise_routing_exception.txt b/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.raise_routing_exception.txt index b40bde5b..1afb7ea4 100644 --- a/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.raise_routing_exception.txt +++ b/docstring_parser/tests/cases/corpus/flask/flask.app.Flask.raise_routing_exception.txt @@ -31,12 +31,10 @@ blank_after_short_description: True blank_after_long_description: True style: rest - DocstringMeta - description: '' kind: text key: 'meta' name: 'private' - DocstringMeta - description: '' kind: text key: 'internal' === detect diff --git a/docstring_parser/tests/cases/corpus/flask/flask.helpers.get_root_path.txt b/docstring_parser/tests/cases/corpus/flask/flask.helpers.get_root_path.txt index 692770b7..0d4e7d16 100644 --- a/docstring_parser/tests/cases/corpus/flask/flask.helpers.get_root_path.txt +++ b/docstring_parser/tests/cases/corpus/flask/flask.helpers.get_root_path.txt @@ -18,7 +18,6 @@ blank_after_short_description: True blank_after_long_description: True style: rest - DocstringMeta - description: '' kind: text key: 'meta' name: 'private' diff --git a/docstring_parser/tests/cases/epydoc/empty_description_01.txt b/docstring_parser/tests/cases/epydoc/empty_description_01.txt new file mode 100644 index 00000000..e29f5113 --- /dev/null +++ b/docstring_parser/tests/cases/epydoc/empty_description_01.txt @@ -0,0 +1,36 @@ +# Test that items with nothing after their colon have no description. +=== input +Short. + +@param x: +@raise ValueError: +@return: +@note: +=== parse epydoc +short_description: 'Short.' +blank_after_short_description: True +blank_after_long_description: False +style: epydoc +- DocstringParam + kind: param + key: 'param' + arg_name: 'x' + is_optional: False +- DocstringRaises + kind: raises + key: 'raise' + type_name: 'ValueError' +- DocstringReturns + kind: returns + key: 'return' + is_generator: False +- DocstringMeta + kind: text + key: 'note' +=== compose epydoc +Short. + +@param x: +@raise ValueError: +@return: +@note: diff --git a/docstring_parser/tests/cases/fork/rest_auto_attribute_01.txt b/docstring_parser/tests/cases/fork/rest_auto_attribute_01.txt index f3aa8386..ca13a745 100644 --- a/docstring_parser/tests/cases/fork/rest_auto_attribute_01.txt +++ b/docstring_parser/tests/cases/fork/rest_auto_attribute_01.txt @@ -49,7 +49,6 @@ style: rest type_name: 'int' is_optional: False - DocstringParam - description: '' kind: param key: 'param' arg_name: 'yay' diff --git a/docstring_parser/tests/cases/fork/rest_parser_attributes_02.txt b/docstring_parser/tests/cases/fork/rest_parser_attributes_02.txt index 392374e7..70abd31c 100644 --- a/docstring_parser/tests/cases/fork/rest_parser_attributes_02.txt +++ b/docstring_parser/tests/cases/fork/rest_parser_attributes_02.txt @@ -34,7 +34,6 @@ blank_after_short_description: True blank_after_long_description: True style: rest - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'attr_1' @@ -54,13 +53,11 @@ style: rest arg_name: 'attr_3' is_optional: False - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'attr_4' is_optional: False - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'attr_5' diff --git a/docstring_parser/tests/cases/fork/rest_parser_attributes_03.txt b/docstring_parser/tests/cases/fork/rest_parser_attributes_03.txt index 45071a30..e6af6cc3 100644 --- a/docstring_parser/tests/cases/fork/rest_parser_attributes_03.txt +++ b/docstring_parser/tests/cases/fork/rest_parser_attributes_03.txt @@ -41,7 +41,6 @@ blank_after_short_description: True blank_after_long_description: False style: rest - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'attr_1' @@ -79,13 +78,11 @@ style: rest type_name: 'bool' is_optional: False - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'attr_4' is_optional: False - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'attr_5' diff --git a/docstring_parser/tests/cases/google/empty_description_01.txt b/docstring_parser/tests/cases/google/empty_description_01.txt new file mode 100644 index 00000000..50f3ce02 --- /dev/null +++ b/docstring_parser/tests/cases/google/empty_description_01.txt @@ -0,0 +1,31 @@ +# Test that items with nothing after their colon have no description. +=== input +Short. + +Args: + x: + +Raises: + ValueError: +=== parse google +short_description: 'Short.' +blank_after_short_description: True +blank_after_long_description: False +style: google +- DocstringParam + kind: param + key: 'param' + arg_name: 'x' + is_optional: False +- DocstringRaises + kind: raises + key: 'raises' + type_name: 'ValueError' +=== compose google +Short. + +Args: + x: + +Raises: + ValueError diff --git a/docstring_parser/tests/cases/google/empty_example_01.txt b/docstring_parser/tests/cases/google/empty_example_01.txt index 1044559a..07e1ee25 100644 --- a/docstring_parser/tests/cases/google/empty_example_01.txt +++ b/docstring_parser/tests/cases/google/empty_example_01.txt @@ -13,7 +13,6 @@ blank_after_short_description: True blank_after_long_description: False style: google - DocstringExample - description: '' kind: examples key: 'examples' - DocstringRaises diff --git a/docstring_parser/tests/cases/google/meta_newlines_01.txt b/docstring_parser/tests/cases/google/meta_newlines_01.txt index e6b789d8..b099991c 100644 --- a/docstring_parser/tests/cases/google/meta_newlines_01.txt +++ b/docstring_parser/tests/cases/google/meta_newlines_01.txt @@ -11,7 +11,6 @@ blank_after_short_description: False blank_after_long_description: False style: google - DocstringParam - description: '' kind: param key: 'param' arg_name: 'asd' diff --git a/docstring_parser/tests/cases/google/meta_newlines_02.txt b/docstring_parser/tests/cases/google/meta_newlines_02.txt index 56d888cf..1c919c13 100644 --- a/docstring_parser/tests/cases/google/meta_newlines_02.txt +++ b/docstring_parser/tests/cases/google/meta_newlines_02.txt @@ -14,7 +14,6 @@ blank_after_short_description: False blank_after_long_description: False style: google - DocstringParam - description: '' kind: param key: 'param' arg_name: 'asd' diff --git a/docstring_parser/tests/cases/google/meta_newlines_03.txt b/docstring_parser/tests/cases/google/meta_newlines_03.txt index a41ba587..369c06f5 100644 --- a/docstring_parser/tests/cases/google/meta_newlines_03.txt +++ b/docstring_parser/tests/cases/google/meta_newlines_03.txt @@ -16,7 +16,6 @@ blank_after_short_description: False blank_after_long_description: False style: google - DocstringParam - description: '' kind: param key: 'param' arg_name: 'asd' diff --git a/docstring_parser/tests/cases/google/meta_newlines_04.txt b/docstring_parser/tests/cases/google/meta_newlines_04.txt index b2ba4c4a..5524928b 100644 --- a/docstring_parser/tests/cases/google/meta_newlines_04.txt +++ b/docstring_parser/tests/cases/google/meta_newlines_04.txt @@ -17,7 +17,6 @@ blank_after_short_description: True blank_after_long_description: False style: google - DocstringParam - description: '' kind: param key: 'param' arg_name: 'asd' diff --git a/docstring_parser/tests/cases/google/meta_newlines_05.txt b/docstring_parser/tests/cases/google/meta_newlines_05.txt index fef8c55a..c7554f9d 100644 --- a/docstring_parser/tests/cases/google/meta_newlines_05.txt +++ b/docstring_parser/tests/cases/google/meta_newlines_05.txt @@ -18,7 +18,6 @@ blank_after_short_description: True blank_after_long_description: True style: google - DocstringParam - description: '' kind: param key: 'param' arg_name: 'asd' diff --git a/docstring_parser/tests/cases/google/meta_newlines_06.txt b/docstring_parser/tests/cases/google/meta_newlines_06.txt index 4725eb93..851b3e3a 100644 --- a/docstring_parser/tests/cases/google/meta_newlines_06.txt +++ b/docstring_parser/tests/cases/google/meta_newlines_06.txt @@ -9,7 +9,6 @@ blank_after_short_description: False blank_after_long_description: False style: google - DocstringParam - description: '' kind: param key: 'param' arg_name: 'asd' diff --git a/docstring_parser/tests/cases/numpydoc/empty_description_01.txt b/docstring_parser/tests/cases/numpydoc/empty_description_01.txt new file mode 100644 index 00000000..55696285 --- /dev/null +++ b/docstring_parser/tests/cases/numpydoc/empty_description_01.txt @@ -0,0 +1,36 @@ +# Test that items with nothing under them have no description. +=== input +Short. + +Parameters +---------- +x : int + +Raises +------ +ValueError +=== parse numpydoc +short_description: 'Short.' +blank_after_short_description: True +blank_after_long_description: False +style: numpydoc +- DocstringParam + kind: param + key: 'param' + arg_name: 'x' + type_name: 'int' + is_optional: False +- DocstringRaises + kind: raises + key: 'raises' + type_name: 'ValueError' +=== compose numpydoc +Short. + +Parameters +---------- +x : int + +Raises +------ +ValueError diff --git a/docstring_parser/tests/cases/rest/attribute_directive_02.txt b/docstring_parser/tests/cases/rest/attribute_directive_02.txt index 01f057f8..ac8468c5 100644 --- a/docstring_parser/tests/cases/rest/attribute_directive_02.txt +++ b/docstring_parser/tests/cases/rest/attribute_directive_02.txt @@ -41,7 +41,6 @@ style: rest arg_name: 'a' is_optional: False - DocstringParam - description: '' kind: attribute key: 'attribute' arg_name: 'y' diff --git a/docstring_parser/tests/cases/rest/empty_description_01.txt b/docstring_parser/tests/cases/rest/empty_description_01.txt new file mode 100644 index 00000000..f7d49c64 --- /dev/null +++ b/docstring_parser/tests/cases/rest/empty_description_01.txt @@ -0,0 +1,36 @@ +# Test that items with nothing after their colon have no description. +=== input +Short. + +:param x: +:raises ValueError: +:returns: +:note: +=== parse rest +short_description: 'Short.' +blank_after_short_description: True +blank_after_long_description: False +style: rest +- DocstringParam + kind: param + key: 'param' + arg_name: 'x' + is_optional: False +- DocstringRaises + kind: raises + key: 'raises' + type_name: 'ValueError' +- DocstringReturns + kind: returns + key: 'returns' + is_generator: False +- DocstringMeta + kind: text + key: 'note' +=== compose rest +Short. + +:param x: +:raises ValueError : +:returns: +:note: