0

In Python, it is possible to extract the docstring of a function:

class MyClass:
    def my_method(self):
        """This is the help message,
        on multiple lines.
        """
        pass

print(MyClass.my_method.__doc__)

The result is:

This is the help message,
        on multiple lines.

What is annoying is that it results in many meaningless spaces: the whitespaces exist to keep the code clean, but are not meant to be part of the message.

Is there a way to get rid of them? Note that I would like to remove only the meaningless ones. So, calls to lstrip or equivalent won't work.

If there are meaningful spaces, I would like to keep them:

class MyClass2:
    def my_method(self):
        """This is the help message,
        on multiple lines.
            This one is intentionally more indented.
        """
        pass

print(MyClass2.my_method.__doc__)

Desired result:

This is the help message,
on multiple lines.
    This one is intentionally more indented.
Codoscope
  • 892
  • 1
  • 10
  • 18
  • Can't you just split on new lines, strip and join? – OneMadGypsy Sep 29 '22 at 14:44
  • Does this answer your question? [PEP 257 docstring trim in standard library?](https://stackoverflow.com/questions/16534246/pep-257-docstring-trim-in-standard-library) – ThePyGuy Sep 29 '22 at 15:42
  • 1
    @ThePyGuy Yes, actually, this is what I have already written in my answer. – Codoscope Sep 29 '22 at 15:44
  • @Codoscope That's an autogenerated comment for marking as duplicate. You can ignore that comment. And also, you can mark your own answer as accepted as it resolves your issue. – ThePyGuy Sep 29 '22 at 15:45
  • I see, I did not know that. Both posts have the same solution, but the questions are not really identical. Should it still be considered as duplicate? – Codoscope Sep 29 '22 at 15:50

2 Answers2

2

Taking code snippet from PEP-257: Handling Docstring Indentation

def trim(docstring):
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxsize
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxsize:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)

OUTPUT:

# Class 1
>>> print(trim(MyClass.my_method.__doc__))
This is the help message,
on multiple lines.

# Class 2
>>> print(trim(MyClass2.my_method.__doc__))
This is the help message,
on multiple lines.
    This one is intentionally more indented.
ThePyGuy
  • 17,779
  • 5
  • 18
  • 45
  • 1
    Thanks! I have found a solution in the `inspect` module thanks to your answer. – Codoscope Sep 29 '22 at 14:55
  • @Codoscope I added that too in the answer so that it'll be helpful for future readers. – ThePyGuy Sep 29 '22 at 15:15
  • Isn't this practice discouraged on SO? It seems to me that in general, people avoid to unnecessarily copy other replies; and the reply that people like the most will automatically appear at the top. – Codoscope Sep 29 '22 at 15:36
  • @Codoscope actually I did not see that you have added that as an answer so I thought to include it in the current answer as it was just in the comment, nevertheless, I was actually using `getdoc` instead of `cleandoc` , anyways, I've rolled back to the previous answer, and since you've already found the same question in SO, its just better to mark it as duplicate. – ThePyGuy Sep 29 '22 at 15:40
  • 1
    ok, that makes sense if you didn't see it. – Codoscope Sep 29 '22 at 15:44
1

Thanks to ThePyGuy's answer, I have found a related post that I could not find before, and that leads to the following solution:

inspect.cleandoc(MyClass2.my_method.__doc__)
Codoscope
  • 892
  • 1
  • 10
  • 18
  • 1
    In fact, instead of using `cleandoc` passing `__doc__` , you can directly pass class/method/function to `getdoc` from `inspect` i.e. `print(inspect.getdoc(MyClass2.my_method))` – ThePyGuy Sep 29 '22 at 15:48
  • 1
    That's useful addition, I hadn't noticed the difference. – Codoscope Sep 29 '22 at 15:52