diff mercurial/pycompat.py @ 52113:31076a2301f1

py-3-13: stabilize the docstring output across all supported Python versions Python 3.13 now trims indents from docstrings at compilation time (to save space in .pyc), so all of our helptext is affected. The indentation has never served a user-facing purpose and was more here because nobody cared enough to remove it: we gain some screen space this way. Rather than undo the transformation (which isn't really possible since the transform also deletes leading/trailing whitespace), we align the behavior of older Python versions with that of 3.13. Unfortunately, this means breaking some of the translations. I've only touched the ones that need to work for some tooling tests to pass, but I do not have the time to fix the rest of them across all languages, since they cannot be done in an automated way. i18n updates have been basically abandonned for a good while now, hopefully someone cares enough to bring them back.
author Rapha?l Gom?s <rgomes@octobus.net>
date Thu, 24 Oct 2024 15:23:52 +0200
parents f4733654f144
children 19ae7730636a
line wrap: on
line diff
--- a/mercurial/pycompat.py	Tue Oct 15 22:30:10 2024 -0400
+++ b/mercurial/pycompat.py	Thu Oct 24 15:23:52 2024 +0200
@@ -349,12 +349,46 @@
     raise exc.with_traceback(tb)
 
 
+# Copied over from the 3.13 Python stdlib `inspect.cleandoc`, with a couple
+# of removals explained inline.
+# It differs slightly from the 3.8+ version, so it's better to use the same
+# version to remove any potential for variation.
+def cleandoc(doc):
+    """Clean up indentation from docstrings.
+
+    Any whitespace that can be uniformly removed from the second line
+    onwards is removed."""
+    lines = doc.expandtabs().split('\n')
+
+    # Find minimum indentation of any non-blank lines after first line.
+    margin = sys.maxsize
+    for line in lines[1:]:
+        content = len(line.lstrip(' '))
+        if content:
+            indent = len(line) - content
+            margin = min(margin, indent)
+    # Remove indentation.
+    if lines:
+        lines[0] = lines[0].lstrip(' ')
+    if margin < sys.maxsize:
+        for i in range(1, len(lines)):
+            lines[i] = lines[i][margin:]
+    # Here the upstream *Python* version does newline trimming, but it looks
+    # like the compiler (written in C) does not, so go with what the compiler
+    # does.
+    return '\n'.join(lines)
+
+
 def getdoc(obj: object) -> Optional[bytes]:
     """Get docstring as bytes; may be None so gettext() won't confuse it
     with _('')"""
     doc = builtins.getattr(obj, '__doc__', None)
     if doc is None:
         return doc
+    if sys.version_info < (3, 13):
+        # Python 3.13+ "cleans up" the docstring at compile time, let's
+        # normalize this behavior for previous versions
+        doc = cleandoc(doc)
     return sysbytes(doc)