Python textwrap — Core Concepts
Why textwrap exists
Terminal output, log messages, CLI help text, email bodies — anywhere text has a width constraint, textwrap handles the formatting. It’s been in the standard library since Python 2.3 and remains the go-to for text-width control.
The five core functions
textwrap.wrap(text, width=70)
Returns a list of strings, each at most width characters. Breaks happen at whitespace boundaries.
import textwrap
lines = textwrap.wrap("The quick brown fox jumps over the lazy dog near the riverbank", width=30)
# ['The quick brown fox jumps', 'over the lazy dog near the', 'riverbank']
textwrap.fill(text, width=70)
Same as wrap, but returns a single string with \n between lines. This is what you use most of the time for display.
print(textwrap.fill(long_paragraph, width=60))
textwrap.shorten(text, width, placeholder='...')
Truncates text to fit within width, collapsing whitespace and appending a placeholder:
textwrap.shorten("Hello world, this is a long sentence", width=25)
# 'Hello world, this [...]'
textwrap.shorten("Hello world, this is long", width=20, placeholder="…")
# 'Hello world, this…'
textwrap.dedent(text)
Removes common leading whitespace from every line. Essential when you embed multi-line strings inside indented functions:
def usage():
msg = textwrap.dedent("""\
Usage: tool [OPTIONS]
-h Show help
-v Verbose output
""")
print(msg)
Without dedent, that string would have 8 spaces of unwanted indentation.
textwrap.indent(text, prefix)
Adds a prefix to the beginning of selected lines (by default, all non-empty lines):
textwrap.indent("line one\nline two\n\nline four", " > ")
# ' > line one\n > line two\n\n > line four'
You can pass a predicate function to control which lines get the prefix.
The TextWrapper class
For repeated formatting with the same settings, instantiate TextWrapper:
wrapper = textwrap.TextWrapper(
width=50,
initial_indent=" • ",
subsequent_indent=" ",
)
print(wrapper.fill("A very long bullet point that needs to wrap across multiple lines"))
Key attributes:
initial_indent— prefix for the first linesubsequent_indent— prefix for continuation linesbreak_long_words— whether to break words exceeding width (defaultTrue)break_on_hyphens— whether to break at hyphens (defaultTrue)max_lines— limit output lines, appending placeholder to the lasttabsize— how many spaces a tab equals (default 8)
Common misconception
textwrap doesn’t handle rich text, ANSI color codes, or Unicode full-width characters correctly out of the box. It counts characters, not display width. If your text has color escape sequences or CJK characters (which are typically 2 columns wide), the wrapping will be off. For those cases, you’ll need libraries like wcwidth or rich.
One thing to remember
For everyday text formatting in terminals and logs, textwrap’s five functions cover almost every need — wrap for lists, fill for display, shorten for truncation, dedent for cleanup, and indent for quoting.
See Also
- Python Atexit How Python's atexit module lets your program clean up after itself right before it shuts down.
- Python Bisect Sorted Lists How Python's bisect module finds things in sorted lists the way you'd find a word in a dictionary — by jumping to the middle.
- Python Contextlib How Python's contextlib module makes the 'with' statement work for anything, not just files.
- Python Copy Module Why copying data in Python isn't as simple as it sounds, and how the copy module prevents sneaky bugs.
- Python Dataclass Field Metadata How Python dataclass fields can carry hidden notes — like sticky notes on a filing cabinet that tools read automatically.