columnize.py
160 lines
| 5.7 KiB
| text/x-python
|
PythonLexer
epatters
|
r2723 | """Return compact set of columns as a string with newlines for an | ||
array of strings. | ||||
Adapted from the routine of the same name inside cmd.py | ||||
Author: Rocky Bernstein. | ||||
License: MIT Open Source License. | ||||
""" | ||||
import types | ||||
def columnize(array, displaywidth=80, colsep = ' ', | ||||
arrange_vertical=True, ljust=True, lineprefix=''): | ||||
"""Return a list of strings as a compact set of columns arranged | ||||
horizontally or vertically. | ||||
For example, for a line width of 4 characters (arranged vertically): | ||||
['1', '2,', '3', '4'] => '1 3\n2 4\n' | ||||
or arranged horizontally: | ||||
['1', '2,', '3', '4'] => '1 2\n3 4\n' | ||||
Each column is only as wide as necessary. By default, columns are | ||||
separated by two spaces - one was not legible enough. Set "colsep" | ||||
to adjust the string separate columns. Set `displaywidth' to set | ||||
the line width. | ||||
Normally, consecutive items go down from the top to bottom from | ||||
the left-most column to the right-most. If "arrange_vertical" is | ||||
set false, consecutive items will go across, left to right, top to | ||||
bottom.""" | ||||
if not isinstance(array, list) and not isinstance(array, tuple): | ||||
raise TypeError, ( | ||||
'array needs to be an instance of a list or a tuple') | ||||
array = [str(i) for i in array] | ||||
# Some degenerate cases | ||||
size = len(array) | ||||
if 0 == size: | ||||
return "<empty>\n" | ||||
elif size == 1: | ||||
return '%s\n' % str(array[0]) | ||||
displaywidth = max(4, displaywidth - len(lineprefix)) | ||||
if arrange_vertical: | ||||
array_index = lambda nrows, row, col: nrows*col + row | ||||
# Try every row count from 1 upwards | ||||
for nrows in range(1, size): | ||||
ncols = (size+nrows-1) // nrows | ||||
colwidths = [] | ||||
totwidth = -len(colsep) | ||||
for col in range(ncols): | ||||
# get max column width for this column | ||||
colwidth = 0 | ||||
for row in range(nrows): | ||||
i = array_index(nrows, row, col) | ||||
if i >= size: break | ||||
x = array[i] | ||||
colwidth = max(colwidth, len(x)) | ||||
pass | ||||
colwidths.append(colwidth) | ||||
totwidth += colwidth + len(colsep) | ||||
if totwidth > displaywidth: | ||||
break | ||||
pass | ||||
if totwidth <= displaywidth: | ||||
break | ||||
pass | ||||
# The smallest number of rows computed and the | ||||
# max widths for each column has been obtained. | ||||
# Now we just have to format each of the | ||||
# rows. | ||||
s = '' | ||||
for row in range(nrows): | ||||
texts = [] | ||||
for col in range(ncols): | ||||
i = row + nrows*col | ||||
if i >= size: | ||||
x = "" | ||||
else: | ||||
x = array[i] | ||||
texts.append(x) | ||||
while texts and not texts[-1]: | ||||
del texts[-1] | ||||
for col in range(len(texts)): | ||||
if ljust: | ||||
texts[col] = texts[col].ljust(colwidths[col]) | ||||
else: | ||||
texts[col] = texts[col].rjust(colwidths[col]) | ||||
pass | ||||
pass | ||||
s += "%s%s\n" % (lineprefix, str(colsep.join(texts))) | ||||
pass | ||||
return s | ||||
else: | ||||
array_index = lambda nrows, row, col: ncols*(row-1) + col | ||||
# Try every column count from size downwards | ||||
prev_colwidths = [] | ||||
colwidths = [] | ||||
for ncols in range(size, 0, -1): | ||||
# Try every row count from 1 upwards | ||||
min_rows = (size+ncols-1) // ncols | ||||
for nrows in range(min_rows, size): | ||||
rounded_size = nrows * ncols | ||||
colwidths = [] | ||||
totwidth = -len(colsep) | ||||
for col in range(ncols): | ||||
# get max column width for this column | ||||
colwidth = 0 | ||||
for row in range(1, nrows+1): | ||||
i = array_index(nrows, row, col) | ||||
if i >= rounded_size: break | ||||
elif i < size: | ||||
x = array[i] | ||||
colwidth = max(colwidth, len(x)) | ||||
pass | ||||
pass | ||||
colwidths.append(colwidth) | ||||
totwidth += colwidth + len(colsep) | ||||
if totwidth >= displaywidth: | ||||
break | ||||
pass | ||||
if totwidth <= displaywidth and i >= rounded_size-1: | ||||
# Found the right nrows and ncols | ||||
nrows = row | ||||
break | ||||
elif totwidth >= displaywidth: | ||||
# Need to reduce ncols | ||||
break | ||||
pass | ||||
if totwidth <= displaywidth and i >= rounded_size-1: | ||||
break | ||||
pass | ||||
# The smallest number of rows computed and the | ||||
# max widths for each column has been obtained. | ||||
# Now we just have to format each of the | ||||
# rows. | ||||
s = '' | ||||
for row in range(1, nrows+1): | ||||
texts = [] | ||||
for col in range(ncols): | ||||
i = array_index(nrows, row, col) | ||||
if i >= size: | ||||
break | ||||
else: x = array[i] | ||||
texts.append(x) | ||||
pass | ||||
for col in range(len(texts)): | ||||
if ljust: | ||||
texts[col] = texts[col].ljust(colwidths[col]) | ||||
else: | ||||
texts[col] = texts[col].rjust(colwidths[col]) | ||||
pass | ||||
pass | ||||
s += "%s%s\n" % (lineprefix, str(colsep.join(texts))) | ||||
pass | ||||
return s | ||||
pass | ||||