Lesson 22 · Python standard library deep dive
Python io, string & textwrap Explained: Text & Streams | Standard Library #22
Video twenty-two of the twenty-five-part series: io, string, and textwrap, for text and stream utilities. In-memory streams, character-class constants,…
- CoursePython standard library deep dive
- Lesson22 of 24
- Video16 min
- FormatJupyter notebook · 10 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPython Standard Library Deep-Dive, Video 22: io, string, textwrap#
- Video twenty-two of the twenty-five-part series: io, string, and textwrap, for text and stream utilities.
- In-memory streams, character-class constants, template substitution, and text wrapping and formatting.
- Let's get into it.
Part 1: io.StringIO - In-Memory Text Streams#
import io
buffer = io.StringIO()
buffer.write('first line\n')
buffer.write('second line\n')
print(buffer.getvalue())
buffer.seek(0)
for line in buffer:
print(repr(line))
Part 2: io.BytesIO - In-Memory Binary Streams#
byte_buffer = io.BytesIO()
byte_buffer.write(b'\x89PNG\r\n')
byte_buffer.write(b'more binary data')
print(byte_buffer.getvalue())
byte_buffer.seek(0)
print(byte_buffer.read(4))
print(byte_buffer.read())
Part 3: seek(), tell(), and the File-Like Protocol#
text_buffer = io.StringIO('Hello, World!')
print(text_buffer.tell())
print(text_buffer.read(5))
print(text_buffer.tell())
text_buffer.seek(7)
print(text_buffer.read())
text_buffer.seek(0)
print(text_buffer.read())
Part 4: string Module Constants#
import string
print(string.ascii_lowercase)
print(string.ascii_uppercase)
print(string.digits)
print(string.punctuation)
print(string.whitespace == ' \t\n\r\x0b\x0c')
print(len(string.printable))
Part 5: string.Template - Simple Substitution#
from string import Template
greeting = Template('Hello, $name! You have $count new messages.')
print(greeting.substitute(name='Ana', count=3))
safe_result = greeting.safe_substitute(name='Sam')
print(safe_result)
try:
greeting.substitute(name='Sam')
except KeyError as e:
print(f'missing key: {e}')
Part 6: string.Formatter - Custom Formatting Behavior#
from string import Formatter
class DefaultFormatter(Formatter):
def get_value(self, key, args, kwargs):
if isinstance(key, str) and key not in kwargs:
return f'<missing:{key}>'
return super().get_value(key, args, kwargs)
fmt = DefaultFormatter()
print(fmt.format('Hello {name}, balance: {balance}', name='Ana'))
Part 7: textwrap.wrap() and fill()#
import textwrap
long_text = 'Python is a genuinely versatile, widely-used programming language favored for data analysis, automation, and web development.'
lines = textwrap.wrap(long_text, width=40)
for line in lines:
print(line)
print('---')
print(textwrap.fill(long_text, width=40))
Part 8: textwrap.shorten(), dedent(), and indent()#
print(textwrap.shorten('This sentence is genuinely far too long to display in full', width=30))
messy = '''
line one
line two
line three, extra indented
'''
print(repr(textwrap.dedent(messy)))
print(textwrap.indent('first\nsecond', prefix='>> '))
Part 9: A Real Pattern - Testing File-Consuming Code with StringIO#
def count_words(file_obj):
total = 0
for line in file_obj:
total += len(line.split())
return total
fake_file = io.StringIO('the quick fox\njumps over\nthe lazy dog\n')
print(count_words(fake_file))
with open('demo_real_words.txt', 'w') as f:
f.write('one two three\n')
with open('demo_real_words.txt') as f:
print(count_words(f))
Part 10: A Real Pattern - Building a Formatted Text Report#
report_template = Template('=== $title ===\n\n$body\n\nGenerated for: $author')
summary = ('This quarter showed genuinely strong growth across every region, '
'with particularly notable gains in the data analytics product line '
'and continued steady demand for the core platform offering.')
wrapped_summary = textwrap.fill(summary, width=50)
final_report = report_template.substitute(
title='Q3 Summary',
body=wrapped_summary,
author='Ana',
)
output = io.StringIO()
output.write(final_report)
output.write('\n')
print(output.getvalue())
Wrap-Up: What You Learned#
- io.StringIO and io.BytesIO provide real in-memory file-like streams, for text and binary respectively.
- seek/tell work identically on real files and on StringIO/BytesIO; code written against 'any file-like object' works on both.
- string provides ready-made character-class constants: ascii_letters, digits, punctuation, whitespace, printable.
- string.Template offers safer plain substitution than f-strings for template text from an untrusted or external source.
- string.Formatter is the class powering str.format; subclassing it customizes formatting behavior, like handling missing fields.
- textwrap.wrap/fill break long text into width-limited lines, as a list or as one joined string.
- textwrap.shorten truncates with a placeholder; dedent strips common leading whitespace; indent adds a prefix per line.
- A real pattern: testing file-consuming code with StringIO instead of real files, exercising identical code paths.
- A real pattern: combining Template, textwrap, and StringIO to assemble a formatted text report in memory.
- That wraps up io, string, and textwrap. Next up: uuid, base64, and binascii, for unique identifiers and encoding.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



