Skip to content

Commit

Permalink
Merge pull request #3183 from chu11/issue3161_annotations_types
Browse files Browse the repository at this point in the history
flux-jobs / python bindings: handle empty string conversions
  • Loading branch information
mergify[bot] committed Sep 2, 2020
2 parents 1e686c0 + 651f57a commit d45cad9
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 26 deletions.
12 changes: 8 additions & 4 deletions doc/man1/flux-jobs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,23 @@ used to transform the value before formatting takes place. Currently,
the following conversion flags are supported by *flux-jobs*:

**!D**
convert a timestamp field to ISO8601 date and time (e.g. 2020-01-07T13:31:00)
convert a timestamp field to ISO8601 date and time (e.g. 2020-01-07T13:31:00).
Defaults to empty string if timestamp field does not exist.

**!d**
convert a timestamp to a Python datetime object. This allows datetime specific
format to be used, e.g. *{t_inactive!d:%H:%M:%S}*. However, note that width
and alignment specifiers are not supported for datetime formatting.
Defaults to datetime of epoch if timestamp field does not exist.

**!F**
convert a duration in floating point seconds to Flux Standard Duration (FSD)
string.
convert a duration in floating point seconds to Flux Standard Duration (FSD).
string. Defaults to empty string if duration field does not exist.


**!H**
convert a duration to hours:minutes:seconds form (e.g. *{runtime!H}*)
convert a duration to hours:minutes:seconds form (e.g. *{runtime!H}*).
Defaults to empty string if duration field does not exist.

Annotations can be retrieved via the *annotations* field name.
Specific keys and sub-object keys can be retrieved separated by a
Expand Down
41 changes: 33 additions & 8 deletions src/bindings/python/flux/job/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,23 +243,48 @@ def convert_field(self, value, conv):
different conversion types. (mainly used for time-specific
fields for now).
"""
orig_value = str(value)
if conv == "d":
# convert from float seconds sinc epoch to a datetime.
# convert from float seconds since epoch to a datetime.
# User can than use datetime specific format fields, e.g.
# {t_inactive!D:%H:%M:S}.
value = datetime.fromtimestamp(value)
# {t_inactive!d:%H:%M:%S}.
try:
value = datetime.fromtimestamp(value)
except TypeError:
if orig_value is "":
value = datetime.fromtimestamp(0.0)
else:
raise
elif conv == "D":
# As above, but convert to ISO 8601 date time string.
value = datetime.fromtimestamp(value).strftime("%FT%T")
try:
value = datetime.fromtimestamp(value).strftime("%FT%T")
except TypeError:
if orig_value is "":
value = ""
else:
raise
elif conv == "F":
# convert to Flux Standard Duration (fsd) string.
value = fsd(value)
try:
value = fsd(value)
except TypeError:
if orig_value is "":
value = ""
else:
raise
elif conv == "H":
# if > 0, always round up to at least one second to
# avoid presenting a nonzero timedelta as zero
if 0 < value < 1:
value = 1
value = str(timedelta(seconds=round(value)))
try:
if 0 < value < 1:
value = 1
value = str(timedelta(seconds=round(value)))
except TypeError:
if orig_value is "":
value = ""
else:
raise
else:
value = super().convert_field(value, conv)
return value
Expand Down
2 changes: 1 addition & 1 deletion src/bindings/python/flux/job/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def __init__(self):
def get_jobs(self):
"""get all successful results, appending errors into self.errors"""
jobs = []
# Wait for all obid RPCs to complete
# Wait for all jobid RPCs to complete
self.wait_for()

# Get all successful jobs, accumulate errors in self.errors
Expand Down
12 changes: 0 additions & 12 deletions src/cmd/flux-jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,6 @@ def fetch_jobs_stdin():
return jobs


def fetch_jobs_all(flux_handle, args, attrs, userid, states, results):
rpc_handle = flux.job.job_list(
flux_handle, args.count, list(attrs), userid, states, results
)
try:
jobs = rpc_handle.get_jobs()
except EnvironmentError as err:
print("{}: {}".format("rpc", err.strerror), file=sys.stderr)
sys.exit(1)
return jobs


def fetch_jobs_flux(args, fields):
flux_handle = flux.Flux()

Expand Down
20 changes: 19 additions & 1 deletion t/t2800-jobs-cmd.t
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,24 @@ test_expect_success 'flux-jobs emits empty string on invalid annotations fields'
done &&
test_cmp invalid-annotations.out invalid-annotations.exp
'

test_expect_success 'flux-jobs emits empty string for special case t_estimate' '
fmt="{annotations.sched.t_estimate}" &&
fmt="${fmt},{annotations.sched.t_estimate!d:%H:%M}" &&
fmt="${fmt},{annotations.sched.t_estimate!D}" &&
fmt="${fmt},{annotations.sched.t_estimate!F}" &&
fmt="${fmt},{annotations.sched.t_estimate!H}" &&
fmt="${fmt},{annotations.sched.t_estimate!D:h}" &&
fmt="${fmt},{annotations.sched.t_estimate!F:h}" &&
fmt="${fmt},{annotations.sched.t_estimate!H:h}" &&
flux jobs -no "${fmt}" >t_estimate_annotations.out 2>&1 &&
test_debug "cat t_estimate_annotations.out" &&
for i in `seq 1 $(state_count active)`; do
echo ",00:00,,,,-,-,-" >> t_estimate_annotations.exp
done &&
test_cmp t_estimate_annotations.out t_estimate_annotations.exp
'

#
# format header tests.
#
Expand Down Expand Up @@ -958,7 +976,7 @@ test_expect_success HAVE_JQ 'flux jobs works on job with illegal R' '

# we make the eventlog invalid by overwriting it in the KVS before job-info will
# look it up. Note that b/c the eventlog is corrupted, the userid for the job
# is never established. So we have to do "--all" and grep for the jobid.
# is never established. So we have to do "--user=all" and grep for the jobid.
test_expect_success HAVE_JQ 'flux jobs works on job with illegal eventlog' '
${RPC} job-info.job-state-pause 0 </dev/null &&
jobid=`flux job submit hostname.json` &&
Expand Down

0 comments on commit d45cad9

Please sign in to comment.