Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(217)

Side by Side Diff: third_party/logilab/logilab/common/table.py

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This file is part of logilab-common.
5 #
6 # logilab-common is free software: you can redistribute it and/or modify it unde r
7 # the terms of the GNU Lesser General Public License as published by the Free
8 # Software Foundation, either version 2.1 of the License, or (at your option) an y
9 # later version.
10 #
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 # details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License along
17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>.
18 """Table management module."""
19
20 from __future__ import print_function
21
22 __docformat__ = "restructuredtext en"
23
24 from six.moves import range
25
26 class Table(object):
27 """Table defines a data table with column and row names.
28 inv:
29 len(self.data) <= len(self.row_names)
30 forall(self.data, lambda x: len(x) <= len(self.col_names))
31 """
32
33 def __init__(self, default_value=0, col_names=None, row_names=None):
34 self.col_names = []
35 self.row_names = []
36 self.data = []
37 self.default_value = default_value
38 if col_names:
39 self.create_columns(col_names)
40 if row_names:
41 self.create_rows(row_names)
42
43 def _next_row_name(self):
44 return 'row%s' % (len(self.row_names)+1)
45
46 def __iter__(self):
47 return iter(self.data)
48
49 def __eq__(self, other):
50 if other is None:
51 return False
52 else:
53 return list(self) == list(other)
54
55 __hash__ = object.__hash__
56
57 def __ne__(self, other):
58 return not self == other
59
60 def __len__(self):
61 return len(self.row_names)
62
63 ## Rows / Columns creation #################################################
64 def create_rows(self, row_names):
65 """Appends row_names to the list of existing rows
66 """
67 self.row_names.extend(row_names)
68 for row_name in row_names:
69 self.data.append([self.default_value]*len(self.col_names))
70
71 def create_columns(self, col_names):
72 """Appends col_names to the list of existing columns
73 """
74 for col_name in col_names:
75 self.create_column(col_name)
76
77 def create_row(self, row_name=None):
78 """Creates a rowname to the row_names list
79 """
80 row_name = row_name or self._next_row_name()
81 self.row_names.append(row_name)
82 self.data.append([self.default_value]*len(self.col_names))
83
84
85 def create_column(self, col_name):
86 """Creates a colname to the col_names list
87 """
88 self.col_names.append(col_name)
89 for row in self.data:
90 row.append(self.default_value)
91
92 ## Sort by column ##########################################################
93 def sort_by_column_id(self, col_id, method = 'asc'):
94 """Sorts the table (in-place) according to data stored in col_id
95 """
96 try:
97 col_index = self.col_names.index(col_id)
98 self.sort_by_column_index(col_index, method)
99 except ValueError:
100 raise KeyError("Col (%s) not found in table" % (col_id))
101
102
103 def sort_by_column_index(self, col_index, method = 'asc'):
104 """Sorts the table 'in-place' according to data stored in col_index
105
106 method should be in ('asc', 'desc')
107 """
108 sort_list = sorted([(row[col_index], row, row_name)
109 for row, row_name in zip(self.data, self.row_names)])
110 # Sorting sort_list will sort according to col_index
111 # If we want reverse sort, then reverse list
112 if method.lower() == 'desc':
113 sort_list.reverse()
114
115 # Rebuild data / row names
116 self.data = []
117 self.row_names = []
118 for val, row, row_name in sort_list:
119 self.data.append(row)
120 self.row_names.append(row_name)
121
122 def groupby(self, colname, *others):
123 """builds indexes of data
124 :returns: nested dictionaries pointing to actual rows
125 """
126 groups = {}
127 colnames = (colname,) + others
128 col_indexes = [self.col_names.index(col_id) for col_id in colnames]
129 for row in self.data:
130 ptr = groups
131 for col_index in col_indexes[:-1]:
132 ptr = ptr.setdefault(row[col_index], {})
133 ptr = ptr.setdefault(row[col_indexes[-1]],
134 Table(default_value=self.default_value,
135 col_names=self.col_names))
136 ptr.append_row(tuple(row))
137 return groups
138
139 def select(self, colname, value):
140 grouped = self.groupby(colname)
141 try:
142 return grouped[value]
143 except KeyError:
144 return []
145
146 def remove(self, colname, value):
147 col_index = self.col_names.index(colname)
148 for row in self.data[:]:
149 if row[col_index] == value:
150 self.data.remove(row)
151
152
153 ## The 'setter' part #######################################################
154 def set_cell(self, row_index, col_index, data):
155 """sets value of cell 'row_indew', 'col_index' to data
156 """
157 self.data[row_index][col_index] = data
158
159
160 def set_cell_by_ids(self, row_id, col_id, data):
161 """sets value of cell mapped by row_id and col_id to data
162 Raises a KeyError if row_id or col_id are not found in the table
163 """
164 try:
165 row_index = self.row_names.index(row_id)
166 except ValueError:
167 raise KeyError("Row (%s) not found in table" % (row_id))
168 else:
169 try:
170 col_index = self.col_names.index(col_id)
171 self.data[row_index][col_index] = data
172 except ValueError:
173 raise KeyError("Column (%s) not found in table" % (col_id))
174
175
176 def set_row(self, row_index, row_data):
177 """sets the 'row_index' row
178 pre:
179 type(row_data) == types.ListType
180 len(row_data) == len(self.col_names)
181 """
182 self.data[row_index] = row_data
183
184
185 def set_row_by_id(self, row_id, row_data):
186 """sets the 'row_id' column
187 pre:
188 type(row_data) == types.ListType
189 len(row_data) == len(self.row_names)
190 Raises a KeyError if row_id is not found
191 """
192 try:
193 row_index = self.row_names.index(row_id)
194 self.set_row(row_index, row_data)
195 except ValueError:
196 raise KeyError('Row (%s) not found in table' % (row_id))
197
198
199 def append_row(self, row_data, row_name=None):
200 """Appends a row to the table
201 pre:
202 type(row_data) == types.ListType
203 len(row_data) == len(self.col_names)
204 """
205 row_name = row_name or self._next_row_name()
206 self.row_names.append(row_name)
207 self.data.append(row_data)
208 return len(self.data) - 1
209
210 def insert_row(self, index, row_data, row_name=None):
211 """Appends row_data before 'index' in the table. To make 'insert'
212 behave like 'list.insert', inserting in an out of range index will
213 insert row_data to the end of the list
214 pre:
215 type(row_data) == types.ListType
216 len(row_data) == len(self.col_names)
217 """
218 row_name = row_name or self._next_row_name()
219 self.row_names.insert(index, row_name)
220 self.data.insert(index, row_data)
221
222
223 def delete_row(self, index):
224 """Deletes the 'index' row in the table, and returns it.
225 Raises an IndexError if index is out of range
226 """
227 self.row_names.pop(index)
228 return self.data.pop(index)
229
230
231 def delete_row_by_id(self, row_id):
232 """Deletes the 'row_id' row in the table.
233 Raises a KeyError if row_id was not found.
234 """
235 try:
236 row_index = self.row_names.index(row_id)
237 self.delete_row(row_index)
238 except ValueError:
239 raise KeyError('Row (%s) not found in table' % (row_id))
240
241
242 def set_column(self, col_index, col_data):
243 """sets the 'col_index' column
244 pre:
245 type(col_data) == types.ListType
246 len(col_data) == len(self.row_names)
247 """
248
249 for row_index, cell_data in enumerate(col_data):
250 self.data[row_index][col_index] = cell_data
251
252
253 def set_column_by_id(self, col_id, col_data):
254 """sets the 'col_id' column
255 pre:
256 type(col_data) == types.ListType
257 len(col_data) == len(self.col_names)
258 Raises a KeyError if col_id is not found
259 """
260 try:
261 col_index = self.col_names.index(col_id)
262 self.set_column(col_index, col_data)
263 except ValueError:
264 raise KeyError('Column (%s) not found in table' % (col_id))
265
266
267 def append_column(self, col_data, col_name):
268 """Appends the 'col_index' column
269 pre:
270 type(col_data) == types.ListType
271 len(col_data) == len(self.row_names)
272 """
273 self.col_names.append(col_name)
274 for row_index, cell_data in enumerate(col_data):
275 self.data[row_index].append(cell_data)
276
277
278 def insert_column(self, index, col_data, col_name):
279 """Appends col_data before 'index' in the table. To make 'insert'
280 behave like 'list.insert', inserting in an out of range index will
281 insert col_data to the end of the list
282 pre:
283 type(col_data) == types.ListType
284 len(col_data) == len(self.row_names)
285 """
286 self.col_names.insert(index, col_name)
287 for row_index, cell_data in enumerate(col_data):
288 self.data[row_index].insert(index, cell_data)
289
290
291 def delete_column(self, index):
292 """Deletes the 'index' column in the table, and returns it.
293 Raises an IndexError if index is out of range
294 """
295 self.col_names.pop(index)
296 return [row.pop(index) for row in self.data]
297
298
299 def delete_column_by_id(self, col_id):
300 """Deletes the 'col_id' col in the table.
301 Raises a KeyError if col_id was not found.
302 """
303 try:
304 col_index = self.col_names.index(col_id)
305 self.delete_column(col_index)
306 except ValueError:
307 raise KeyError('Column (%s) not found in table' % (col_id))
308
309
310 ## The 'getter' part #######################################################
311
312 def get_shape(self):
313 """Returns a tuple which represents the table's shape
314 """
315 return len(self.row_names), len(self.col_names)
316 shape = property(get_shape)
317
318 def __getitem__(self, indices):
319 """provided for convenience"""
320 rows, multirows = None, False
321 cols, multicols = None, False
322 if isinstance(indices, tuple):
323 rows = indices[0]
324 if len(indices) > 1:
325 cols = indices[1]
326 else:
327 rows = indices
328 # define row slice
329 if isinstance(rows, str):
330 try:
331 rows = self.row_names.index(rows)
332 except ValueError:
333 raise KeyError("Row (%s) not found in table" % (rows))
334 if isinstance(rows, int):
335 rows = slice(rows, rows+1)
336 multirows = False
337 else:
338 rows = slice(None)
339 multirows = True
340 # define col slice
341 if isinstance(cols, str):
342 try:
343 cols = self.col_names.index(cols)
344 except ValueError:
345 raise KeyError("Column (%s) not found in table" % (cols))
346 if isinstance(cols, int):
347 cols = slice(cols, cols+1)
348 multicols = False
349 else:
350 cols = slice(None)
351 multicols = True
352 # get sub-table
353 tab = Table()
354 tab.default_value = self.default_value
355 tab.create_rows(self.row_names[rows])
356 tab.create_columns(self.col_names[cols])
357 for idx, row in enumerate(self.data[rows]):
358 tab.set_row(idx, row[cols])
359 if multirows :
360 if multicols:
361 return tab
362 else:
363 return [item[0] for item in tab.data]
364 else:
365 if multicols:
366 return tab.data[0]
367 else:
368 return tab.data[0][0]
369
370 def get_cell_by_ids(self, row_id, col_id):
371 """Returns the element at [row_id][col_id]
372 """
373 try:
374 row_index = self.row_names.index(row_id)
375 except ValueError:
376 raise KeyError("Row (%s) not found in table" % (row_id))
377 else:
378 try:
379 col_index = self.col_names.index(col_id)
380 except ValueError:
381 raise KeyError("Column (%s) not found in table" % (col_id))
382 return self.data[row_index][col_index]
383
384 def get_row_by_id(self, row_id):
385 """Returns the 'row_id' row
386 """
387 try:
388 row_index = self.row_names.index(row_id)
389 except ValueError:
390 raise KeyError("Row (%s) not found in table" % (row_id))
391 return self.data[row_index]
392
393 def get_column_by_id(self, col_id, distinct=False):
394 """Returns the 'col_id' col
395 """
396 try:
397 col_index = self.col_names.index(col_id)
398 except ValueError:
399 raise KeyError("Column (%s) not found in table" % (col_id))
400 return self.get_column(col_index, distinct)
401
402 def get_columns(self):
403 """Returns all the columns in the table
404 """
405 return [self[:, index] for index in range(len(self.col_names))]
406
407 def get_column(self, col_index, distinct=False):
408 """get a column by index"""
409 col = [row[col_index] for row in self.data]
410 if distinct:
411 col = list(set(col))
412 return col
413
414 def apply_stylesheet(self, stylesheet):
415 """Applies the stylesheet to this table
416 """
417 for instruction in stylesheet.instructions:
418 eval(instruction)
419
420
421 def transpose(self):
422 """Keeps the self object intact, and returns the transposed (rotated)
423 table.
424 """
425 transposed = Table()
426 transposed.create_rows(self.col_names)
427 transposed.create_columns(self.row_names)
428 for col_index, column in enumerate(self.get_columns()):
429 transposed.set_row(col_index, column)
430 return transposed
431
432
433 def pprint(self):
434 """returns a string representing the table in a pretty
435 printed 'text' format.
436 """
437 # The maximum row name (to know the start_index of the first col)
438 max_row_name = 0
439 for row_name in self.row_names:
440 if len(row_name) > max_row_name:
441 max_row_name = len(row_name)
442 col_start = max_row_name + 5
443
444 lines = []
445 # Build the 'first' line <=> the col_names one
446 # The first cell <=> an empty one
447 col_names_line = [' '*col_start]
448 for col_name in self.col_names:
449 col_names_line.append(col_name + ' '*5)
450 lines.append('|' + '|'.join(col_names_line) + '|')
451 max_line_length = len(lines[0])
452
453 # Build the table
454 for row_index, row in enumerate(self.data):
455 line = []
456 # First, build the row_name's cell
457 row_name = self.row_names[row_index]
458 line.append(row_name + ' '*(col_start-len(row_name)))
459
460 # Then, build all the table's cell for this line.
461 for col_index, cell in enumerate(row):
462 col_name_length = len(self.col_names[col_index]) + 5
463 data = str(cell)
464 line.append(data + ' '*(col_name_length - len(data)))
465 lines.append('|' + '|'.join(line) + '|')
466 if len(lines[-1]) > max_line_length:
467 max_line_length = len(lines[-1])
468
469 # Wrap the table with '-' to make a frame
470 lines.insert(0, '-'*max_line_length)
471 lines.append('-'*max_line_length)
472 return '\n'.join(lines)
473
474
475 def __repr__(self):
476 return repr(self.data)
477
478 def as_text(self):
479 data = []
480 # We must convert cells into strings before joining them
481 for row in self.data:
482 data.append([str(cell) for cell in row])
483 lines = ['\t'.join(row) for row in data]
484 return '\n'.join(lines)
485
486
487
488 class TableStyle:
489 """Defines a table's style
490 """
491
492 def __init__(self, table):
493
494 self._table = table
495 self.size = dict([(col_name, '1*') for col_name in table.col_names])
496 # __row_column__ is a special key to define the first column which
497 # actually has no name (<=> left most column <=> row names column)
498 self.size['__row_column__'] = '1*'
499 self.alignment = dict([(col_name, 'right')
500 for col_name in table.col_names])
501 self.alignment['__row_column__'] = 'right'
502
503 # We shouldn't have to create an entry for
504 # the 1st col (the row_column one)
505 self.units = dict([(col_name, '') for col_name in table.col_names])
506 self.units['__row_column__'] = ''
507
508 # XXX FIXME : params order should be reversed for all set() methods
509 def set_size(self, value, col_id):
510 """sets the size of the specified col_id to value
511 """
512 self.size[col_id] = value
513
514 def set_size_by_index(self, value, col_index):
515 """Allows to set the size according to the column index rather than
516 using the column's id.
517 BE CAREFUL : the '0' column is the '__row_column__' one !
518 """
519 if col_index == 0:
520 col_id = '__row_column__'
521 else:
522 col_id = self._table.col_names[col_index-1]
523
524 self.size[col_id] = value
525
526
527 def set_alignment(self, value, col_id):
528 """sets the alignment of the specified col_id to value
529 """
530 self.alignment[col_id] = value
531
532
533 def set_alignment_by_index(self, value, col_index):
534 """Allows to set the alignment according to the column index rather than
535 using the column's id.
536 BE CAREFUL : the '0' column is the '__row_column__' one !
537 """
538 if col_index == 0:
539 col_id = '__row_column__'
540 else:
541 col_id = self._table.col_names[col_index-1]
542
543 self.alignment[col_id] = value
544
545
546 def set_unit(self, value, col_id):
547 """sets the unit of the specified col_id to value
548 """
549 self.units[col_id] = value
550
551
552 def set_unit_by_index(self, value, col_index):
553 """Allows to set the unit according to the column index rather than
554 using the column's id.
555 BE CAREFUL : the '0' column is the '__row_column__' one !
556 (Note that in the 'unit' case, you shouldn't have to set a unit
557 for the 1st column (the __row__column__ one))
558 """
559 if col_index == 0:
560 col_id = '__row_column__'
561 else:
562 col_id = self._table.col_names[col_index-1]
563
564 self.units[col_id] = value
565
566
567 def get_size(self, col_id):
568 """Returns the size of the specified col_id
569 """
570 return self.size[col_id]
571
572
573 def get_size_by_index(self, col_index):
574 """Allows to get the size according to the column index rather than
575 using the column's id.
576 BE CAREFUL : the '0' column is the '__row_column__' one !
577 """
578 if col_index == 0:
579 col_id = '__row_column__'
580 else:
581 col_id = self._table.col_names[col_index-1]
582
583 return self.size[col_id]
584
585
586 def get_alignment(self, col_id):
587 """Returns the alignment of the specified col_id
588 """
589 return self.alignment[col_id]
590
591
592 def get_alignment_by_index(self, col_index):
593 """Allors to get the alignment according to the column index rather than
594 using the column's id.
595 BE CAREFUL : the '0' column is the '__row_column__' one !
596 """
597 if col_index == 0:
598 col_id = '__row_column__'
599 else:
600 col_id = self._table.col_names[col_index-1]
601
602 return self.alignment[col_id]
603
604
605 def get_unit(self, col_id):
606 """Returns the unit of the specified col_id
607 """
608 return self.units[col_id]
609
610
611 def get_unit_by_index(self, col_index):
612 """Allors to get the unit according to the column index rather than
613 using the column's id.
614 BE CAREFUL : the '0' column is the '__row_column__' one !
615 """
616 if col_index == 0:
617 col_id = '__row_column__'
618 else:
619 col_id = self._table.col_names[col_index-1]
620
621 return self.units[col_id]
622
623
624 import re
625 CELL_PROG = re.compile("([0-9]+)_([0-9]+)")
626
627 class TableStyleSheet:
628 """A simple Table stylesheet
629 Rules are expressions where cells are defined by the row_index
630 and col_index separated by an underscore ('_').
631 For example, suppose you want to say that the (2,5) cell must be
632 the sum of its two preceding cells in the row, you would create
633 the following rule :
634 2_5 = 2_3 + 2_4
635 You can also use all the math.* operations you want. For example:
636 2_5 = sqrt(2_3**2 + 2_4**2)
637 """
638
639 def __init__(self, rules = None):
640 rules = rules or []
641 self.rules = []
642 self.instructions = []
643 for rule in rules:
644 self.add_rule(rule)
645
646
647 def add_rule(self, rule):
648 """Adds a rule to the stylesheet rules
649 """
650 try:
651 source_code = ['from math import *']
652 source_code.append(CELL_PROG.sub(r'self.data[\1][\2]', rule))
653 self.instructions.append(compile('\n'.join(source_code),
654 'table.py', 'exec'))
655 self.rules.append(rule)
656 except SyntaxError:
657 print("Bad Stylesheet Rule : %s [skipped]" % rule)
658
659
660 def add_rowsum_rule(self, dest_cell, row_index, start_col, end_col):
661 """Creates and adds a rule to sum over the row at row_index from
662 start_col to end_col.
663 dest_cell is a tuple of two elements (x,y) of the destination cell
664 No check is done for indexes ranges.
665 pre:
666 start_col >= 0
667 end_col > start_col
668 """
669 cell_list = ['%d_%d'%(row_index, index) for index in range(start_col,
670 end_col + 1)]
671 rule = '%d_%d=' % dest_cell + '+'.join(cell_list)
672 self.add_rule(rule)
673
674
675 def add_rowavg_rule(self, dest_cell, row_index, start_col, end_col):
676 """Creates and adds a rule to make the row average (from start_col
677 to end_col)
678 dest_cell is a tuple of two elements (x,y) of the destination cell
679 No check is done for indexes ranges.
680 pre:
681 start_col >= 0
682 end_col > start_col
683 """
684 cell_list = ['%d_%d'%(row_index, index) for index in range(start_col,
685 end_col + 1)]
686 num = (end_col - start_col + 1)
687 rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num
688 self.add_rule(rule)
689
690
691 def add_colsum_rule(self, dest_cell, col_index, start_row, end_row):
692 """Creates and adds a rule to sum over the col at col_index from
693 start_row to end_row.
694 dest_cell is a tuple of two elements (x,y) of the destination cell
695 No check is done for indexes ranges.
696 pre:
697 start_row >= 0
698 end_row > start_row
699 """
700 cell_list = ['%d_%d'%(index, col_index) for index in range(start_row,
701 end_row + 1)]
702 rule = '%d_%d=' % dest_cell + '+'.join(cell_list)
703 self.add_rule(rule)
704
705
706 def add_colavg_rule(self, dest_cell, col_index, start_row, end_row):
707 """Creates and adds a rule to make the col average (from start_row
708 to end_row)
709 dest_cell is a tuple of two elements (x,y) of the destination cell
710 No check is done for indexes ranges.
711 pre:
712 start_row >= 0
713 end_row > start_row
714 """
715 cell_list = ['%d_%d'%(index, col_index) for index in range(start_row,
716 end_row + 1)]
717 num = (end_row - start_row + 1)
718 rule = '%d_%d=' % dest_cell + '('+'+'.join(cell_list)+')/%f'%num
719 self.add_rule(rule)
720
721
722
723 class TableCellRenderer:
724 """Defines a simple text renderer
725 """
726
727 def __init__(self, **properties):
728 """keywords should be properties with an associated boolean as value.
729 For example :
730 renderer = TableCellRenderer(units = True, alignment = False)
731 An unspecified property will have a 'False' value by default.
732 Possible properties are :
733 alignment, unit
734 """
735 self.properties = properties
736
737
738 def render_cell(self, cell_coord, table, table_style):
739 """Renders the cell at 'cell_coord' in the table, using table_style
740 """
741 row_index, col_index = cell_coord
742 cell_value = table.data[row_index][col_index]
743 final_content = self._make_cell_content(cell_value,
744 table_style, col_index +1)
745 return self._render_cell_content(final_content,
746 table_style, col_index + 1)
747
748
749 def render_row_cell(self, row_name, table, table_style):
750 """Renders the cell for 'row_id' row
751 """
752 cell_value = row_name
753 return self._render_cell_content(cell_value, table_style, 0)
754
755
756 def render_col_cell(self, col_name, table, table_style):
757 """Renders the cell for 'col_id' row
758 """
759 cell_value = col_name
760 col_index = table.col_names.index(col_name)
761 return self._render_cell_content(cell_value, table_style, col_index +1)
762
763
764
765 def _render_cell_content(self, content, table_style, col_index):
766 """Makes the appropriate rendering for this cell content.
767 Rendering properties will be searched using the
768 *table_style.get_xxx_by_index(col_index)' methods
769
770 **This method should be overridden in the derived renderer classes.**
771 """
772 return content
773
774
775 def _make_cell_content(self, cell_content, table_style, col_index):
776 """Makes the cell content (adds decoration data, like units for
777 example)
778 """
779 final_content = cell_content
780 if 'skip_zero' in self.properties:
781 replacement_char = self.properties['skip_zero']
782 else:
783 replacement_char = 0
784 if replacement_char and final_content == 0:
785 return replacement_char
786
787 try:
788 units_on = self.properties['units']
789 if units_on:
790 final_content = self._add_unit(
791 cell_content, table_style, col_index)
792 except KeyError:
793 pass
794
795 return final_content
796
797
798 def _add_unit(self, cell_content, table_style, col_index):
799 """Adds unit to the cell_content if needed
800 """
801 unit = table_style.get_unit_by_index(col_index)
802 return str(cell_content) + " " + unit
803
804
805
806 class DocbookRenderer(TableCellRenderer):
807 """Defines how to render a cell for a docboook table
808 """
809
810 def define_col_header(self, col_index, table_style):
811 """Computes the colspec element according to the style
812 """
813 size = table_style.get_size_by_index(col_index)
814 return '<colspec colname="c%d" colwidth="%s"/>\n' % \
815 (col_index, size)
816
817
818 def _render_cell_content(self, cell_content, table_style, col_index):
819 """Makes the appropriate rendering for this cell content.
820 Rendering properties will be searched using the
821 table_style.get_xxx_by_index(col_index)' methods.
822 """
823 try:
824 align_on = self.properties['alignment']
825 alignment = table_style.get_alignment_by_index(col_index)
826 if align_on:
827 return "<entry align='%s'>%s</entry>\n" % \
828 (alignment, cell_content)
829 except KeyError:
830 # KeyError <=> Default alignment
831 return "<entry>%s</entry>\n" % cell_content
832
833
834 class TableWriter:
835 """A class to write tables
836 """
837
838 def __init__(self, stream, table, style, **properties):
839 self._stream = stream
840 self.style = style or TableStyle(table)
841 self._table = table
842 self.properties = properties
843 self.renderer = None
844
845
846 def set_style(self, style):
847 """sets the table's associated style
848 """
849 self.style = style
850
851
852 def set_renderer(self, renderer):
853 """sets the way to render cell
854 """
855 self.renderer = renderer
856
857
858 def update_properties(self, **properties):
859 """Updates writer's properties (for cell rendering)
860 """
861 self.properties.update(properties)
862
863
864 def write_table(self, title = ""):
865 """Writes the table
866 """
867 raise NotImplementedError("write_table must be implemented !")
868
869
870
871 class DocbookTableWriter(TableWriter):
872 """Defines an implementation of TableWriter to write a table in Docbook
873 """
874
875 def _write_headers(self):
876 """Writes col headers
877 """
878 # Define col_headers (colstpec elements)
879 for col_index in range(len(self._table.col_names)+1):
880 self._stream.write(self.renderer.define_col_header(col_index,
881 self.style))
882
883 self._stream.write("<thead>\n<row>\n")
884 # XXX FIXME : write an empty entry <=> the first (__row_column) column
885 self._stream.write('<entry></entry>\n')
886 for col_name in self._table.col_names:
887 self._stream.write(self.renderer.render_col_cell(
888 col_name, self._table,
889 self.style))
890
891 self._stream.write("</row>\n</thead>\n")
892
893
894 def _write_body(self):
895 """Writes the table body
896 """
897 self._stream.write('<tbody>\n')
898
899 for row_index, row in enumerate(self._table.data):
900 self._stream.write('<row>\n')
901 row_name = self._table.row_names[row_index]
902 # Write the first entry (row_name)
903 self._stream.write(self.renderer.render_row_cell(row_name,
904 self._table,
905 self.style))
906
907 for col_index, cell in enumerate(row):
908 self._stream.write(self.renderer.render_cell(
909 (row_index, col_index),
910 self._table, self.style))
911
912 self._stream.write('</row>\n')
913
914 self._stream.write('</tbody>\n')
915
916
917 def write_table(self, title = ""):
918 """Writes the table
919 """
920 self._stream.write('<table>\n<title>%s></title>\n'%(title))
921 self._stream.write(
922 '<tgroup cols="%d" align="left" colsep="1" rowsep="1">\n'%
923 (len(self._table.col_names)+1))
924 self._write_headers()
925 self._write_body()
926
927 self._stream.write('</tgroup>\n</table>\n')
928
929
OLDNEW
« no previous file with comments | « third_party/logilab/logilab/common/sphinxutils.py ('k') | third_party/logilab/logilab/common/tasksqueue.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698