Skip to content

Commit aa112e0

Browse files
kmvanbrunttleonhardt
authored andcommitted
Refactor: Isolate column rendering from printing.
This change moves the core logic for rendering columns from the columnize() method to a new helper method, render_columns().
1 parent 18831d1 commit aa112e0

File tree

3 files changed

+55
-24
lines changed

3 files changed

+55
-24
lines changed

cmd2/cmd2.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4243,22 +4243,26 @@ def _print_documented_command_topics(self, header: str, cmds: list[str], verbose
42434243
category_grid.add_row(topics_table)
42444244
self.poutput(category_grid, "")
42454245

4246-
def columnize(self, str_list: list[str] | None, display_width: int = 80) -> None:
4247-
"""Display a list of single-line strings as a compact set of columns.
4246+
def render_columns(self, str_list: list[str] | None, display_width: int = 80) -> str:
4247+
"""Render a list of single-line strings as a compact set of columns.
42484248
4249-
Override of cmd's columnize() to handle strings with ANSI style sequences and wide characters.
4249+
This method correctly handles strings containing ANSI escape codes and
4250+
full-width characters (like those used in CJK languages). Each column is
4251+
only as wide as necessary and columns are separated by two spaces.
42504252
4251-
Each column is only as wide as necessary.
4252-
Columns are separated by two spaces (one was not legible enough).
4253+
:param str_list: list of single-line strings to display
4254+
:param display_width: max number of display columns to fit into
4255+
:return: a string containing the columnized output
42534256
"""
42544257
if not str_list:
4255-
self.poutput("<empty>")
4256-
return
4258+
return ""
42574259

42584260
size = len(str_list)
42594261
if size == 1:
4260-
self.poutput(str_list[0])
4261-
return
4262+
return str_list[0]
4263+
4264+
rows: list[str] = []
4265+
42624266
# Try every row count from 1 upwards
42634267
for nrows in range(1, len(str_list)):
42644268
ncols = (size + nrows - 1) // nrows
@@ -4294,7 +4298,22 @@ def columnize(self, str_list: list[str] | None, display_width: int = 80) -> None
42944298
del texts[-1]
42954299
for col in range(len(texts)):
42964300
texts[col] = su.align_left(texts[col], width=colwidths[col])
4297-
self.poutput(" ".join(texts))
4301+
rows.append(" ".join(texts))
4302+
4303+
return "\n".join(rows)
4304+
4305+
def columnize(self, str_list: list[str] | None, display_width: int = 80) -> None:
4306+
"""Display a list of single-line strings as a compact set of columns.
4307+
4308+
Override of cmd's columnize() that uses the render_columns() method.
4309+
The method correctly handles strings with ANSI style sequences and
4310+
full-width characters (like those used in CJK languages).
4311+
4312+
:param str_list: list of single-line strings to display
4313+
:param display_width: max number of display columns to fit into
4314+
"""
4315+
columnized_strs = self.render_columns(str_list, display_width)
4316+
self.poutput(columnized_strs)
42984317

42994318
@staticmethod
43004319
def _build_shortcuts_parser() -> Cmd2ArgumentParser:

cmd2/string_utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""Provides string utility functions.
22
33
This module offers a collection of string utility functions built on the Rich library.
4-
These utilities are designed to correctly handle strings with complex formatting, such as
5-
ANSI escape codes and full-width characters (like those used in CJK languages), which the
6-
standard Python library's string methods do not properly support.
4+
These utilities are designed to correctly handle strings with ANSI escape codes and
5+
full-width characters (like those used in CJK languages).
76
"""
87

98
from rich.align import AlignMethod
@@ -107,7 +106,7 @@ def strip_style(val: str) -> str:
107106
def str_width(val: str) -> int:
108107
"""Return the display width of a string.
109108
110-
This is intended for single line strings.
109+
This is intended for single-line strings.
111110
Replace tabs with spaces before calling this.
112111
113112
:param val: the string being measured

tests/test_cmd2.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,22 +1355,35 @@ def test_help_verbose_with_fake_command(capsys) -> None:
13551355
assert cmds[1] not in out
13561356

13571357

1358-
def test_columnize_empty_list(capsys) -> None:
1359-
help_app = HelpApp()
1358+
def test_render_columns_no_strs(help_app: HelpApp) -> None:
13601359
no_strs = []
1361-
help_app.columnize(no_strs)
1362-
out, err = capsys.readouterr()
1363-
assert "<empty>" in out
1360+
result = help_app.render_columns(no_strs)
1361+
assert result == ""
13641362

13651363

1366-
def test_columnize_too_wide(capsys) -> None:
1367-
help_app = HelpApp()
1364+
def test_render_columns_one_str(help_app: HelpApp) -> None:
1365+
one_str = ["one_string"]
1366+
result = help_app.render_columns(one_str)
1367+
assert result == "one_string"
1368+
1369+
1370+
def test_render_columns_too_wide(help_app: HelpApp) -> None:
13681371
commands = ["kind_of_long_string", "a_slightly_longer_string"]
1369-
help_app.columnize(commands, display_width=10)
1372+
result = help_app.render_columns(commands, display_width=10)
1373+
1374+
expected = "kind_of_long_string \na_slightly_longer_string"
1375+
assert result == expected
1376+
1377+
1378+
def test_columnize(capsys: pytest.CaptureFixture[str]) -> None:
1379+
help_app = HelpApp()
1380+
items = ["one", "two"]
1381+
help_app.columnize(items)
13701382
out, err = capsys.readouterr()
13711383

1372-
expected = "kind_of_long_string \na_slightly_longer_string\n"
1373-
assert expected == out
1384+
# poutput() adds a newline at the end.
1385+
expected = "one two\n"
1386+
assert out == expected
13741387

13751388

13761389
class HelpCategoriesApp(cmd2.Cmd):

0 commit comments

Comments
 (0)