Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make Python 3's Print fit the size of the command prompt?

Python 3 can easily handle printing a lot of text:

text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent vitae odio quis felis consectetur blandit. Etiam mattis vehicula ex id sodales. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris fermentum semper nisi vel aliquam. Ut nec facilisis lectus. Maecenas auctor blandit condimentum. Donec finibus orci ac imperdiet congue. Pellentesque sem leo, commodo non metus ac, posuere maximus lorem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. "
print(text)

The problem is that this text is printed like this:

enter image description here

This is of course not very good for readability of the text. Going through all of the text by hand to insert white lines makes for quite a hassle. I have seen this question, but I'm wondering if there's an option to do this automatically.

like image 676
Thomas Jacobs Avatar asked Dec 24 '22 05:12

Thomas Jacobs


1 Answers

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.

like image 105
ShadowRanger Avatar answered Dec 26 '22 19:12

ShadowRanger