Skip to content

Commit 99c88f1

Browse files
committed
rf(cmdline): Simplify table2string implementation
The previous table2string implementation reimplemented Python formatting, calculating spaces for aligning left, right or center. This patch just translates our mini-language into the Python mini-language, and updates the tests. Previously, when centering required adding an odd number of spaces, we added the extra to the left, while Python adds to the right. This difference does not seem worth preserving. This also avoids allocating a numpy array in order to transpose by using the `zip(*list)` trick.
1 parent 59733ff commit 99c88f1

File tree

2 files changed

+43
-43
lines changed

2 files changed

+43
-43
lines changed

nibabel/cmdline/tests/test_utils.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,37 @@
2828

2929

3030
def test_table2string():
31-
assert table2string([['A', 'B', 'C', 'D'], ['E', 'F', 'G', 'H']]) == 'A B C D\nE F G H\n'
31+
# Trivial case should do something sensible
32+
assert table2string([]) == '\n'
3233
assert (
3334
table2string(
34-
[
35-
["Let's", 'Make', 'Tests', 'And'],
36-
['Have', 'Lots', 'Of', 'Fun'],
37-
['With', 'Python', 'Guys', '!'],
38-
]
35+
[['A', 'B', 'C', 'D'],
36+
['E', 'F', 'G', 'H']]
37+
) == (
38+
'A B C D\n'
39+
'E F G H\n'
3940
)
40-
== "Let's Make Tests And\n Have Lots Of Fun" + '\n With Python Guys !\n'
41-
)
41+
) # fmt: skip
42+
assert (
43+
table2string(
44+
[["Let's", 'Make', 'Tests', 'And'],
45+
['Have', 'Lots', 'Of', 'Fun'],
46+
['With', 'Python', 'Guys', '!']]
47+
) == (
48+
"Let's Make Tests And\n"
49+
'Have Lots Of Fun\n'
50+
'With Python Guys !\n'
51+
)
52+
) # fmt: skip
53+
assert (
54+
table2string(
55+
[['This', 'Table', '@lIs', 'Ragged'],
56+
['And', '@rit', 'uses', '@csome', 'alignment', 'markup']]
57+
) == (
58+
'This Table Is Ragged\n'
59+
'And it uses some alignment markup\n'
60+
)
61+
) # fmt: skip
4262

4363

4464
def test_ap():

nibabel/cmdline/utils.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212

1313
# global verbosity switch
1414
import re
15-
from io import StringIO
16-
from math import ceil
17-
18-
import numpy as np
1915

2016
verbose_level = 0
2117

@@ -42,32 +38,28 @@ def table2string(table, out=None):
4238
table : list of lists of strings
4339
What is aimed to be printed
4440
out : None or stream
45-
Where to print. If None -- will print and return string
41+
Where to print. If None, return string
4642
4743
Returns
4844
-------
4945
string if out was None
5046
"""
5147

52-
print2string = out is None
53-
if print2string:
54-
out = StringIO()
55-
5648
# equalize number of elements in each row
5749
nelements_max = len(table) and max(len(x) for x in table)
5850

51+
table = [row + [''] * (nelements_max - len(row)) for row in table]
5952
for i, table_ in enumerate(table):
6053
table[i] += [''] * (nelements_max - len(table_))
6154

62-
# figure out lengths within each column
63-
atable = np.asarray(table)
6455
# eat whole entry while computing width for @w (for wide)
6556
markup_strip = re.compile('^@([lrc]|w.*)')
66-
col_width = [max(len(markup_strip.sub('', x)) for x in column) for column in atable.T]
67-
string = ''
68-
for i, table_ in enumerate(table):
69-
string_ = ''
70-
for j, item in enumerate(table_):
57+
col_width = [max(len(markup_strip.sub('', x)) for x in column) for column in zip(*table)]
58+
trans = str.maketrans("lrcw", "<>^^")
59+
lines = []
60+
for row in table:
61+
line = []
62+
for item, width in zip(row, col_width):
7163
item = str(item)
7264
if item.startswith('@'):
7365
align = item[1]
@@ -77,26 +69,14 @@ def table2string(table, out=None):
7769
else:
7870
align = 'c'
7971

80-
nspacesl = max(ceil((col_width[j] - len(item)) / 2.0), 0)
81-
nspacesr = max(col_width[j] - nspacesl - len(item), 0)
82-
83-
if align in ('w', 'c'):
84-
pass
85-
elif align == 'l':
86-
nspacesl, nspacesr = 0, nspacesl + nspacesr
87-
elif align == 'r':
88-
nspacesl, nspacesr = nspacesl + nspacesr, 0
89-
else:
90-
raise RuntimeError(f'Should not get here with align={align}')
91-
92-
string_ += '%%%ds%%s%%%ds ' % (nspacesl, nspacesr) % ('', item, '')
93-
string += string_.rstrip() + '\n'
94-
out.write(string)
72+
line.append(f'{item:{align.translate(trans)}{width}}')
73+
lines.append(' '.join(line).rstrip())
9574

96-
if print2string:
97-
value = out.getvalue()
98-
out.close()
99-
return value
75+
ret = '\n'.join(lines) + '\n'
76+
if out is not None:
77+
out.write(ret)
78+
else:
79+
return ret
10080

10181

10282
def ap(helplist, format_, sep=', '):

0 commit comments

Comments
 (0)