comparison sat_frontends/jp/common.py @ 3121:040ca99e25fe

jp (common): various Table fixes: - renamed fromDict to more accurate fromListDict - fixed handling of None filter in fromListDict - fixes show_header when no header exist - escape headers when using namedtuple, so it won't fail
author Goffi <goffi@goffi.org>
date Sat, 25 Jan 2020 21:08:37 +0100
parents d9f328374473
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3120:0c29155ac68b 3121:040ca99e25fe
22 import os.path 22 import os.path
23 import time 23 import time
24 import tempfile 24 import tempfile
25 import asyncio 25 import asyncio
26 import shlex 26 import shlex
27 import re
27 from pathlib import Path 28 from pathlib import Path
28 from sat_frontends.jp.constants import Const as C 29 from sat_frontends.jp.constants import Const as C
29 from sat.core.i18n import _ 30 from sat.core.i18n import _
30 from sat.core import exceptions 31 from sat.core import exceptions
31 from sat.tools.common import regex 32 from sat.tools.common import regex
477 the callable will get 2 arguments: 478 the callable will get 2 arguments:
478 - current column value 479 - current column value
479 - RowData with all columns values 480 - RowData with all columns values
480 if may also only use 1 argument, which will then be current col value. 481 if may also only use 1 argument, which will then be current col value.
481 the callable must return a string 482 the callable must return a string
482 if it's unicode, it will be used with .format and must countain u'{}' which will be replaced with the string 483 if it's unicode, it will be used with .format and must countain u'{}' which
484 will be replaced with the string.
483 if not None, must have same number of columns as data 485 if not None, must have same number of columns as data
484 @param use_buffer(bool): if True, bufferise output instead of printing it directly 486 @param use_buffer(bool): if True, bufferise output instead of printing it directly
485 """ 487 """
486 self.host = host 488 self.host = host
487 self._buffer = [] if use_buffer else None 489 self._buffer = [] if use_buffer else None
493 #  rows countains one list per row with columns values 495 #  rows countains one list per row with columns values
494 self.rows = [] 496 self.rows = []
495 497
496 size = None 498 size = None
497 if headers: 499 if headers:
498 row_cls = namedtuple("RowData", headers) 500 # we use a namedtuple to make the value easily accessible from filters
501 headers_safe = [re.sub(r'[^a-zA-Z_]', '_', h) for h in headers]
502 row_cls = namedtuple("RowData", headers_safe)
499 else: 503 else:
500 row_cls = tuple 504 row_cls = tuple
501 505
502 for row_data in data: 506 for row_data in data:
503 new_row = [] 507 new_row = []
510 else: 514 else:
511 try: 515 try:
512 col_value = filter_(value, row_cls(*row_data_list)) 516 col_value = filter_(value, row_cls(*row_data_list))
513 except TypeError: 517 except TypeError:
514 col_value = filter_(value) 518 col_value = filter_(value)
515 # we count size without ANSI code as they will change length of the string 519 # we count size without ANSI code as they will change length of the
516 # when it's mostly style/color changes. 520 # string when it's mostly style/color changes.
517 col_size = len(regex.ansiRemove(col_value)) 521 col_size = len(regex.ansiRemove(col_value))
518 else: 522 else:
519 col_value = str(value) 523 col_value = str(value)
520 col_size = len(col_value) 524 col_size = len(col_value)
521 new_row.append(col_value) 525 new_row.append(col_value)
555 yield default 559 yield default
556 else: 560 else:
557 raise e 561 raise e
558 562
559 @classmethod 563 @classmethod
560 def fromDict(cls, host, data, keys=None, headers=None, filters=None, defaults=None): 564 def fromListDict(
561 """Prepare a table to display it 565 cls, host, data, keys=None, headers=None, filters=None, defaults=None):
562 566 """Create a table from a list of dictionaries
563 the whole data will be read and kept into memory, 567
564 to be printed 568 each dictionary is a row of the table, keys being columns names.
569 the whole data will be read and kept into memory, to be printed
565 @param data(list[dict[unicode, unicode]]): data to create the table from 570 @param data(list[dict[unicode, unicode]]): data to create the table from
566 @param keys(iterable[unicode], None): keys to get 571 @param keys(iterable[unicode], None): keys to get
567 if None, all keys will be used 572 if None, all keys will be used
568 @param headers(iterable[unicode], None): name of the columns 573 @param headers(iterable[unicode], None): name of the columns
569 names must be in same order as keys 574 names must be in same order as keys
577 raise exceptions.DataError("You must specify keys order to used headers") 582 raise exceptions.DataError("You must specify keys order to used headers")
578 if keys is None: 583 if keys is None:
579 keys = list(data[0].keys()) 584 keys = list(data[0].keys())
580 if headers is None: 585 if headers is None:
581 headers = keys 586 headers = keys
587 if filters is None:
588 filters = {}
582 filters = [filters.get(k) for k in keys] 589 filters = [filters.get(k) for k in keys]
583 return cls( 590 return cls(
584 host, (cls.readDictValues(d, keys, defaults) for d in data), headers, filters 591 host, (cls.readDictValues(d, keys, defaults) for d in data), headers, filters
585 ) 592 )
586 593
648 655
649 @param show_header(bool): True if header need no be shown 656 @param show_header(bool): True if header need no be shown
650 @param show_borders(bool): True if borders need no be shown 657 @param show_borders(bool): True if borders need no be shown
651 @param hide_cols(None, iterable(unicode)): columns which should not be displayed 658 @param hide_cols(None, iterable(unicode)): columns which should not be displayed
652 @param head_alignment(unicode): how to align headers, can be left, center or right 659 @param head_alignment(unicode): how to align headers, can be left, center or right
653 @param columns_alignment(unicode): how to align columns, can be left, center or right 660 @param columns_alignment(unicode): how to align columns, can be left, center or
661 right
654 @param col_sep(unicode): separator betweens columns 662 @param col_sep(unicode): separator betweens columns
655 @param head_line(unicode): character to use to make line under head 663 @param head_line(unicode): character to use to make line under head
656 @param disp(callable, None): method to use to display the table 664 @param disp(callable, None): method to use to display the table
657 None to use self.host.disp 665 None to use self.host.disp
658 """ 666 """
691 self._disp( 699 self._disp(
692 top_left + top_sep.join([top * size for size in sizes]) + top_right 700 top_left + top_sep.join([top * size for size in sizes]) + top_right
693 ) 701 )
694 702
695 # headers 703 # headers
696 if show_header: 704 if show_header and self.headers is not None:
697 self._disp( 705 self._disp(
698 left 706 left
699 + self._headers(head_sep, headers, sizes, head_alignment, head_style) 707 + self._headers(head_sep, headers, sizes, head_alignment, head_style)
700 + right 708 + right
701 ) 709 )