Does Python have a built-in function for unindenting a multiline string?

Question:

Say I have the string

s = """
    Controller = require 'controller'

    class foo
        view: 'baz'
        class: 'bar'

        constructor: ->
            Controller.mix @
"""

Every line in the string now has a global 4 space indentation. If this string was declared inside a function, it would have a 8 space global indentation, etc.

Does Python have a function for removing the global left indentation of string?

I would like that function output to be:

Controller = require 'controller'

class foo
    view: 'baz'
    class: 'bar'

    constructor: ->
        Controller.mix @"
Asked By: Hubro

||

Answers:

Not a built-in function, but a function in the standard library: textwrap.dedent()

>>> print(textwrap.dedent(s))

Controller = require 'controller'

class foo
    view: 'baz'
    class: 'bar'

    constructor: ->
        Controller.mix @
Answered By: Sven Marnach

textwrap.dedent() is close to what you want, but it does not implement what you asked for, because it has a leading newline. You can either wrap dedent in a function that strips the leading newline from s:

def my_dedent(string):
    if string and string[0] == 'n':
        string = string[1:]
    return textwrap.dedent(string)

However textwrap.dedent() handles lines with just whitespace in special way that is OK if you are generating Python source from an indent multiline statement, where trailing whitespace is insignificant.

But in general it is inappropriate that textwrap.dedent() removes extra whitespace from lines with more whitespace than the ‘maximum indent’, removes whitespace from all whitespace lines and that it descards any whitespace before the closing """, especially since this behaviour is undocumented and done with non-transparent regular expressions.

Since I also generate non-Python source code where spaces are often significant I use the following routine. It doesn’t handle TAB indentation, but it does give you the output you asked without leading newline, where textwrap.dedent() fails.

def remove_leading_spaces(s, strict=False):
    '''Remove the maximum common spaces from all non-empty lines in string

Typically used to remove leading spaces from all non-empty lines in a
multiline string, preserving all extra spaces.
A leading newline (when not useing '"""') is removed unless the strict
argument is True.

Note that if you want two spaces on the last line of the return value 
without a newline, you have to use the max indentation + 2 spaces before 
the closing """. If you just input 2 spaces that is likely to be the 
maximum indent.
    '''
    if s and not strict and s[0] == 'n':
        s = s[1:]
    lines = s.splitlines(True) # keep ends
    max_spaces = -1
    for line in lines:
        if line != 'n':
            for idx, c in enumerate(line[:max_spaces]):
                if not c == ' ':
                    break
            max_spaces = idx + 1
    return ''.join([l if l == 'n' else l[max_spaces-1:] for l in lines])
Answered By: Anthon

I know this question has already been answered but there’s also this way:

import inspect

def test():
    t = """
    some text
    """

    return inspect.cleandoc(t)

print(test())
Answered By: iSplasher

I was able to do this with a carriage return:

s = """
    r Controller = require 'controller'
    r
    rclass foo
    r    view: 'baz'
    r    class: 'bar'
    r
    r    constructor: ->
    r        Controller.mix @
    r"""
Answered By: Erik Goepfert
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.