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 serverEmail 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 lineTextWrapper 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: