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 line
  • subsequent_indent — prefix for continuation lines
  • break_long_words — whether to break words exceeding width (default True)
  • break_on_hyphens — whether to break at hyphens (default True)
  • max_lines — limit output lines, appending placeholder to the last
  • tabsize — 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.

pythonstandard-librarytext-processing

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.