summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--cmd2/table_creator.py41
-rw-r--r--tests/test_table_creator.py38
3 files changed, 68 insertions, 15 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c372f74b..12583fd9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
## 2.3.0 (TBD, 2021)
* Bug Fixes
* Fixed `AttributeError` in `rl_get_prompt()` when prompt is `None`.
+* Enhancements
+ * 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.
## 2.2.0 (September 14, 2021)
* Bug Fixes
diff --git a/cmd2/table_creator.py b/cmd2/table_creator.py
index 5420ebec..29f7ad62 100644
--- a/cmd2/table_creator.py
+++ b/cmd2/table_creator.py
@@ -17,6 +17,7 @@ from enum import (
from typing import (
Any,
Deque,
+ List,
Optional,
Sequence,
Tuple,
@@ -64,8 +65,10 @@ class Column:
width: Optional[int] = None,
header_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
header_vert_align: VerticalAlignment = VerticalAlignment.BOTTOM,
+ override_header_style: bool = True,
data_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
data_vert_align: VerticalAlignment = VerticalAlignment.TOP,
+ override_data_style: bool = True,
max_data_lines: Union[int, float] = constants.INFINITY,
) -> None:
"""
@@ -77,8 +80,15 @@ class Column:
this width using word-based wrapping (defaults to actual width of header or 1 if header is blank)
:param header_horiz_align: horizontal alignment of header cells (defaults to left)
:param header_vert_align: vertical alignment of header cells (defaults to bottom)
+ :param override_header_style: if True, then the table is allowed to apply text styles to the header, which may
+ interfere with any styles the header already has. If False, the header is printed as is.
+ Table classes which apply style to headers must respect this flag. (defaults to True)
:param data_horiz_align: horizontal alignment of data cells (defaults to left)
:param data_vert_align: vertical alignment of data cells (defaults to top)
+ :param override_data_style: if True, then the table is allowed to apply text styles to the data, which may
+ interfere with any styles the data already has. If False, the data is printed as is.
+ Table classes which apply style to data must respect this flag. See the AlternatingTable
+ class for an example of this. (defaults to True)
:param max_data_lines: maximum lines allowed in a data cell. If line count exceeds this, then the final
line displayed will be truncated with an ellipsis. (defaults to INFINITY)
:raises: ValueError if width is less than 1
@@ -93,8 +103,11 @@ class Column:
self.header_horiz_align = header_horiz_align
self.header_vert_align = header_vert_align
+ self.override_header_style = override_header_style
+
self.data_horiz_align = data_horiz_align
self.data_vert_align = data_vert_align
+ self.override_data_style = override_data_style
if max_data_lines < 1:
raise ValueError("Max data lines cannot be less than 1")
@@ -856,6 +869,11 @@ class AlternatingTable(BorderedTable):
"""
Implementation of BorderedTable which uses background colors to distinguish between rows instead of row border
lines. This class can be used to create the whole table at once or one row at a time.
+
+ AlternatingTable will not apply background color to data whose Columns set override_data_style to False.
+ Background color will still be applied to those Columns's padding and fill characters. For example, to nest
+ an AlternatingTable within an AlternatingTable, set override_data_style to False on the Column which contains
+ the nested table.
"""
def __init__(
@@ -908,22 +926,27 @@ class AlternatingTable(BorderedTable):
:param row_data: data with an entry for each column in the row
:return: data row string
"""
- pre_line = '║' + self.padding * SPACE
+ # Just color the padding, not the border characters
+ colored_padding = self._apply_bg_color(self.padding * SPACE)
- inter_cell = self.padding * SPACE
+ pre_line = '║' + colored_padding
+
+ inter_cell = colored_padding
if self.column_borders:
inter_cell += '│'
- inter_cell += self.padding * SPACE
+ inter_cell += colored_padding
- post_line = self.padding * SPACE + '║'
+ post_line = colored_padding + '║'
fill_char = self._apply_bg_color(SPACE)
- pre_line = self._apply_bg_color(pre_line)
- inter_cell = self._apply_bg_color(inter_cell)
- post_line = self._apply_bg_color(post_line)
- # Apply appropriate background color to data, but don't change the original
- to_display = [self._apply_bg_color(col) for col in row_data]
+ # Apply background colors to data whose Columns allow it
+ to_display: List[Any] = []
+ for index, col in enumerate(self.cols):
+ if col.override_data_style:
+ to_display.append(self._apply_bg_color(row_data[index]))
+ else:
+ to_display.append(row_data[index])
row = self.generate_row(
row_data=to_display, fill_char=fill_char, pre_line=pre_line, inter_cell=inter_cell, post_line=post_line
diff --git a/tests/test_table_creator.py b/tests/test_table_creator.py
index e1bc8883..5efdf7c2 100644
--- a/tests/test_table_creator.py
+++ b/tests/test_table_creator.py
@@ -68,6 +68,19 @@ def test_column_creation():
tc = TableCreator([c])
assert tc.cols[0].width == ansi.style_aware_wcswidth("line with tabs")
+ # Add basic tests for override_header_style and override_data_style to make sure these members don't get removed.
+ c = Column("Column 1")
+ assert c.override_header_style is True
+ assert c.override_data_style is True
+
+ c = Column("Column 1", override_header_style=False)
+ assert c.override_header_style is False
+ assert c.override_data_style is True
+
+ c = Column("Column 1", override_data_style=False)
+ assert c.override_header_style is True
+ assert c.override_data_style is False
+
def test_column_alignment():
column_1 = Column(
@@ -580,7 +593,7 @@ def test_alternating_table_creation():
'║ Col 1 │ Col 2 ║\n'
'╠═════════════════╪═════════════════╣\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '\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'
+ '║\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[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'
'╚═════════════════╧═════════════════╝'
)
@@ -591,8 +604,7 @@ def test_alternating_table_creation():
'╔═════════════════╤═════════════════╗\n'
'║ Col 1 │ Col 2 ║\n'
'╠═════════════════╪═════════════════╣\n'
- '\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'
- '\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'
+ '║\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[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║\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[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'
'╚═════════════════╧═════════════════╝'
)
@@ -604,7 +616,7 @@ def test_alternating_table_creation():
'║ Col 1 Col 2 ║\n'
'╠══════════════════════════════════╣\n'
'║ Col 1 Row 1 Col 2 Row 1 ║\n'
- '\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'
+ '║\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[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'
'╚══════════════════════════════════╝'
)
@@ -614,7 +626,7 @@ def test_alternating_table_creation():
assert table == (
'╔═════════════════╤═════════════════╗\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '\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'
+ '║\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[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'
'╚═════════════════╧═════════════════╝'
)
@@ -626,10 +638,24 @@ def test_alternating_table_creation():
'║ Col 1 │ Col 2 ║\n'
'╠═══════════════════╪═══════════════════╣\n'
'║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
- '\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'
+ '║\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[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'
'╚═══════════════════╧═══════════════════╝'
)
+ # Make sure AlternatingTable respects override_data_style flag.
+ # Don't allow background color on data's text in second column.
+ column_2 = Column("Col 2", width=15, override_data_style=False)
+ at = AlternatingTable([column_1, column_2])
+ table = at.generate_table(row_data)
+ assert table == (
+ '╔═════════════════╤═════════════════╗\n'
+ '║ Col 1 │ Col 2 ║\n'
+ '╠═════════════════╪═════════════════╣\n'
+ '║ Col 1 Row 1 │ Col 2 Row 1 ║\n'
+ '║\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[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'
+ '╚═════════════════╧═════════════════╝'
+ )
+
# Invalid padding
with pytest.raises(ValueError) as excinfo:
AlternatingTable([column_1, column_2], padding=-1)