Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 @"
like image 387
Hubro Avatar asked Jun 22 '12 13:06

Hubro


People also ask

How does Python handle multiline strings?

In Python, you have different ways to specify a multiline string. You can have a string split across multiple lines by enclosing it in triple quotes. Alternatively, brackets can also be used to spread a string into different lines. Moreover, backslash works as a line continuation character in Python.

Which character is used to create multi line string in Python with or?

Python uses triple quotes when using multi-line strings and when it contains newline characters.

How do you remove an indent from a string in Python?

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.


3 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 @
like image 111
Sven Marnach Avatar answered Oct 08 '22 08:10

Sven Marnach


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())
like image 44
iSplasher Avatar answered Oct 08 '22 08:10

iSplasher


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])
like image 10
Anthon Avatar answered Oct 08 '22 10:10

Anthon