-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtableformatter.py
147 lines (140 loc) · 6.69 KB
/
tableformatter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# Source: http://code.activestate.com/recipes/267662-table-indentation/
import cStringIO,operator
def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify='left',
separateRows=False, prefix='', postfix='', wrapfunc=lambda x:x):
"""Indents a table by column.
- rows: A sequence of sequences of items, one sequence per row.
- hasHeader: True if the first row consists of the columns' names.
- headerChar: Character to be used for the row separator line
(if hasHeader==True or separateRows==True).
- delim: The column delimiter.
- justify: Determines how are data justified in their column.
Valid values are 'left','right' and 'center'.
- separateRows: True if rows are to be separated by a line
of 'headerChar's.
- prefix: A string prepended to each printed row.
- postfix: A string appended to each printed row.
- wrapfunc: A function f(text) for wrapping text; each element in
the table is first wrapped by this function."""
# closure for breaking logical rows to physical, using wrapfunc
def rowWrapper(row):
newRows = [wrapfunc(item).split('\n') for item in row]
return [[substr or '' for substr in item] for item in map(None,*newRows)]
# break each logical row into one or more physical ones
logicalRows = [rowWrapper(row) for row in rows]
# columns of physical rows
columns = map(None,*reduce(operator.add,logicalRows))
# get the maximum of each column by the string length of its items
maxWidths = [max([len(str(item)) for item in column]) for column in columns]
rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
len(delim)*(len(maxWidths)-1))
# select the appropriate justify method
justify = {'center':str.center, 'right':str.rjust, 'left':str.ljust}[justify.lower()]
output=cStringIO.StringIO()
if separateRows: print >> output, rowSeparator
for physicalRows in logicalRows:
for row in physicalRows:
print >> output, \
prefix \
+ delim.join([justify(str(item),width) for (item,width) in zip(row,maxWidths)]) \
+ postfix
if separateRows or hasHeader: print >> output, rowSeparator; hasHeader=False
return output.getvalue()
# written by Mike Brown
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
def wrap_onspace(text, width):
"""
A word-wrap function that preserves existing line breaks
and most spaces in the text. Expects that existing line
breaks are posix newlines (\n).
"""
return reduce(lambda line, word, width=width: '%s%s%s' %
(line,
' \n'[(len(line[line.rfind('\n')+1:])
+ len(word.split('\n',1)[0]
) >= width)],
word),
text.split(' ')
)
import re
def wrap_onspace_strict(text, width):
"""Similar to wrap_onspace, but enforces the width constraint:
words longer than width are split."""
wordRegex = re.compile(r'\S{'+str(width)+r',}')
return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width)
import math
def wrap_always(text, width):
"""A simple word-wrap function that wraps text on exactly width characters.
It doesn't split the text in words."""
return '\n'.join([ text[width*i:width*(i+1)] \
for i in xrange(int(math.ceil(1.*len(text)/width))) ])
if __name__ == '__main__':
labels = ('First Name', 'Last Name', 'Age', 'Position')
data = \
'''John,Smith,24,Software Engineer
Mary,Brohowski,23,Sales Manager
Aristidis,Papageorgopoulos,28,Senior Reseacher'''
rows = [row.strip().split(',') for row in data.splitlines()]
print 'Without wrapping function\n'
print indent([labels]+rows, hasHeader=True)
# test indent with different wrapping functions
width = 10
for wrapper in (wrap_always,wrap_onspace,wrap_onspace_strict):
print 'Wrapping function: %s(x,width=%d)\n' % (wrapper.__name__,width)
print indent([labels]+rows, hasHeader=True, separateRows=True,
prefix='| ', postfix=' |',
wrapfunc=lambda x: wrapper(x,width))
# output:
#
#Without wrapping function
#
#First Name | Last Name | Age | Position
#-------------------------------------------------------
#John | Smith | 24 | Software Engineer
#Mary | Brohowski | 23 | Sales Manager
#Aristidis | Papageorgopoulos | 28 | Senior Reseacher
#
#Wrapping function: wrap_always(x,width=10)
#
#----------------------------------------------
#| First Name | Last Name | Age | Position |
#----------------------------------------------
#| John | Smith | 24 | Software E |
#| | | | ngineer |
#----------------------------------------------
#| Mary | Brohowski | 23 | Sales Mana |
#| | | | ger |
#----------------------------------------------
#| Aristidis | Papageorgo | 28 | Senior Res |
#| | poulos | | eacher |
#----------------------------------------------
#
#Wrapping function: wrap_onspace(x,width=10)
#
#---------------------------------------------------
#| First Name | Last Name | Age | Position |
#---------------------------------------------------
#| John | Smith | 24 | Software |
#| | | | Engineer |
#---------------------------------------------------
#| Mary | Brohowski | 23 | Sales |
#| | | | Manager |
#---------------------------------------------------
#| Aristidis | Papageorgopoulos | 28 | Senior |
#| | | | Reseacher |
#---------------------------------------------------
#
#Wrapping function: wrap_onspace_strict(x,width=10)
#
#---------------------------------------------
#| First Name | Last Name | Age | Position |
#---------------------------------------------
#| John | Smith | 24 | Software |
#| | | | Engineer |
#---------------------------------------------
#| Mary | Brohowski | 23 | Sales |
#| | | | Manager |
#---------------------------------------------
#| Aristidis | Papageorgo | 28 | Senior |
#| | poulos | | Reseacher |
#---------------------------------------------