]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-150285: Wrap long single-line summary in text output in pydoc (GH-151081)
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 10 Jun 2026 10:44:35 +0000 (13:44 +0300)
committerGitHub <noreply@github.com>
Wed, 10 Jun 2026 10:44:35 +0000 (13:44 +0300)
Lib/pydoc.py
Lib/test/test_pydoc/longsummary.py [new file with mode: 0644]
Lib/test/test_pydoc/test_pydoc.py
Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst [new file with mode: 0644]

index 497cc7d90a42456d1d3dbe5f58d03e809aed62a9..ca4eb1001981681e0982f75648a1a48e4bb95a5c 100644 (file)
@@ -1240,6 +1240,17 @@ class TextDoc(Doc):
         lines = [(prefix + line).rstrip() for line in text.split('\n')]
         return '\n'.join(lines)
 
+    def _format_doc(self, text, width=68):
+        """Wraps the single-line summary if it is too long."""
+        if not text: return ''
+        lines = text.split('\n', 2)
+        if len(lines) > 1 and lines[1]:
+            return text
+        lines[:1] = textwrap.wrap(lines[0], width,
+                                  break_long_words=False,
+                                  break_on_hyphens=False)
+        return '\n'.join(lines)
+
     def section(self, title, contents):
         """Format a section with a given heading."""
         clean_contents = self.indent(contents).rstrip()
@@ -1390,6 +1401,7 @@ location listed above.
 
         doc = getdoc(object)
         if doc:
+            doc = self._format_doc(doc)
             push(doc + '\n')
 
         # List the mro, if non-trivial.
@@ -1590,6 +1602,7 @@ location listed above.
             return decl + '\n'
         else:
             doc = getdoc(object) or ''
+            doc = self._format_doc(doc)
             return decl + '\n' + (doc and self.indent(doc).rstrip() + '\n')
 
     def docdata(self, object, name=None, mod=None, cl=None, *ignored):
@@ -1602,6 +1615,7 @@ location listed above.
             push('\n')
         doc = getdoc(object) or ''
         if doc:
+            doc = self._format_doc(doc)
             push(self.indent(doc))
             push('\n')
         return ''.join(results)
@@ -1620,7 +1634,8 @@ location listed above.
         if not doc:
             doc = getdoc(object)
         if doc:
-            line += '\n' + self.indent(str(doc)) + '\n'
+            doc = self._format_doc(str(doc))
+            line += '\n' + self.indent(doc) + '\n'
         return line
 
 class _PlainTextDoc(TextDoc):
diff --git a/Lib/test/test_pydoc/longsummary.py b/Lib/test/test_pydoc/longsummary.py
new file mode 100644 (file)
index 0000000..0e566d3
--- /dev/null
@@ -0,0 +1,29 @@
+class C:
+    """This is a class summary that consists of a very long single line, exceeding the recommended PEP 8 limit.
+
+    The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+    """
+    def meth(self):
+        """This is a method summary that consists of a very long single line, exceeding the recommended PEP 8 limit.
+
+        The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+        """
+
+    @property
+    def prop(self):
+        """This is a property summary that consists of a very long single line, exceeding the recommended PEP 8 limit.
+
+        The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+        """
+
+def func(self):
+    """This is a function summary that consists of a very long single line, exceeding the recommended PEP 8 limit.
+
+    The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+    """
+
+data = C()
+data.__doc__ = """This is a data summary that consists of a very long single line, exceeding the recommended PEP 8 limit.
+
+The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+"""
index 5cd26923f75c311e0fda2493d428a0aebcb5d71e..5543c664528e6c846a776fc851dfc157157113bd 100644 (file)
@@ -1245,6 +1245,71 @@ function_with_really_long_name_so_annotations_can_be_rather_small(
 <lambda> lambda very_long_parameter_name_that_should_not_fit_into_a_single_line, second_very_long_parameter_name
 ''' % __name__)
 
+    @requires_docstrings
+    def test_long_summaries(self):
+        from . import longsummary
+        doc = pydoc.render_doc(longsummary)
+        doc = clean_text(doc)
+        self.assertEqual(doc, '''Python Library Documentation: module test.test_pydoc.longsummary in test.test_pydoc
+
+NAME
+    test.test_pydoc.longsummary
+
+CLASSES
+    builtins.object
+        C
+
+    class C(builtins.object)
+     |  This is a class summary that consists of a very long single line,
+     |  exceeding the recommended PEP 8 limit.
+     |
+     |  The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+     |
+     |  Methods defined here:
+     |
+     |  meth(self)
+     |      This is a method summary that consists of a very long single line,
+     |      exceeding the recommended PEP 8 limit.
+     |
+     |      The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+     |
+     |  ----------------------------------------------------------------------
+     |  Readonly properties defined here:
+     |
+     |  prop
+     |      This is a property summary that consists of a very long single line,
+     |      exceeding the recommended PEP 8 limit.
+     |
+     |      The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+     |
+     |  ----------------------------------------------------------------------
+     |  Data descriptors defined here:
+     |
+     |  __dict__
+     |      dictionary for instance variables
+     |
+     |  __weakref__
+     |      list of weak references to the object
+
+FUNCTIONS
+    func(self)
+        This is a function summary that consists of a very long single line,
+        exceeding the recommended PEP 8 limit.
+
+        The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+
+DATA
+    data = <test.test_pydoc.longsummary.C object>
+        This is a data summary that consists of a very long single line,
+        exceeding the recommended PEP 8 limit.
+
+        The rest of the docstring body, separated from the summary by a blank line, can also contain very long lines.
+
+FILE
+    %s
+
+''' % inspect.getabsfile(longsummary))
+
     def test__future__imports(self):
         # __future__ features are excluded from module help,
         # except when it's the __future__ module itself
diff --git a/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst b/Misc/NEWS.d/next/Library/2026-06-08-15-04-35.gh-issue-150285.-Tj94n.rst
new file mode 100644 (file)
index 0000000..60b1f3e
--- /dev/null
@@ -0,0 +1 @@
+:mod:`pydoc` now wraps long single-line summary in text output.