The textwrap module handles text formatting—wrapping, indenting, and cleaning up whitespace. Essential for CLI output, documentation generators, and email formatting.

Basic Wrapping

import textwrap
 
text = "This is a very long line that needs to be wrapped to fit within a certain width for better readability."
 
# Wrap to 40 characters
wrapped = textwrap.wrap(text, width=40)
print(wrapped)
# ['This is a very long line that needs to',
#  'be wrapped to fit within a certain',
#  'width for better readability.']
 
# Join with newlines
print(textwrap.fill(text, width=40))
# This is a very long line that needs to
# be wrapped to fit within a certain
# width for better readability.

Shorten Text

Truncate with ellipsis:

import textwrap
 
long_text = "This is a very long description that should be shortened for display in a compact space."
 
short = textwrap.shorten(long_text, width=40, placeholder="...")
print(short)
# 'This is a very long description...'
 
# Custom placeholder
short = textwrap.shorten(long_text, width=40, placeholder=" [more]")
print(short)
# 'This is a very long description [more]'

Indentation

import textwrap
 
text = """First line
Second line
Third line"""
 
# Add indent to all lines
indented = textwrap.indent(text, prefix='    ')
print(indented)
#     First line
#     Second line
#     Third line
 
# Conditional indent
def not_empty(line):
    return line.strip() != ''
 
indented = textwrap.indent(text, prefix='> ', predicate=not_empty)

Dedenting

Remove common leading whitespace:

import textwrap
 
code = """
    def hello():
        print("world")
        return True
"""
 
# Remove common indentation
dedented = textwrap.dedent(code)
print(dedented)
# 
# def hello():
#     print("world")
#     return True
 
# Clean and dedent
clean = textwrap.dedent(code).strip()

TextWrapper for Reuse

import textwrap
 
# Create reusable wrapper
wrapper = textwrap.TextWrapper(
    width=60,
    initial_indent='  ',        # First line indent
    subsequent_indent='    ',   # Following lines indent
    break_long_words=True,
    break_on_hyphens=True,
)
 
paragraphs = [
    "This is the first paragraph with some content.",
    "This is the second paragraph with different content.",
]
 
for para in paragraphs:
    print(wrapper.fill(para))
    print()

Terminal Output Formatting

import textwrap
import shutil
 
def print_wrapped(text: str, indent: int = 0):
    """Print text wrapped to terminal width."""
    width = shutil.get_terminal_size().columns - indent
    prefix = ' ' * indent
    
    wrapped = textwrap.fill(text, width=width)
    indented = textwrap.indent(wrapped, prefix)
    print(indented)
 
# Usage
print_wrapped("This is a very long message that will automatically wrap to fit your terminal window size.", indent=4)

Help Text Formatting

import textwrap
 
def format_help(commands: dict, width: int = 80) -> str:
    """Format command help text."""
    lines = []
    
    for cmd, description in commands.items():
        # Wrap description with hanging indent
        wrapper = textwrap.TextWrapper(
            width=width,
            initial_indent=f"  {cmd:12} ",
            subsequent_indent=' ' * 16,
        )
        lines.append(wrapper.fill(description))
    
    return '\n'.join(lines)
 
commands = {
    'init': 'Initialize a new project in the current directory with default settings',
    'build': 'Build the project, compiling all source files and generating output',
    'deploy': 'Deploy the built project to the configured remote server',
}
 
print(format_help(commands))
#   init         Initialize a new project in the current
#                directory with default settings
#   build        Build the project, compiling all source
#                files and generating output
#   deploy       Deploy the built project to the configured
#                remote server

Email Formatting

import textwrap
 
def format_email_body(paragraphs: list[str], width: int = 72) -> str:
    """Format email body with proper wrapping."""
    formatted = []
    
    for para in paragraphs:
        # Preserve blank lines, wrap others
        if not para.strip():
            formatted.append('')
        else:
            formatted.append(textwrap.fill(para, width=width))
    
    return '\n\n'.join(formatted)
 
