Skip to content
This repository was archived by the owner on Feb 20, 2026. It is now read-only.

Commit 4101528

Browse files
authored
Merge pull request #58 from TankerHQ/dm/info-table-headers
Fix using info_table with `keys="headers"`
2 parents c412f8b + c083f6a commit 4101528

7 files changed

Lines changed: 289 additions & 171 deletions

File tree

cli_ui/__init__.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union, IO
1+
from typing import IO, Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union
22

33
Dict, Type
44

@@ -15,8 +15,8 @@
1515
import traceback
1616

1717
import colorama
18-
import unidecode
1918
import tabulate
19+
import unidecode
2020

2121
ConfigValue = Union[None, bool, str]
2222
FileObj = IO[str]
@@ -409,17 +409,31 @@ def tabs(num: int) -> str:
409409
def info_table(
410410
data: Any, *, headers: Union[str, Sequence[str]] = (), fileobj: FileObj = sys.stdout
411411
) -> None:
412-
colored_data = list()
413-
plain_data = list()
414-
for row in data:
415-
colored_row = list()
416-
plain_row = list()
417-
for item in row:
418-
colored_str, plain_str = process_tokens(item, end="")
419-
colored_row.append(colored_str)
420-
plain_row.append(plain_str)
421-
colored_data.append(colored_row)
422-
plain_data.append(plain_row)
412+
if headers == "keys":
413+
colored_data: Any = dict()
414+
plain_data: Any = dict()
415+
for key, sequence in data.items():
416+
colored_sequence = list()
417+
plain_sequence = list()
418+
for item in sequence:
419+
colored_str, plain_str = process_tokens(item, end="")
420+
colored_sequence.append(colored_str)
421+
plain_sequence.append(plain_str)
422+
colored_key, plain_key = process_tokens(key, end="")
423+
colored_data[colored_key] = colored_sequence
424+
plain_data[plain_key] = plain_sequence
425+
else:
426+
colored_data = list()
427+
plain_data = list()
428+
for row in data:
429+
colored_row = list()
430+
plain_row = list()
431+
for item in row:
432+
colored_str, plain_str = process_tokens(item, end="")
433+
colored_row.append(colored_str)
434+
plain_row.append(plain_str)
435+
colored_data.append(colored_row)
436+
plain_data.append(plain_row)
423437
if config_color(fileobj):
424438
data_for_tabulate = colored_data
425439
else:

cli_ui/tests/conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from typing import Any, Iterator, Optional
2-
import cli_ui
31
import re
2+
from typing import Any, Iterator, Optional
43

54
import pytest
65

6+
import cli_ui
7+
78

89
class MessageRecorder:
910
""" Helper class to tests emitted messages """

cli_ui/tests/test_cli_ui.py

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
from typing import Iterator
21
import datetime
32
import io
43
import operator
54
import re
5+
from typing import Iterator
66
from unittest import mock
77

8-
import colorama.ansi
98
import colorama
9+
import colorama.ansi
1010
import pytest
1111

1212
import cli_ui
1313
from cli_ui.tests.conftest import MessageRecorder
1414

15-
16-
def assert_equal_strings(a: str, b: str) -> bool:
17-
return a.split() == b.split()
15+
BLUE = colorama.Fore.BLUE
16+
GREEN = colorama.Fore.GREEN
17+
RED = colorama.Fore.RED
18+
RESET_ALL = colorama.Style.RESET_ALL
19+
BRIGHT = colorama.Style.BRIGHT
1820

1921

2022
class SmartTTY(io.StringIO):
@@ -59,16 +61,10 @@ def test_info_stdout_is_a_tty(smart_tty: io.StringIO) -> None:
5961
)
6062
# fmt: on
6163
expected = (
62-
colorama.Fore.RED
63-
+ "this is red "
64-
+ colorama.Style.RESET_ALL
65-
+ colorama.Fore.GREEN
66-
+ "this is green"
67-
+ colorama.Style.RESET_ALL
68-
+ "\n"
64+
RED + "this is red " + RESET_ALL + GREEN + "this is green" + "\n" + RESET_ALL
6965
)
7066
actual = smart_tty.getvalue()
71-
assert_equal_strings(actual, expected)
67+
assert actual == expected
7268

7369

7470
def test_update_title(smart_tty: SmartTTY) -> None:
@@ -78,17 +74,13 @@ def test_update_title(smart_tty: SmartTTY) -> None:
7874
fileobj=smart_tty,
7975
update_title=True
8076
)
81-
# fmt: on
8277
expected = (
83-
colorama.ansi.set_title("Something bold")
84-
+ "Something "
85-
+ colorama.Style.BRIGHT
86-
+ "bold"
87-
+ colorama.Style.RESET_ALL
88-
+ "\n"
78+
"\x1b]0;Something bold\n\x07"
79+
f"Something {BRIGHT}bold\n{RESET_ALL}"
8980
)
81+
# fmt: on
9082
actual = smart_tty.getvalue()
91-
assert_equal_strings(actual, expected)
83+
assert actual == expected
9284

9385

9486
def test_info_stdout_is_not_a_tty(dumb_tty: DumbTTY) -> None:
@@ -101,23 +93,16 @@ def test_info_stdout_is_not_a_tty(dumb_tty: DumbTTY) -> None:
10193
# fmt: on
10294
expected = "this is red this is green\n"
10395
actual = dumb_tty.getvalue()
104-
assert_equal_strings(actual, expected)
96+
assert actual == expected
10597

