Skip to content

Commit e35ab9c

Browse files
committed
Added settings to Column class which prevent a table from overriding existing styles in header
and/or data text. These were added to support nesting an AlternatingTable within an AlternatingTable, but other custom table classes can also use these settings. AlternatingTable no longer applies background color to outer borders. This was done to improve appearance since the background color extended beyond the borders of the table.
1 parent c5aaee5 commit e35ab9c

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
## 2.3.0 (TBD, 2021)
22
* Bug Fixes
33
* Fixed `AttributeError` in `rl_get_prompt()` when prompt is `None`.
4+
* Enhancements
5+
* Added settings to Column class which prevent a table from overriding existing styles in header
6+
and/or data text. These were added to support nesting an AlternatingTable within an AlternatingTable,
7+
but other custom table classes can also use these settings.
8+
* AlternatingTable no longer applies background color to outer borders. This was done to improve appearance
9+
since the background color extended beyond the borders of the table.
410

511
## 2.2.0 (September 14, 2021)
612
* Bug Fixes

cmd2/table_creator.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from typing import (
1818
Any,
1919
Deque,
20+
List,
2021
Optional,
2122
Sequence,
2223
Tuple,
@@ -64,8 +65,10 @@ def __init__(
6465
width: Optional[int] = None,
6566
header_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
6667
header_vert_align: VerticalAlignment = VerticalAlignment.BOTTOM,
68+
override_header_style: bool = True,
6769
data_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
6870
data_vert_align: VerticalAlignment = VerticalAlignment.TOP,
71+
override_data_style: bool = True,
6972
max_data_lines: Union[int, float] = constants.INFINITY,
7073
) -> None:
7174
"""
@@ -77,8 +80,15 @@ def __init__(
7780
this width using word-based wrapping (defaults to actual width of header or 1 if header is blank)
7881
:param header_horiz_align: horizontal alignment of header cells (defaults to left)
7982
:param header_vert_align: vertical alignment of header cells (defaults to bottom)
83+
:param override_header_style: if True, then the table is allowed to apply text styles to the header, which may
84+
interfere with any styles the header already has. If False, the header is printed as is.
85+
Table classes which apply style to headers must respect this flag. (defaults to True)
8086
:param data_horiz_align: horizontal alignment of data cells (defaults to left)
8187
:param data_vert_align: vertical alignment of data cells (defaults to top)
88+
:param override_data_style: if True, then the table is allowed to apply text styles to the data, which may
89+
interfere with any styles the data already has. If False, the data is printed as is.
90+
Table classes which apply style to data must respect this flag. See the AlternatingTable
91+
class for an example of this. (defaults to True)
8292
:param max_data_lines: maximum lines allowed in a data cell. If line count exceeds this, then the final
8393
line displayed will be truncated with an ellipsis. (defaults to INFINITY)
8494
:raises: ValueError if width is less than 1
@@ -93,8 +103,11 @@ def __init__(
93103

94104
self.header_horiz_align = header_horiz_align
95105
self.header_vert_align = header_vert_align
106+
self.override_header_style = override_header_style
107+
96108
self.data_horiz_align = data_horiz_align
97109
self.data_vert_align = data_vert_align
110+
self.override_data_style = override_data_style
98111

99112
if max_data_lines < 1:
100113
raise ValueError("Max data lines cannot be less than 1")
@@ -856,6 +869,13 @@ class AlternatingTable(BorderedTable):
856869
"""
857870
Implementation of BorderedTable which uses background colors to distinguish between rows instead of row border
858871
lines. This class can be used to create the whole table at once or one row at a time.
872+
873+
AlternatingTable will not apply background color to data whose Columns set override_data_style to False.
874+
Background color will still be applied to those Columns's padding and fill characters.
875+
876+
To nest an AlternatingTable within another AlternatingTable, set override_data_style to False on the Column
877+
which contains the nested table. That will prevent the current row's background color from affecting the colors
878+
of the nested table.
859879
"""
860880

861881
def __init__(
@@ -908,22 +928,27 @@ def generate_data_row(self, row_data: Sequence[Any]) -> str:
908928
:param row_data: data with an entry for each column in the row
909929
:return: data row string
910930
"""
911-
pre_line = '║' + self.padding * SPACE
931+
# Only color the padding and not the outer border characters
932+
pre_line = '║' + self._apply_bg_color(self.padding * SPACE)
912933

913934
inter_cell = self.padding * SPACE
914935
if self.column_borders:
915936
inter_cell += '│'
916937
inter_cell += self.padding * SPACE
938+
inter_cell = self._apply_bg_color(inter_cell)
917939

918-
post_line = self.padding * SPACE + '║'
940+
# Only color the padding and not the outer border characters
941+
post_line = self._apply_bg_color(self.padding * SPACE) + '║'
919942

920943
fill_char = self._apply_bg_color(SPACE)
921-
pre_line = self._apply_bg_color(pre_line)
922-
inter_cell = self._apply_bg_color(inter_cell)
923-
post_line = self._apply_bg_color(post_line)
924944

925-
# Apply appropriate background color to data, but don't change the original
926-
to_display = [self._apply_bg_color(col) for col in row_data]
945+
# Apply background colors to data whose Columns allow it
946+
to_display: List[Any] = []
947+
for index, col in enumerate(self.cols):
948+
if col.override_data_style:
949+
to_display.append(self._apply_bg_color(row_data[index]))
950+
else:
951+
to_display.append(row_data[index])
927952

928953
row = self.generate_row(
929954
row_data=to_display, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line

tests/test_table_creator.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ def test_column_creation():
6868
tc = TableCreator([c])
6969
assert tc.cols[0].width == ansi.style_aware_wcswidth("line with tabs")
7070

71+
# Add basic tests for override_header_style and override_data_style to make sure these members don't get removed.
72+
c = Column("Column 1")
73+
assert c.override_header_style is True
74+
assert c.override_data_style is True
75+
76+
c = Column("Column 1", override_header_style=False)
77+
assert c.override_header_style is False
78+
assert c.override_data_style is True
79+
80+
c = Column("Column 1", override_data_style=False)
81+
assert c.override_header_style is True
82+
assert c.override_data_style is False
83+
7184

7285
def test_column_alignment():
7386
column_1 = Column(
@@ -580,7 +593,7 @@ def test_alternating_table_creation():
580593
'║ Col 1 │ Col 2 ║\n'
581594
'╠═════════════════╪═════════════════╣\n'
582595
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
583-
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
596+
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
584597
'╚═════════════════╧═════════════════╝'
585598
)
586599

@@ -591,8 +604,8 @@ def test_alternating_table_creation():
591604
'╔═════════════════╤═════════════════╗\n'
592605
'║ Col 1 │ Col 2 ║\n'
593606
'╠═════════════════╪═════════════════╣\n'
594-
'\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\n'
595-
'\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m │ \x1b[49m\x1b[0m\x1b[42mCol 2 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\n'
607+
'\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 1 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m │ \x1b[49m\x1b[0m\x1b[104mCol 2 Row 1\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\n'
608+
'\x1b[42m \x1b[49m\x1b[0m\x1b[42mCol 1 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m │ \x1b[49m\x1b[0m\x1b[42mCol 2 Row 2\x1b[49m\x1b[0m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[42m \x1b[49m\x1b[0m\x1b[42m \x1b[49m\n'
596609
'╚═════════════════╧═════════════════╝'
597610
)
598611

@@ -604,7 +617,7 @@ def test_alternating_table_creation():
604617
'║ Col 1 Col 2 ║\n'
605618
'╠══════════════════════════════════╣\n'
606619
'║ Col 1 Row 1 Col 2 Row 1 ║\n'
607-
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
620+
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
608621
'╚══════════════════════════════════╝'
609622
)
610623

