Does Python have a built-in function for unindenting a multiline string?
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 @
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])
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())