def quote_reply(text: str, width: int = 72) -> str:
    """Format text as email reply quote."""
    # First wrap, then prefix
    wrapped = textwrap.fill(text, width=width - 2)  # Leave room for "> "
    return textwrap.indent(wrapped, '> ')
 
# Usage
body = format_email_body([
    "Thank you for your email regarding the project proposal.",
    "I've reviewed the documents and have a few questions.",
    "Please see my comments below.",
])
 
quoted = quote_reply("The original message that was sent to me and needs to be quoted in the reply.")

Code Block Handling

import textwrap
 
def dedent_code(code: str) -> str:
    """Clean up code from docstrings or multiline strings."""
    return textwrap.dedent(code).strip()
 
# In docstrings
def example():
    """
    Example function.
    
    Usage:
        result = example()
        print(result)
    """
    pass
 
# Extract and clean docstring examples
doc = example.__doc__
clean_doc = textwrap.dedent(doc).strip()

Preserving Line Breaks

import textwrap
 
def wrap_preserving_breaks(text: str, width: int = 70) -> str:
    """Wrap text but preserve explicit line breaks."""
    paragraphs = text.split('\n\n')
    wrapped_paragraphs = []
    
    for para in paragraphs:
        # Join lines within paragraph, then wrap
        single_line = ' '.join(para.split())
        wrapped = textwrap.fill(single_line, width=width)
        wrapped_paragraphs.append(wrapped)
    
    return '\n\n'.join(wrapped_paragraphs)
 
text = """This is the first paragraph that
might have weird line breaks.
 
This is the second paragraph that
is also formatted oddly."""
 
print(wrap_preserving_breaks(text))

Bullet Point Formatting

import textwrap
 
def format_bullet_list(items: list[str], width: int = 70) -> str:
    """Format bullet point list with proper wrapping."""
    wrapper = textwrap.TextWrapper(
        width=width,
        initial_indent='• ',
        subsequent_indent='  ',
    )
    
    return '\n'.join(wrapper.fill(item) for item in items)
 
items = [
    "This is the first item which might be quite long and need wrapping",
    "Short item",
    "Another longer item that definitely needs to wrap to the next line",
]
 
print(format_bullet_list(items))
# • This is the first item which might be quite long and
#   need wrapping
# • Short item
# • Another longer item that definitely needs to wrap to
#   the next line

TextWrapper Options

import textwrap
 
wrapper = textwrap.TextWrapper(
    width=70,                    # Maximum line width
    initial_indent='',           # Prefix for first line
    subsequent_indent='',        # Prefix for other lines
    expand_tabs=True,            # Convert tabs to spaces
    tabsize=8,                   # Tab width
    replace_whitespace=True,     # Collapse whitespace
    fix_sentence_endings=False,  # Add double space after sentences
    break_long_words=True,       # Break words longer than width
    break_on_hyphens=True,       # Break on hyphens
    drop_whitespace=True,        # Drop leading/trailing whitespace
    max_lines=None,              # Maximum lines (with placeholder)
    placeholder=' [...]',        # Truncation indicator
)

Documentation Generation

import textwrap
 
def format_docstring(func) -> str:
    """Format function docstring for documentation."""
    if not func.__doc__:
        return "No documentation available."
    
    # Clean up docstring
    doc = textwrap.dedent(func.__doc__).strip()
    
    # Wrap each paragraph
    paragraphs = doc.split('\n\n')
    wrapped = [textwrap.fill(p, width=72) for p in paragraphs]
    
    return '\n\n'.join(wrapped)
 
def my_function(x, y):
    """
    Calculate the sum of two numbers.
    
    This function takes two numeric arguments and returns
    their sum. It supports both integers and floating-point
    numbers.
    
    Args:
        x: First number
        y: Second number
    
    Returns:
        The sum of x and y
    """
    return x + y
 
print(format_docstring(my_function))

The textwrap module transforms messy text into clean, formatted output. Whether you're building CLIs, formatting emails, or generating documentation, it handles the tedious wrapping and indentation work.

React to this post: