Wrapping text output in Python

Share
Copied to clipboard.
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
4 min. read 3 min. video Python 3.10—3.14

How can you wrap text to a specific width in Python?

Improving readability with text wrapping

Sometimes programmers like to manually wrap their text to a specific maximum line length.

This is unwrapped free-flowing text (made with a multiline string):

license = """
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
""".strip()

print(license)

When we print this text at the terminal, it will something like this:

$ python3 license.py
Permission is hereby granted, free of charge, to any person obtaining a copy of this softwar
e and associated documentation files (the "Software"), to deal in the Software without restr
iction, including without limitation the rights to use, copy, modify, merge, publish, distri
bute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Soft
ware is furnished to do so, subject to the following conditions:

Let's manually wrap this text to a 78-character line length:

license = """
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
""".strip()

print(license)

When we print this text, we'll now see that it's a bit easier to read in our terminal:

$ python3 license.py
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

How can we do this text wrapping automatically?

Wrapping text to a fixed width with textwrap.wrap

Python's textwrap module has a wrap function for wrapping text. If we pass our text to the wrap function, it will wrap our text to a maximum line length of 70 characters:

>>> from textwrap import wrap
>>> for line in wrap(license):
...     print(line)
...
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

To customize the length that we're wrapping to, we can pass the width keyword argument:

>>> from textwrap import wrap
>>> for line in wrap(license, width=78):
...     print(line)
...
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

Now we're wrapping to 78 characters.

Rendering wrapped text as a single string with textwrap.fill

Note that the wrap function returns a list of lines without line breaks at the end of each line:

>>> wrap(license, width=78)
['Permission is hereby granted, free of charge, to any person obtaining a copy', 'of this software and associated documentation files (the "Software"), to deal', 'in the Software without restriction, including without limitation the rights', 'to use, copy, modify, merge, publish, distribute, sublicense, and/or sell', 'copies of the Software, and to permit persons to whom the Software is', 'furnished to do so, subject to the following conditions:']

We've been looping over this list and printing out each line individually.

We could instead use the string join method to join these lines together with newline characters:

>>> wrapped = "\n".join(wrap(license, width=78))
>>> print(wrapped)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

Or we could use the textwrap.fill function, which returns a string with all the lines joined together automatically:

>>> from textwrap import fill
>>> wrapped = fill(license, width=78)
>>> print(wrapped)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The fill function accepts all the same arguments that wrap accepts.

Wrapping text with multiple paragraphs

Note that the text we've been wrapping contains just a single paragraph. The utilities in the textwrap module are meant to operate on text that represents just one paragraph.

But what if our text contains multiple paragraphs?

license = """
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
""".strip()

Assuming the line endings in our text have been normalized, we could split this text by two consecutive new line characters to get paragraphs:

>>> paragraphs = license.split("\n\n")

Or if we know that each line will either be a full paragraph or an empty line, we could just split our text into lines:

>>> paragraphs = license.splitlines()

Then we could run fill on each of these paragraphs and then join them back together with newline characters:

>>> wrapped = "\n".join([fill(p, width=78) for p in paragraphs])

Here's a version of our code from before that wraps multiple paragraphs of text by using the splitlines method and the textwrap.fill function:

from textwrap import fill

license = """
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
""".strip()

for paragraph in license.splitlines():
    print(fill(paragraph, width=78))

If we run this code, we'll see that it indeed wrapped all three paragraphs in our text:

$ python license.py
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Indentation, line breaks, and more

The wrap and fill functions both rely on the textwrap module's TextWrapper class.

If we ask for help on the fill or wrap functions from the textwrap module, we'll see a reference to the TextWrapper class:

>>> from textwrap import fill
>>> help(fill)
Help on function fill in module textwrap:

fill(text, width=70, **kwargs)
    Fill a single paragraph of text, returning a new string.

    Reformat the single paragraph in 'text' to fit in lines of no more
    than 'width' columns, and return a new string containing the entire
    wrapped paragraph.  As with wrap(), tabs are expanded and other
    whitespace characters converted to space.  See TextWrapper class for
    available keyword args to customize wrapping behaviour.
~
 Help on fill line 1/10 (END) (press h for help or q to quit)

Any keyword argument that can be passed to the textwrap.TextWrapper class can also be passed to fill or wrap.

The TextWrapper class allows for customizing indentation, where lines are broken when wrapping, whitespace normalization, and more. You can check out the documentation for all the features that this TextWrapper class supports.

Text wrapping with TextWrapper class

The next time you need to hard-wrap text in Python, check out the textwrap module's wrap and fill functions.

A Python Tip Every Week

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