diff --git a/IPython/utils/text.py b/IPython/utils/text.py index 5935aac..9b780d2 100644 --- a/IPython/utils/text.py +++ b/IPython/utils/text.py @@ -618,25 +618,30 @@ class DollarFormatter(FullEvalFormatter): # Utils to columnize a list of string #----------------------------------------------------------------------------- -def _chunks(l, n): - """Yield successive n-sized chunks from l.""" - for i in py3compat.xrange(0, len(l), n): - yield l[i:i+n] +def _col_chunks(l, nrows, row_first=False): + """Yield successive nrows-sized column chunks from l.""" + if row_first: + ncols = (len(l) // nrows) + (len(l) % nrows > 0) + for i in py3compat.xrange(ncols): + yield [l[j] for j in py3compat.xrange(i, len(l), nrows)] + else: + for i in py3compat.xrange(0, len(l), nrows): + yield l[i:(i + nrows)] -def _find_optimal(rlist , separator_size=2 , displaywidth=80): +def _find_optimal(rlist, row_first=False, separator_size=2, displaywidth=80): """Calculate optimal info to columnize a list of string""" - for nrow in range(1, len(rlist)+1) : - chk = list(map(max,_chunks(rlist, nrow))) - sumlength = sum(chk) - ncols = len(chk) - if sumlength+separator_size*(ncols-1) <= displaywidth : - break; - return {'columns_numbers' : ncols, - 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0, - 'rows_numbers' : nrow, - 'columns_width' : chk - } + for nrow in range(1, len(rlist) + 1): + col_widths = list(map(max, _col_chunks(rlist, nrow, row_first))) + sumlength = sum(col_widths) + ncols = len(col_widths) + if sumlength + separator_size * (ncols - 1) <= displaywidth: + break + return {'num_columns': ncols, + 'optimal_separator_width': (displaywidth - sumlength) / (ncols - 1) if (ncols - 1) else 0, + 'num_rows': nrow, + 'column_widths': col_widths + } def _get_or_default(mylist, i, default=None): @@ -647,7 +652,7 @@ def _get_or_default(mylist, i, default=None): return mylist[i] -def compute_item_matrix(items, empty=None, *args, **kwargs) : +def compute_item_matrix(items, row_first=False, empty=None, *args, **kwargs) : """Returns a nested list, and info to columnize items Parameters @@ -655,6 +660,9 @@ def compute_item_matrix(items, empty=None, *args, **kwargs) : items list of strings to columize + row_first : (default False) + Whether to to compute columns for a row-first matrix instead ofr + column-first (default). empty : (default None) default value to fill list if needed separator_size : int (default=2) @@ -675,11 +683,11 @@ def compute_item_matrix(items, empty=None, *args, **kwargs) : dict_info some info to make columnize easier: - columns_numbers + num_columns number of columns - rows_numbers + num_rows number of rows - columns_width + column_widths list of with of each columns optimal_separator_width best separator width between columns @@ -689,24 +697,27 @@ def compute_item_matrix(items, empty=None, *args, **kwargs) : :: In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l'] - ...: compute_item_matrix(l,displaywidth=12) + ...: compute_item_matrix(l, displaywidth=12) Out[1]: ([['aaa', 'f', 'k'], ['b', 'g', 'l'], ['cc', 'h', None], ['d', 'i', None], ['eeeee', 'j', None]], - {'columns_numbers': 3, - 'columns_width': [5, 1, 1], + {'num_columns': 3, + 'column_widths': [5, 1, 1], 'optimal_separator_width': 2, - 'rows_numbers': 5}) + 'num_rows': 5}) """ - info = _find_optimal(list(map(len, items)), *args, **kwargs) - nrow, ncol = info['rows_numbers'], info['columns_numbers'] - return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info) + info = _find_optimal(list(map(len, items)), row_first, *args, **kwargs) + nrow, ncol = info['num_rows'], info['num_columns'] + if row_first: + return ([[_get_or_default(items, c * nrow + r, default=empty) for r in range(nrow)] for c in range(ncol)], info) + else: + return ([[_get_or_default(items, c * nrow + r, default=empty) for c in range(ncol)] for r in range(nrow)], info) -def columnize(items, separator=' ', displaywidth=80): +def columnize(items, row_first=False, separator=' ', displaywidth=80): """ Transform a list of strings into a single string with columns. Parameters @@ -714,6 +725,10 @@ def columnize(items, separator=' ', displaywidth=80): items : sequence of strings The strings to process. + row_first : (default False) + Whether to to compute columns for a row-first matrix instead ofr + column-first (default). + separator : str, optional [default is two spaces] The string that separates columns. @@ -724,11 +739,11 @@ def columnize(items, separator=' ', displaywidth=80): ------- The formatted string. """ - if not items : + if not items: return '\n' - matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth) + matrix, info = compute_item_matrix(items, row_first=row_first, separator_size=len(separator), displaywidth=displaywidth) fmatrix = [filter(None, x) for x in matrix] - sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])]) + sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['column_widths'])]) return '\n'.join(map(sjoin, fmatrix))+'\n'