Skip to content

Commit 24c96f5

Browse files
committed
Always set timestamp on kill, provide spent time in str()
(cherry picked from commit 06f1650)
1 parent b57aabd commit 24c96f5

File tree

6 files changed

+48
-6
lines changed

6 files changed

+48
-6
lines changed

doc/source/ExecResult.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ API: ExecResult
4444
``typing.Optional(datetime.datetime)``
4545
Timestamp
4646

47+
.. py:method:: set_timestamp()
48+
49+
Set timestamp if empty.
50+
51+
This will block future object changes.
52+
53+
.. versionadded:: 4.0.0
54+
4755
.. py:attribute:: cmd
4856
4957
``str``

exec_helpers/_ssh_client_base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,9 @@ def poll_pipes() -> None:
718718
async_result.interface.status_event.set()
719719
future.cancel()
720720

721+
concurrent.futures.wait([future], 0.001)
722+
result.set_timestamp()
723+
721724
wait_err_msg = _log_templates.CMD_WAIT_ERROR.format(result=result, timeout=timeout)
722725
self.logger.debug(wait_err_msg)
723726
raise exceptions.ExecHelperTimeoutError(result=result, timeout=timeout) # type: ignore

exec_helpers/async_api/subprocess_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ async def poll_stderr() -> None:
147147
finally:
148148
stdout_task.cancel()
149149
stderr_task.cancel()
150+
result.set_timestamp()
150151

151152
wait_err_msg = _log_templates.CMD_WAIT_ERROR.format(result=result, timeout=timeout)
152153
self.logger.debug(wait_err_msg)

exec_helpers/exec_result.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def __init__(
9797
self._stderr = ()
9898

9999
self.__exit_code = proc_enums.INVALID # type: typing.Union[int, proc_enums.ExitCodes]
100-
self.__timestamp = None
100+
self.__timestamp = None # type: typing.Optional[datetime.datetime]
101101
self.exit_code = exit_code
102102

103103
self.__started = started # type: typing.Optional[datetime.datetime]
@@ -139,6 +139,16 @@ def timestamp(self) -> typing.Optional[datetime.datetime]:
139139
"""
140140
return self.__timestamp
141141

142+
def set_timestamp(self) -> None:
143+
"""Set timestamp if empty.
144+
145+
This will block future object changes.
146+
147+
.. versionadded:: 4.0.0
148+
"""
149+
if self.timestamp is None:
150+
self.__timestamp = datetime.datetime.utcnow()
151+
142152
@staticmethod
143153
def _get_bytearray_from_array(src: typing.Iterable[bytes]) -> bytearray:
144154
"""Get bytearray from array of bytes blocks.
@@ -370,7 +380,7 @@ def exit_code(self, new_val: typing.Union[int, proc_enums.ExitCodes]) -> None:
370380
with self.stdout_lock, self.stderr_lock:
371381
self.__exit_code = proc_enums.exit_code_to_enum(new_val)
372382
if self.__exit_code != proc_enums.INVALID:
373-
self.__timestamp = datetime.datetime.utcnow() # type: ignore
383+
self.__timestamp = datetime.datetime.utcnow()
374384

375385
@property
376386
def started(self) -> typing.Optional[datetime.datetime]:
@@ -471,20 +481,31 @@ def __str__(self) -> str:
471481
"""Representation for logging."""
472482
if self.started:
473483
started = "\tstarted={started},\n".format(started=self.started.strftime('%Y-%m-%d %H:%M:%S'))
484+
if self.timestamp:
485+
_spent = (self.timestamp - self.started).seconds
486+
spent = "\tspent={hours:02d}:{minutes:02d}:{seconds:02d},\n".format(
487+
hours=_spent // (60 * 60),
488+
minutes=_spent // 60,
489+
seconds=_spent % 60
490+
)
491+
else:
492+
spent = ""
474493
else:
475494
started = ""
495+
spent = ""
476496
return (
477497
"{cls}(\n\tcmd={cmd!r},"
478498
"\n\t stdout=\n'{stdout_brief}',"
479499
"\n\tstderr=\n'{stderr_brief}', "
480500
"\n\texit_code={exit_code!s},"
481-
"\n{started})".format(
501+
"\n{started}{spent})".format(
482502
cls=self.__class__.__name__,
483503
cmd=self.cmd,
484504
stdout_brief=self.stdout_brief,
485505
stderr_brief=self.stderr_brief,
486506
exit_code=self.exit_code,
487-
started=started
507+
started=started,
508+
spent=spent
488509
)
489510
)
490511

exec_helpers/subprocess_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def close_streams() -> None:
171171
command, async_result.interface.returncode
172172
)
173173
)
174+
result.set_timestamp()
174175
close_streams()
175176

176177
wait_err_msg = _log_templates.CMD_WAIT_ERROR.format(result=result, timeout=timeout)

test/test_exec_result.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,19 +226,27 @@ def test_stdin_bytearray(self):
226226
def test_started(self):
227227
started = datetime.datetime.utcnow()
228228
result = exec_helpers.ExecResult(cmd, exit_code=0, started=started)
229+
spent = (result.timestamp - started).seconds
229230
self.assertIs(result.started, started)
230231
self.assertEqual(
231232
str(result),
232233
"{cls}(\n\tcmd={cmd!r},"
233234
"\n\t stdout=\n'{stdout_brief}',"
234235
"\n\tstderr=\n'{stderr_brief}', "
235236
"\n\texit_code={exit_code!s},"
236-
"\n\tstarted={started},\n)".format(
237+
"\n\tstarted={started},"
238+
"\n\tspent={spent},"
239+
"\n)".format(
237240
cls=exec_helpers.ExecResult.__name__,
238241
cmd=cmd,
239242
stdout_brief="",
240243
stderr_brief="",
241244
exit_code=proc_enums.EXPECTED,
242-
started=started.strftime('%Y-%m-%d %H:%M:%S')
245+
started=started.strftime("%Y-%m-%d %H:%M:%S"),
246+
spent="{hours:02d}:{minutes:02d}:{seconds:02d}".format(
247+
hours=spent // (60 * 60),
248+
minutes=spent // 60,
249+
seconds=spent % 60
250+
),
243251
),
244252
)

0 commit comments

Comments
 (0)