##// END OF EJS Templates
re-write columnize, with intermediate step....
Matthias BUSSONNIER -
Show More
@@ -14,6 +14,7 b''
14 14
15 15 import os
16 16 import math
17 import random
17 18
18 19 import nose.tools as nt
19 20
@@ -32,13 +33,37 b' def test_columnize():'
32 33 items = [l*size for l in 'abc']
33 34 out = text.columnize(items, displaywidth=80)
34 35 nt.assert_equals(out, 'aaaaa bbbbb ccccc\n')
35 out = text.columnize(items, displaywidth=10)
36 out = text.columnize(items, displaywidth=12)
36 37 nt.assert_equals(out, 'aaaaa ccccc\nbbbbb\n')
37
38 out = text.columnize(items, displaywidth=10)
39 nt.assert_equals(out, 'aaaaa\nbbbbb\nccccc\n')
40
41 def test_columnize_random():
42 """Test with random input to hopfully catch edge case """
43 for nitems in [random.randint(2,70) for i in range(2,20)]:
44 displaywidth = random.randint(20,200)
45 rand_len = [random.randint(2,displaywidth) for i in range(nitems)]
46 items = ['x'*l for l in rand_len]
47 out = text.columnize(items, displaywidth=displaywidth)
48 longer_line = max([len(x) for x in out.split('\n')])
49 longer_element = max(rand_len)
50 if longer_line > displaywidth:
51 print "Columnize displayed something lager than displaywidth : %s " % longer_line
52 print "longer element : %s " % longer_element
53 print "displaywidth : %s " % displaywidth
54 print "number of element : %s " % nitems
55 print "size of each element :\n %s" % rand_len
56 assert False
57
58 def test_columnize_medium():
59 """Test with inputs than shouldn't be wider tahn 80 """
60 size = 40
61 items = [l*size for l in 'abc']
62 out = text.columnize(items, displaywidth=80)
63 nt.assert_equals(out, '\n'.join(items+['']))
38 64
39 65 def test_columnize_long():
40 66 """Test columnize with inputs longer than the display window"""
41 text.columnize(['a'*81, 'b'*81], displaywidth=80)
42 67 size = 11
43 68 items = [l*size for l in 'abc']
44 69 out = text.columnize(items, displaywidth=size-1)
@@ -660,6 +660,45 b' class DollarFormatter(FullEvalFormatter):'
660 660 # Re-yield the {foo} style pattern
661 661 yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion)
662 662
663 #-----------------------------------------------------------------------------
664 # Utils to columnize a list of string
665 #-----------------------------------------------------------------------------
666 def _chunks(l, n):
667 """Yield successive n-sized chunks from l."""
668 for i in xrange(0, len(l), n):
669 yield l[i:i+n]
670
671 def _find_optimal(rlist , sepsize=2 , displaywidth=80):
672 """Calculate optimal info to columnize a list of string"""
673 for nrow in range(1, len(rlist)+1) :
674 chk = [max(l) for l in _chunks(rlist, nrow) ]
675 sumlength = sum(chk)
676 ncols = len(chk)
677 if sumlength+sepsize*(ncols-1) <= displaywidth :
678 break;
679 return {'columns_numbers' : ncols,
680 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0,
681 'rows_numbers' : nrow,
682 'columns_width' : chk
683 }
684
685 def _get_or_default(mylist, i, default=None):
686 """return list item number, or default if don't exist"""
687 if i >= len(mylist):
688 return default
689 else :
690 return mylist[i]
691
692 def compute_item_matrix(items, *args, **kwargs) :
693 """ Transform a list of strings into a nested list to columnize
694
695 Returns a tuple of (strings_matrix, dict_info)
696
697 innermost lists are rows, see columnize for options info
698 """
699 info = _find_optimal(map(len, items), *args, **kwargs)
700 nrow, ncol = info['rows_numbers'], info['columns_numbers']
701 return ([[ _get_or_default(items, c*nrow+i) for c in range(ncol) ] for i in range(nrow) ], info)
663 702
664 703 def columnize(items, separator=' ', displaywidth=80):
665 704 """ Transform a list of strings into a single string with columns.
@@ -679,58 +718,11 b" def columnize(items, separator=' ', displaywidth=80):"
679 718 -------
680 719 The formatted string.
681 720 """
682 # Note: this code is adapted from columnize 0.3.2.
683 # See http://code.google.com/p/pycolumnize/
684
685 # Some degenerate cases.
686 size = len(items)
687 if size == 0:
721 if not items :
688 722 return '\n'
689 elif size == 1:
690 return '%s\n' % items[0]
691
692 # Special case: if any item is longer than the maximum width, there's no
693 # point in triggering the logic below...
694 item_len = map(len, items) # save these, we can reuse them below
695 longest = max(item_len)
696 if longest >= displaywidth:
697 return '\n'.join(items+[''])
698
699 # Try every row count from 1 upwards
700 array_index = lambda nrows, row, col: nrows*col + row
701 for nrows in range(1, size):
702 ncols = (size + nrows - 1) // nrows
703 colwidths = []
704 totwidth = -len(separator)
705 for col in range(ncols):
706 # Get max column width for this column
707 colwidth = 0
708 for row in range(nrows):
709 i = array_index(nrows, row, col)
710 if i >= size: break
711 x, len_x = items[i], item_len[i]
712 colwidth = max(colwidth, len_x)
713 colwidths.append(colwidth)
714 totwidth += colwidth + len(separator)
715 if totwidth > displaywidth:
716 break
717 if totwidth <= displaywidth:
718 break
719
720 # The smallest number of rows computed and the max widths for each
721 # column has been obtained. Now we just have to format each of the rows.
722 string = ''
723 for row in range(nrows):
724 texts = []
725 for col in range(ncols):
726 i = row + nrows*col
727 if i >= size:
728 texts.append('')
729 else:
730 texts.append(items[i])
731 while texts and not texts[-1]:
732 del texts[-1]
733 for col in range(len(texts)):
734 texts[col] = texts[col].ljust(colwidths[col])
735 string += '%s\n' % separator.join(texts)
736 return string
723 matrix, info = compute_item_matrix(items, sepsize=len(separator), displaywidth=displaywidth)
724 #sep = ' '*min(info['optimal_separator_width'], 9)
725 fmatrix = matrix
726 fmatrix = [filter(None, x) for x in matrix]
727 sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])])
728 return '\n'.join(map(sjoin, fmatrix))+'\n'
General Comments 0
You need to be logged in to leave comments. Login now