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=1 |
|
|
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