Show More
@@ -14,6 +14,7 b'' | |||||
14 |
|
14 | |||
15 | import os |
|
15 | import os | |
16 | import math |
|
16 | import math | |
|
17 | import random | |||
17 |
|
18 | |||
18 | import nose.tools as nt |
|
19 | import nose.tools as nt | |
19 |
|
20 | |||
@@ -32,13 +33,37 b' def test_columnize():' | |||||
32 | items = [l*size for l in 'abc'] |
|
33 | items = [l*size for l in 'abc'] | |
33 | out = text.columnize(items, displaywidth=80) |
|
34 | out = text.columnize(items, displaywidth=80) | |
34 | nt.assert_equals(out, 'aaaaa bbbbb ccccc\n') |
|
35 | nt.assert_equals(out, 'aaaaa bbbbb ccccc\n') | |
35 |
out = text.columnize(items, displaywidth=1 |
|
36 | out = text.columnize(items, displaywidth=12) | |
36 | nt.assert_equals(out, 'aaaaa ccccc\nbbbbb\n') |
|
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 | def test_columnize_long(): |
|
65 | def test_columnize_long(): | |
40 | """Test columnize with inputs longer than the display window""" |
|
66 | """Test columnize with inputs longer than the display window""" | |
41 | text.columnize(['a'*81, 'b'*81], displaywidth=80) |
|
|||
42 | size = 11 |
|
67 | size = 11 | |
43 | items = [l*size for l in 'abc'] |
|
68 | items = [l*size for l in 'abc'] | |
44 | out = text.columnize(items, displaywidth=size-1) |
|
69 | out = text.columnize(items, displaywidth=size-1) |
@@ -660,6 +660,45 b' class DollarFormatter(FullEvalFormatter):' | |||||
660 | # Re-yield the {foo} style pattern |
|
660 | # Re-yield the {foo} style pattern | |
661 | yield (txt + literal_txt[continue_from:], field_name, format_spec, conversion) |
|
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 | def columnize(items, separator=' ', displaywidth=80): |
|
703 | def columnize(items, separator=' ', displaywidth=80): | |
665 | """ Transform a list of strings into a single string with columns. |
|
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 | The formatted string. |
|
719 | The formatted string. | |
681 | """ |
|
720 | """ | |
682 | # Note: this code is adapted from columnize 0.3.2. |
|
721 | if not items : | |
683 | # See http://code.google.com/p/pycolumnize/ |
|
|||
684 |
|
||||
685 | # Some degenerate cases. |
|
|||
686 | size = len(items) |
|
|||
687 | if size == 0: |
|
|||
688 | return '\n' |
|
722 | return '\n' | |
689 | elif size == 1: |
|
723 | matrix, info = compute_item_matrix(items, sepsize=len(separator), displaywidth=displaywidth) | |
690 | return '%s\n' % items[0] |
|
724 | #sep = ' '*min(info['optimal_separator_width'], 9) | |
691 |
|
725 | fmatrix = matrix | ||
692 | # Special case: if any item is longer than the maximum width, there's no |
|
726 | fmatrix = [filter(None, x) for x in matrix] | |
693 | # point in triggering the logic below... |
|
727 | sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])]) | |
694 | item_len = map(len, items) # save these, we can reuse them below |
|
728 | return '\n'.join(map(sjoin, fmatrix))+'\n' | |
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 |
|
General Comments 0
You need to be logged in to leave comments.
Login now