SaltyCrane Blog — Notes on JavaScript and web development

Python word wrap function

Update 2008-09-18: I just found out there is a textwrap module in the Python Standard Library. See the docs at: http://docs.python.org/lib/module-textwrap.html.

I'm working on creating function headers for our C development code. These headers include the function name, a purpose statement, input/output lists, etc. So my solution (because I'm lazy and prefer scripting to writing hundreds of lines of not-actually-code) is to write a Python script to write the headers. I put all the pertinent information (function names, variable names, descriptions, etc.) in an Excel spreadsheet and I'm using Python to read the data and properly format it. (See here and here for more info on using Python w/ Excel.) Things are going pretty well. The spreadsheet (and the script) are getting a little messy, but overall, it is faster (I think), more fun (definitely) and I believe will produce more consistent results. It also allows me to be more flexible if names or descriptions change in the future because I only need to change the information once in the spreadsheet instead of mutiple places in the actual .c files.

One of the rules for the function headers is that they must be 80 columns or less in width. It is annoying to wrap everything by hand so I wrote a Python function to do the wrapping. (I know Emacs could probably do all this in about 2 lines of Lisp, but I haven't learned that much yet.) Here is the script with a couple of examples:

def word_wrap(string, width=80, ind1=0, ind2=0, prefix=''):
    """ word wrapping function.
        string: the string to wrap
        width: the column number to wrap at
        prefix: prefix each line with this string (goes before any indentation)
        ind1: number of characters to indent the first line
        ind2: number of characters to indent the rest of the lines
    """
    string = prefix + ind1 * " " + string
    newstring = ""
    while len(string) > width:
        # find position of nearest whitespace char to the left of "width"
        marker = width - 1
        while not string[marker].isspace():
            marker = marker - 1

        # remove line from original string and add it to the new string
        newline = string[0:marker] + "\n"
        newstring = newstring + newline
        string = prefix + ind2 * " " + string[marker + 1:]

    return newstring + string
Example 1: wrap at 60 characters
string = "PURPOSE: To end world hunger, create peace for all people, solve all technological and scientific problems, make an exorbitant amount of money, and remain humble in the process."
print word_wrap(string, 60)
Results:
PURPOSE: To end world hunger, create peace for all people,
solve all technological and scientific problems, make an
exorbitant amount of money, and remain humble in the
process.
Example 2: wrap at 60 chars, with a prefix, and a hanging indent
print word_wrap(string, 60, 0, 9, " * ")
Results:
 * PURPOSE: To end world hunger, create peace for all
 *          people, solve all technological and scientific
 *          problems, make an exorbitant amount of money,
 *          and remain humble in the process.

Comments


#1 Harshad commented on :

Thank you for the code! I've used it in a small program I created - pietweet, a twitter client for python /commandline.

http://harshadsharma.com/doku.php/software:pietweet

Cheers!


#2 Eliot commented on :

Harshad,
I'm glad it was useful for you. Pietweet looks like a cool program.


#3 Seun Osewa commented on :

The python tetwrap module doesn't allow multiple paragraphs.


#4 Ed commented on :

You mention emacs, but not knowing how to do it. The feature you want is built into emacs. Mark the buffer and then hit Alt-q (or M-q for the purists)


#5 Eliot commented on :

Ed, thanks for the Emacs tip. I would do good to remember that keybinding!


#6 Arthur Dent commented on :

It's an old post, but...

Get rid of the 'if'.

while len(string) > width: is enough.

In other words...

12 s/if/while/
13 d
14,22 s/^\t//
23,26 d

#7 Eliot commented on :

Arthur Dent: That makes the code much cleaner! I don't know what I was thinking. I made the change above. Thanks for the tip.


#8 winterheart commented on :

textwrap is more flexible and can handle with strings without spaces that > width (URLs for example) http://docs.python.org/library/textwrap.html

import textwrap

wrapper = textwrap.TextWrapper(width=80,
    initial_indent=" " * 4,
    subsequent_indent=" " * 4,
    break_long_words=False,
    break_on_hyphens=False)

string = "my non-wraped string"
print wrapper.fill(string)

#9 Eliot commented on :

winterheart: I agree the textwrap module should be used if possible.

I ran your example:

import textwrap

wrapper = textwrap.TextWrapper(width=80,
    initial_indent=" " * 4,
    subsequent_indent=" " * 4,
    break_long_words=False,
    break_on_hyphens=False)

string = "PURPOSE: To end world hunger, create peace for all people, solve all technological and scientific problems, make an exorbitant amount of money, and remain humble in the process."
print wrapper.fill(string)

And got the following output:

    PURPOSE: To end world hunger, create peace for all people, solve all
    technological and scientific problems, make an exorbitant amount of money,
    and remain humble in the process.

Thanks.