|
6 | 6 | import enum |
7 | 7 | import re |
8 | 8 | import textwrap as textw |
9 | | -from typing import List, Iterable, Optional, Tuple, Union |
| 9 | +from typing import List, Iterable, Optional, Tuple, Union, Callable |
10 | 10 |
|
11 | 11 | from wcwidth import wcswidth |
12 | 12 |
|
@@ -36,6 +36,7 @@ def __subclasshook__(cls, C): |
36 | 36 | ANSI_ESCAPE_RE = re.compile(r'\x1b[^m]*m') |
37 | 37 | TAB_WIDTH = 4 |
38 | 38 | __version__ = '0.1.0' |
| 39 | +ELLIPSIS = '…' |
39 | 40 |
|
40 | 41 |
|
41 | 42 | def _text_wrap(text: str, width: int=70) -> List[str]: |
@@ -375,11 +376,17 @@ def _pad_columns(text: str, pad_char: str, align: Union[ColumnAlignment, str], w |
375 | 376 | class WrapMode(enum.Enum): |
376 | 377 | """Cell wrap mode""" |
377 | 378 | WRAP = 0 |
| 379 | + """Wraps the cell contents""" |
378 | 380 | WRAP_WITH_INDENT = 1 |
| 381 | + """Wraps the cell contents and indents the wrapped lines with string defined in the column's wrap prefix""" |
379 | 382 | TRUNCATE_END = 2 |
| 383 | + """Truncates the end of the line with an ellipsis to indicate truncation""" |
380 | 384 | TRUNCATE_FRONT = 3 |
| 385 | + """Truncates the beginning of the line with an ellipsis to indicate truncation""" |
381 | 386 | TRUNCATE_MIDDLE = 4 |
| 387 | + """Truncates the middle of the line with an ellipsis to indicate truncation""" |
382 | 388 | TRUNCATE_HARD = 5 |
| 389 | + """Truncates the end of the line with no truncation indicator""" |
383 | 390 |
|
384 | 391 |
|
385 | 392 | class Grid(abc.ABC): |
@@ -576,7 +583,8 @@ def header_col_divider_span(self, row_index: Union[int, None]) -> str: |
576 | 583 |
|
577 | 584 | def set_default_grid(grid: Grid) -> None: |
578 | 585 | global DEFAULT_GRID |
579 | | - DEFAULT_GRID = grid |
| 586 | + if grid is not None: |
| 587 | + DEFAULT_GRID = grid |
580 | 588 |
|
581 | 589 |
|
582 | 590 | def generate_table(rows: Iterable[Iterable], columns: Collection[Union[str, Tuple[str, dict]]]=None, |
@@ -611,24 +619,46 @@ def generate_table(rows: Iterable[Iterable], columns: Collection[Union[str, Tupl |
611 | 619 | return formatter.generate_table(rows) |
612 | 620 |
|
613 | 621 |
|
614 | | -def Column(col_name, |
615 | | - width=None, |
616 | | - attrib=None, |
617 | | - wrap_mode=None, |
618 | | - header_halign=None, |
619 | | - header_valign=None, |
620 | | - cell_halign=None, |
621 | | - cell_valign=None, |
622 | | - formatter=None, |
623 | | - obj_formatter=None): |
624 | | - """Convenience function to simplify column definition""" |
| 622 | +def Column(col_name: str, |
| 623 | + width: int=None, |
| 624 | + attrib: str=None, |
| 625 | + wrap_mode: WrapMode=None, |
| 626 | + wrap_prefix: str=None, |
| 627 | + cell_padding: int=None, |
| 628 | + header_halign: ColumnAlignment=None, |
| 629 | + header_valign: ColumnAlignment=None, |
| 630 | + cell_halign: ColumnAlignment=None, |
| 631 | + cell_valign: ColumnAlignment=None, |
| 632 | + formatter: Callable=None, |
| 633 | + obj_formatter: Callable=None): |
| 634 | + """ |
| 635 | + Processes column options and generates a tuple in the format the TableFormatter expects |
| 636 | +
|
| 637 | + :param col_name: Column name to display |
| 638 | + :param width: Number of displayed terminal characters. Unicode wide characters count as 2 displayed characters. |
| 639 | + :param attrib: The name of the object attribute to look up for cell contents on this column |
| 640 | + :param wrap_mode: Defines how to handle long cells that must be wropped or truncated |
| 641 | + :param wrap_prefix: String to display at the beginning of each wrapped line in a cell |
| 642 | + :param cell_padding: Number of padding spaces to the left and right of each cell |
| 643 | + :param header_halign: Horizontal alignment of the column header |
| 644 | + :param header_valign: Vertical alignment of the column header |
| 645 | + :param cell_halign: Horizontal alignment of the cells in this column |
| 646 | + :param cell_valign: Vertical alignment of the cells in this column |
| 647 | + :param formatter: Callable that can process the value in this column for display. |
| 648 | + :param obj_formatter: Callable that processes the row object to generate content for this column |
| 649 | + :return: A column tuple the TableFormatter expects |
| 650 | + """ |
625 | 651 | opts = dict() |
626 | 652 | if width is not None: |
627 | 653 | opts[TableFormatter.COL_OPT_WIDTH] = width |
628 | 654 | if attrib is not None: |
629 | 655 | opts[TableFormatter.COL_OPT_ATTRIB_NAME] = attrib |
630 | 656 | if wrap_mode is not None: |
631 | 657 | opts[TableFormatter.COL_OPT_WRAP_MODE] = wrap_mode |
| 658 | + if wrap_prefix is not None: |
| 659 | + opts[TableFormatter.COL_OPT_WRAP_INDENT_PREFIX] = wrap_prefix |
| 660 | + if cell_padding is not None: |
| 661 | + opts[TableFormatter.COL_OPT_CELL_PADDING] = cell_padding |
632 | 662 | if header_halign is not None: |
633 | 663 | opts[TableFormatter.COL_OPT_HEADER_HALIGN] = header_halign |
634 | 664 | if header_valign is not None: |
@@ -685,7 +715,7 @@ def __init__(self, |
685 | 715 | transpose=False, |
686 | 716 | row_show_header=False): |
687 | 717 | """ |
688 | | - :param columns: list of tuples: (column name, [max width]) |
| 718 | + :param columns: list of either column names or tuples of (column name, dict of column options) |
689 | 719 | :param cell_padding: number of spaces to pad to the left/right of each column |
690 | 720 | """ |
691 | 721 | self._columns = columns |
@@ -1006,12 +1036,15 @@ def generate_table(self, entries: List[Iterable], force_transpose=False): |
1006 | 1036 | subsequent_indent=prefix) |
1007 | 1037 | field_entry.extend(wrapper.wrap(field_line_text)) |
1008 | 1038 | elif wrap_mode == WrapMode.TRUNCATE_END: |
1009 | | - field_entry.append(field_line_text[:(line_max - 3)] + '...') |
| 1039 | + ell_len = _wcswidth(ELLIPSIS) |
| 1040 | + field_entry.append(field_line_text[:(line_max - ell_len)] + ELLIPSIS) |
1010 | 1041 | elif wrap_mode == WrapMode.TRUNCATE_FRONT: |
1011 | | - field_entry.append('...' + field_line_text[line_length - line_max + 3:]) |
| 1042 | + ell_len = _wcswidth(ELLIPSIS) |
| 1043 | + field_entry.append(ELLIPSIS + field_line_text[line_length - line_max + ell_len:]) |
1012 | 1044 | elif wrap_mode == WrapMode.TRUNCATE_MIDDLE: |
1013 | | - field_entry.append(field_line_text[: (line_max - 5) // 2] + ' ... ' + |
1014 | | - field_line_text[line_length - ((line_max - 5) // 2):]) |
| 1045 | + ell_len = _wcswidth(ELLIPSIS) + 2 |
| 1046 | + field_entry.append(field_line_text[: (line_max - ell_len) // 2] + ' ' + ELLIPSIS + ' ' + |
| 1047 | + field_line_text[line_length - ((line_max - ell_len) // 2):]) |
1015 | 1048 | elif wrap_mode == WrapMode.TRUNCATE_HARD: |
1016 | 1049 | field_entry.append(field_line_text[:line_max]) |
1017 | 1050 | else: |
|
0 commit comments