As mentioned in the comments, pprint
and textwrap
can be used to align output to a given width. Given that, the only trick is determining the width to use. You could just use 78 or 80 and assume the window is that large, but that means bad wrapping if the terminal is smaller, and wasted space if it's larger.
To check the actual width for use with pprint
/textwrap
, if you're on Python 3.3 or higher, you can use shutil.get_terminal_size
:
import shutil
def get_terminal_columns():
return shutil.get_terminal_size().columns
If you can't use 3.3+, you can use the curses
module to determine the size in a somewhat more complicated way:
import curses
def get_terminal_columns():
try:
# Initialize curses for terminal and check dimensions
rows, cols = curses.initscr().getmaxyx()
finally:
# Unload curses to release control of terminal so it behaves normally
curses.endwin()
return cols
Using one of these functions, you can then define:
from __future__ import print_function # Only needed on Py2
import textwrap
def print_autobreak(*args, sep=' '):
width = get_terminal_columns() # Check size once to avoid rechecks per "paragraph"
# Convert all args to strings, join with separator, then split on any newlines,
# preserving line endings, so each "paragraph" wrapped separately
for line in sep.join(map(str, args)).splitlines(True):
# Py3's print function makes it easy to print textwrap.wrap's result as one-liner
print(*textwrap.wrap(line, width), sep="\n")
This automatically performs line breaking between words based on the terminal size, preserves existing "hard" newlines and spaces, and (partially) matches the behavior of print
by allowing multiple arguments with an optional separator. It needs to completely realize the output string before printing (the original print
can print one-by-one, reducing memory usage a bit for huge outputs), but that's a side-effect of performing appropriate global wrapping.
If you need more complete line breaking handling (so two prints can occur without newlines, yet the second one accounts for the length of the first), you'll need a more stateful printer (to remember used characters on a line) or invent something a tad more complex with full curses
functionality. I'll leave that as an exercise.