Skip to content

Commit a29d29d

Browse files
authored
Merge pull request #7053 from chu11/issue7030_flux_jobs_column_width
python: consider output header w/ expandable width
2 parents 98e5965 + 8c72102 commit a29d29d

File tree

5 files changed

+40
-5
lines changed

5 files changed

+40
-5
lines changed

src/bindings/python/flux/util.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ def format(self, obj):
948948
raise KeyError(f"Invalid format field {exc} for {typestr}")
949949
return retval
950950

951-
def filter(self, items):
951+
def filter(self, items, no_header=False):
952952
"""
953953
Check for format fields that are prefixed with `?:` (e.g. "?:{name}"),
954954
and filter them out of the current format string if they result in an
@@ -960,6 +960,10 @@ def filter(self, items):
960960
961961
(`?+:` requests both actions: filter out field if it is empty for all
962962
items, if not expand to maximum width)
963+
964+
Args:
965+
items (iterable): list of items to consider for output
966+
no_header (boolean): do not use header in calculations (default: False)
963967
"""
964968

965969
# Build a list of all format strings that have one of the width
@@ -1000,12 +1004,18 @@ def sentinel_keys():
10001004

10011005
# Save the modified format, index, type, maximum width,
10021006
# observed width, and broken-down spec in lst:
1007+
initialmaxwidth = spec.width or 0
1008+
if sentinels[end] in ("maxwidth", "both"):
1009+
initialmaxwidth = max(
1010+
initialmaxwidth,
1011+
0 if no_header else len(self.headings.get(field, "")),
1012+
)
10031013
lst.append(
10041014
dict(
10051015
fmt=fmt,
10061016
index=index,
10071017
type=sentinels[end],
1008-
maxwidth=spec.width or 0,
1018+
maxwidth=initialmaxwidth,
10091019
width=0,
10101020
spec=spec,
10111021
)
@@ -1129,7 +1139,7 @@ def print_items(self, items, no_header=False, pre=None, post=None):
11291139
post (callable): Function to call after printing each item
11301140
"""
11311141
# Preprocess original format by processing with filter():
1132-
newfmt = self.filter(items)
1142+
newfmt = self.filter(items, no_header=no_header)
11331143
# Get the current class for creating a new formatter instance:
11341144
cls = self.__class__
11351145
# Create new instance of the current class from filtered format:

src/cmd/flux-jobs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ def main():
545545
formatter.set_sort_keys(args.sort)
546546

547547
(jobs, truncated) = fetch_jobs(args, formatter.fields)
548-
sformatter = JobInfoFormat(formatter.filter(jobs))
548+
sformatter = JobInfoFormat(formatter.filter(jobs, no_header=args.no_header))
549549

550550
if not args.no_header:
551551
print(sformatter.header())

src/cmd/flux-pgrep.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def main():
274274
if PROGRAM == "flux-pkill":
275275
pkill(fh, args, jobs)
276276

277-
sformatter = JobInfoFormat(formatter.filter(jobs))
277+
sformatter = JobInfoFormat(formatter.filter(jobs, no_header=args.no_header))
278278

279279
# "default" can be overridden by environment variable, so check if
280280
# it's different than the builtin default

t/python/t0024-util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,15 @@ def test_filter(self):
212212
).filter(items)
213213
self.assertEqual(fmt, "{i:>8} {f:8.2}")
214214

215+
# N.B. float has a width of 5 due to "FLOAT" header
215216
fmt = OutputFormat(
216217
"?+:{i:>7} ?:{s:>6} ?+:{f:.2f}", headings=self.headings
217218
).filter(items)
219+
self.assertEqual(fmt, "{i:>8} {f:5.2f}")
220+
221+
fmt = OutputFormat(
222+
"?+:{i:>7} ?:{s:>6} ?+:{f:.2f}", headings=self.headings
223+
).filter(items, no_header=True)
218224
self.assertEqual(fmt, "{i:>8} {f:3.2f}")
219225

220226
def test_sort(self):
@@ -261,10 +267,17 @@ def test_issue6530(self):
261267
b = Item("abcdefghijklmnop", 2, 13)
262268
c = Item("c", 4, 5.0)
263269

270+
# N.B. iinteger has a width of 7 due to "INTEGER" header
271+
# N.B. float has a width of 5 due to "FLOAT" header
264272
items = [a, b, c]
265273
fmt = OutputFormat(
266274
"+:{s:5.5} +:{i:4d} +:{f:.2f}", headings=self.headings
267275
).filter(items)
276+
self.assertEqual(fmt, "{s:16.16} {i:7d} {f:5.2f}")
277+
278+
fmt = OutputFormat(
279+
"+:{s:5.5} +:{i:4d} +:{f:.2f}", headings=self.headings
280+
).filter(items, no_header=True)
268281
self.assertEqual(fmt, "{s:16.16} {i:4d} {f:3.2f}")
269282

270283
def test_copy(self):

t/t2800-jobs-cmd.t

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ test_expect_success 'flux-jobs: specified width overrides expandable field' '
209209
test_debug "cat expanded2.out" &&
210210
grep "^ nosuchcommand" expanded2.out
211211
'
212+
# N.B. if header was not accounted for, the job name "a" would not be
213+
# right aligned by three spaces (header is "NAME")
214+
test_expect_success 'flux-jobs: header accounted for in expandable field' '
215+
echo "{\"id\":195823665152,\"state\":8,\"name\":\"a\"}" \
216+
| flux jobs --from-stdin -o "+:{name:>1}" > expanded3.out &&
217+
grep "^ a" expanded3.out
218+
'
219+
test_expect_success 'flux-jobs: header not accounted for in expandable field if no header output' '
220+
echo "{\"id\":195823665152,\"state\":8,\"name\":\"a\"}" \
221+
| flux jobs --from-stdin -no "+:{name:>1}" > expanded3.out &&
222+
grep "^a" expanded3.out
223+
'
212224
test_expect_success 'flux-jobs: collapsible+expandable fields work' '
213225
flux jobs -ao "{id.f58:<12} ?+:{exception.type:>1}" >both.out &&
214226
flux jobs -f running,completed \

0 commit comments

Comments
 (0)