# -*- coding: utf-8 -*-
"""Returns a 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
mmikulicic: taken from http://pypi.python.org/pypi/columnize and
adapted for strings containing ANSI escapes (should work mostly for
colors).
"""
import re
CLEAN_COLOR_RE = re.compile('\x1b[^;]*;?..?m')
[docs]def bw(text):
"""Plain black and white text."""
return CLEAN_COLOR_RE.sub('', text)
[docs]def color_ljust(text, size):
return text.ljust(size + len(text) - len(bw(text)))
[docs]def color_rjust(text, size):
return text.rjust(size + len(text) - len(bw(text)))
[docs]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(bw(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] = color_ljust(texts[col], colwidths[col])
else:
texts[col] = color_rjust(texts[col], 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
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(bw(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] = color_ljust(texts[col], colwidths[col])
else:
texts[col] = color_rjust(texts[col], colwidths[col])
pass
pass
s += "%s%s\n" % (lineprefix, str(colsep.join(texts)))
pass
return s
pass