From bf3de9d82b2022c9afc6434cbce51f318ab087ba Mon Sep 17 00:00:00 2001 From: Dash Date: Sat, 7 Nov 2026 10:43:37 +0100 Subject: [PATCH] attrdoc: clean attribute docstrings parse_from_object() took an attribute's docstring, written below it in the class body, as it stood in the source, with its indent and the blank lines around it. It is now cleaned as a docstring is. --- CHANGELOG.md | 1 + docstring_parser/attrdoc.py | 4 +++- docstring_parser/tests/test_parse_from_object.py | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be0876d2..201deabc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - General: Keep the indent of the long description's first line - General: Compose a param's type as written, such as `int, optional` - General: Compose docstrings so they read back the same, splitting lines at newlines only +- General: Clean attribute docstrings in `parse_from_object()` as other docstrings - ReST: Stop the last field from absorbing text after the field list; that text now goes to the long description (thanks to @apoorvdarshan) - ReST: Raise `ParseError` instead of `IndexError` for a field with no name - ReST: Indent composed continuation lines in compact mode, so one starting with a role such as `:class:` is no longer read back as a new field diff --git a/docstring_parser/attrdoc.py b/docstring_parser/attrdoc.py index 473c28ed..a65837e0 100644 --- a/docstring_parser/attrdoc.py +++ b/docstring_parser/attrdoc.py @@ -9,6 +9,7 @@ import typing as T from types import ModuleType +from ._lines import cleandoc from .common import Docstring, DocstringParam, Kind @@ -120,7 +121,8 @@ def add_attribute_docstrings( if arg_name not in documented: param = DocstringParam( key="attribute", - description=description, + # Cleaned as a docstring is, without its indent. + description=cleandoc(description) if description else None, arg_name=arg_name, type_name=type_name, is_optional=default is not None, diff --git a/docstring_parser/tests/test_parse_from_object.py b/docstring_parser/tests/test_parse_from_object.py index 3e395ffe..d2debb1e 100644 --- a/docstring_parser/tests/test_parse_from_object.py +++ b/docstring_parser/tests/test_parse_from_object.py @@ -182,3 +182,19 @@ def test_from_object_with_descriptor_doc() -> None: assert docstring.short_description is None assert len(docstring.meta) == 0 + + +def test_attribute_docstring_is_cleaned() -> None: + """Test that an attribute's docstring is cleaned as a docstring is.""" + + class WithAttr: + """Class docstring.""" + + value: int = 1 + """ + Indented + attribute doc. + """ + + docstring = parse_from_object(WithAttr) + assert docstring.attrs[0].description == "Indented\n attribute doc."