r'''Takes an iterator of strings, and attempts to wrap them in whole chunks
    to fit within width. Takes an optional preamble which is prepended before
    the first line, and an optional per-line prefix (which is appended to every
    line but the first). If return_gen is true, this function returns a
    generator that will produce the lines of output as needed, otherwise it
    returns a single joined string

def linewrap(chunks, width=None, sep=' ', preamble='', line_prefix=''):
    r'''Takes an iterator of strings, and attempts to wrap them in whole chunks
    to fit within width. Takes an optional preamble which is prepended before
    the first line, and an optional per-line prefix (which is appended to every
    line but the first). If return_gen is true, this function returns a
    generator that will produce the lines of output as needed, otherwise it
    returns a single joined string'''
    sep_len = len(sep)

    if isinstance(chunks, basestring):
        chunks = chunks.split()
    if preamble:
        chunks[0] = preamble + chunks[0]

    def line_len(line):
        r'Gets the full length of a line passed a list of strings'
        word_len = sum(len(l) for l in line)
        seps_len = sep_len * (len(line) - 1)
        return word_len + seps_len if seps_len >= 0 else 0

    def gen():
        r'The generator to incrementally create lines from the input'
        sep_len = len(sep)
        line = []
        for chunk in chunks:
            if line_len(line) + len(chunk) + sep_len > width:
                yield sep.join(line)
                line = [line_prefix + chunk]
            else:
                line.append(chunk)
        if line:
            yield sep.join(line)

    return gen()