10698

10799
def test_info_characters(smart_tty: SmartTTY) -> None:
108100
cli_ui.info(
109101
"Doing stuff", cli_ui.ellipsis, "sucess", cli_ui.check, fileobj=smart_tty
110102
)
111103
actual = smart_tty.getvalue()
112-
expected = (
113-
"Doing stuff "
114-
+ colorama.Style.RESET_ALL
115-
+ "…"
116-
+ " sucess "
117-
+ colorama.Fore.GREEN
118-
+ "✓"
119-
)
120-
assert_equal_strings(actual, expected)
104+
expected = f"Doing stuff {RESET_ALL}{RESET_ALL}{RESET_ALL}sucess {RESET_ALL}{GREEN}{RESET_ALL}\n{RESET_ALL}"
105+
assert actual == expected
121106

122107

123108
def test_timestamp(dumb_tty: DumbTTY, toggle_timestamp: None) -> None:
@@ -128,6 +113,80 @@ def test_timestamp(dumb_tty: DumbTTY, toggle_timestamp: None) -> None:
128113
assert datetime.datetime.strptime(match.groups()[0], "%Y-%m-%d %H:%M:%S")
129114

130115

116+
def test_table_with_lists_no_color(dumb_tty: DumbTTY) -> None:
117+
headers = ["name", "score"]
118+
data = [
119+
[(cli_ui.bold, "John"), (cli_ui.green, 10)],
120+
[(cli_ui.bold, "Jane"), (cli_ui.green, 5)],
121+
]
122+
cli_ui.info_table(data, headers=headers, fileobj=dumb_tty)
123+
actual = dumb_tty.getvalue()
124+
# fmt: off
125+
expected = (
126+
"name score\n"
127+
"------ -------\n"
128+
"John 10\n"
129+
"Jane 5\n"
130+
)
131+
# fmt: on
132+
assert actual == expected
133+
134+
135+
def test_table_with_dict_no_color(dumb_tty: DumbTTY) -> None:
136+
data = {
137+
(cli_ui.bold, "Name"): [(cli_ui.green, "Alice"), (cli_ui.green, "Bob")],
138+
(cli_ui.bold, "Age"): [(cli_ui.blue, 24), (cli_ui.blue, 9)],
139+
}
140+
cli_ui.info_table(data, headers="keys", fileobj=dumb_tty)
141+
actual = dumb_tty.getvalue()
142+
# fmt: off
143+
expected = (
144+
"Name Age\n"
145+
"------ -----\n"
146+
"Alice 24\n"
147+
"Bob 9\n"
148+
)
149+
# fmt: on
150+
assert actual == expected
151+
152+
153+
def test_table_with_dict_and_color(smart_tty: SmartTTY) -> None:
154+
data = {
155+
(cli_ui.bold, "Name",): [(cli_ui.green, "Alice"), (cli_ui.green, "Bob")],
156+
(cli_ui.bold, "Age",): [(cli_ui.blue, 24), (cli_ui.blue, 9)],
157+
}
158+
cli_ui.info_table(data, headers="keys", fileobj=smart_tty)
159+
actual = smart_tty.getvalue()
160+
# fmt: off
161+
expected = (
162+
f"{BRIGHT}Name{RESET_ALL} {BRIGHT}Age{RESET_ALL}\n"
163+
"------ -----\n"
164+
f"{GREEN}Alice{RESET_ALL} {BLUE}24{RESET_ALL}\n"
165+
f"{GREEN}Bob{RESET_ALL} {BLUE}9{RESET_ALL}\n"
166+
)
167+
# fmt: on
168+
assert actual == expected
169+
170+
171+
def test_table_with_lists_with_color(smart_tty: SmartTTY) -> None:
172+
headers = ["name", "score"]
173+
data = [
174+
[(cli_ui.bold, "John"), (cli_ui.green, 10)],
175+
[(cli_ui.bold, "Jane"), (cli_ui.green, 5)],
176+
]
177+
cli_ui.info_table(data, headers=headers, fileobj=smart_tty)
178+
actual = smart_tty.getvalue()
179+
# fmt: off
180+
expected = (
181+
"name score\n"
182+
"------ -------\n"
183+
f"{BRIGHT}John{RESET_ALL} {GREEN}10{RESET_ALL}\n"
184+
f"{BRIGHT}Jane{RESET_ALL} {GREEN}5{RESET_ALL}\n"
185+
)
186+
# fmt: on
187+
assert actual == expected
188+
189+
131190
def test_record_message(message_recorder: MessageRecorder) -> None:
132191
cli_ui.info_1("This is foo")
133192
assert message_recorder.find("foo")

docs/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
----------
33

4+
v0.12.0
5+
+++++++
6+
7+
* Fix using `info_table` with `keys="headers"`
8+
49
v0.11.0
510
+++++++
611

lint.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set -x
22
set -e
33

44
poetry run black --check .
5+
poetry run isort --check .
56
poetry run mypy cli_ui
67
poetry run pyflakes cli_ui/__init__.py cli_ui/tests/test_cli_ui.py
78
poetry run sphinx-build -W docs docs/_build/html

0 commit comments

Comments
 (0)