Unindent multiline strings in Python with dedent

Share
Copied to clipboard.
Series: Strings
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
4 min. read 3 min. video Python 3.8—3.12

Let's talk about how to create a multi-line string in Python without accidentally indenting the text within that string.

Manually dedenting multi-line strings

Here we have a function that prints out a copyright statement:

def copyright():
    print("""\
        Copyright (c) 1991-2000 ACME Corp
        All Rights Reserved.

        Copyright (c) 2000-2030 Cyberdyne
        All Rights Reserved."""
    )

This function works, but the copyright statement that it prints out is indented:

>>> copyright()
        Copyright (c) 1991-2000 ACME Corp
        All Rights Reserved.

        Copyright (c) 2000-2030 Cyberdyne
        All Rights Reserved.

Each line in this copyright statement begins with eight spaces. This happens because in our code, the text within our string begins with eight spaces before each line.

We can fix this problem by manually dedenting the text within this string:

def copyright():
    print("""\
Copyright (c) 1991-2000 ACME Corp
All Rights Reserved.

Copyright (c) 2000-2030 Cyberdyne
All Rights Reserved."""
    )

While this does work:

>>> copyright()
Copyright (c) 1991-2000 ACME Corp
All Rights Reserved.

Copyright (c) 2000-2030 Cyberdyne
All Rights Reserved.

This also makes our code a bit tricky to read.

Our code is less readable than before because up here, we our code suddenly dedents in the middle of our string.

We could fix this problem in by using the dedent function in Python's textwrap module.

Using textwrap.dedent to unindent strings

The dedent function allows us to indent our code however we'd like.

Our multi-line string can be nicely indented in our code because the dedent function will will dedent it for us:

from textwrap import dedent

def copyright():
    print(dedent("""\
        Copyright (c) 1991-2000 ACME Corp
        All Rights Reserved.

        Copyright (c) 2000-2030 Cyberdyne
        All Rights Reserved."""
    ))

At the point where we use this string, we'll see that it doesn't have any indentation:

>>> copyright()
Copyright (c) 1991-2000 ACME Corp
All Rights Reserved.

Copyright (c) 2000-2030 Cyberdyne
All Rights Reserved.

The dedent function removed the indentation for us.

Mind your newlines

Note that our string starts with a backslash (\):

from textwrap import dedent

def copyright():
    print(dedent("""\
        ...
    ))

That backslash removes the extra newline character (\n) that this string would start with if this backslash weren't here. Without that backslash, we would need to start our text on the same line to avoid that newline character:

from textwrap import dedent

def copyright():
    print(dedent("""Copyright (c) 1991-2000 ACME Corp
        ...

Note that we're also ending our multi-line string on the same line that our text ends:

from textwrap import dedent

def copyright():
    print(dedent("""\
        ...
        All Rights Reserved."""
    ))

It would be nice if we could end it on the next line instead, but that would add an extra newline at the end of our string.

I prefer to combine dedent with the string strip method to take care of these newlines.

Combining dedent with strip to make the code more readable

Here we're using the strip method with our string:

from textwrap import dedent

def copyright():
    print(dedent("""
        Copyright (c) 1991-2000 ACME Corp
        All Rights Reserved.

        Copyright (c) 2000-2030 Cyberdyne
        All Rights Reserved.
    """).strip("\n"))

The dedent function is dedenting a string that starts with a newline character and ends with a newline character (note that we end our multi-line string on the next line). After we dedent, we then use the string strip method to remove those newline characters.

Our copyright statement looks as it should:

>>> copyright()
Copyright (c) 1991-2000 ACME Corp
All Rights Reserved.

Copyright (c) 2000-2030 Cyberdyne
All Rights Reserved.

And we have nicely indented code that doesn't have a strange backslash. Plus we don't need to worry about where exactly our multi-line string ends in our code: we're ending our string on the new line and that's okay!

dedent maintains relative indentation levels

It's important to note that the dedent function doesn't remove all whitespace from the beginning of each line. It's a little bit smarter than that. The dedent function maintains relative indentation within a string.

Here we have a string that is expected to have some lines indented more than others:

from textwrap import dedent

example_list = dedent("""
    - Fix leap year bug
        - User registration breaks on leap years
        - Write a regression test
    - Record a screencast
""").strip("\n")

print("The list.txt file should show a bulleted list, like this:")
print(example_list)

You can see that every line has at least four spaces of indentation, but some lines have more indentation.

When we run dedent against this string, you'll see the four spaces of indentation (that's common to each line) is removed:

$ python3 example_list.py
The list.txt file should show a bulleted list, like this:
- Fix leap year bug
    - User registration breaks on leap years
    - Write a regression test
- Record a screencast

But the indentation that some lines have (that is relatively greater than other lines) is maintained.

Use textwrap.dedent to unindent strings

If you'd like to nicely format your multi-line string within your Python code without printing indented text by mistake, you can use the dedent function from Python's textwrap module.

Try out dedent now.

Series: Strings

Regardless of what you're doing in Python, you almost certainly use strings all the time. A string is usually the default tool we reach for when we don't have a more specific way to represent our data.

To track your progress on this Python Morsels topic trail, sign in or sign up.

0%
A Python Tip Every Week

Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.