Thomas Jacobs
Thomas Jacobs

Reputation: 181

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.

Upvotes: 5

Views: 6397

Answers (3)

ShadowRanger
ShadowRanger

Reputation: 155458

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.

Upvotes: 8

Maxime B
Maxime B

Reputation: 1016

You might have a look at this Gist.

Then using the returned values to split the string in subsequent print calls accordingly.

IDLE shell:

Width can be retrieved with sys.stdout.shell.width and that will raise an AttributeError on any other environment (in which case you would use the gist)

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148975

The hard part will be to get the size of the console (Windows world) or terminal (Unix), because I know no portable way to get it. It involves (as shown in the Gist proposed by @MaximeB):

  • the use of GetConsoleScreenBufferInfo from windows API for Windows (via ctypes.windll
  • the use of fcntl.ioctl on Linux or other Unixes

not speaking of possible corner cases... But the Gist could be enough for your requirements if you only use Windows.

Once you have found the width, of if you can accept a static width of 72 or 80 or any other value, the texwrap module can do the trick:

for i in textwrap.wrap(text, 72):
    print i

gives:

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.

Upvotes: 2

Related Questions