@@ -614,7 +627,7 @@ def test_alternating_table_creation():
614627
assert table == (
615628
'╔═════════════════╤═════════════════╗\n'
616629
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
617-
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
630+
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
618631
'╚═════════════════╧═════════════════╝'
619632
)
620633

@@ -626,10 +639,24 @@ def test_alternating_table_creation():
626639
'║ Col 1 │ Col 2 ║\n'
627640
'╠═══════════════════╪═══════════════════╣\n'
628641
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
629-
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
642+
'\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0m\x1b[100mCol 2 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m\n'
630643
'╚═══════════════════╧═══════════════════╝'
631644
)
632645

646+
# Make sure AlternatingTable respects override_data_style flag.
647+
# Don't allow background color on data's text in second column.
648+
column_2 = Column("Col 2", width=15, override_data_style=False)
649+
at = AlternatingTable([column_1, column_2])
650+
table = at.generate_table(row_data)
651+
assert table == (
652+
'╔═════════════════╤═════════════════╗\n'
653+
'║ Col 1 │ Col 2 ║\n'
654+
'╠═════════════════╪═════════════════╣\n'
655+
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
656+
'║\x1b[100m \x1b[49m\x1b[0m\x1b[100mCol 1 Row 2\x1b[49m\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m │ \x1b[49m\x1b[0mCol 2 Row 2\x1b[0m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[100m \x1b[49m\x1b[0m\x1b[100m \x1b[49m║\n'
657+
'╚═════════════════╧═════════════════╝'
658+
)
659+
633660
# Invalid padding
634661
with pytest.raises(ValueError) as excinfo:
635662
AlternatingTable([column_1, column_2], padding=-1)

0 commit comments

Comments
 (0)