diff --git a/doc/source/exceptions.rst b/doc/source/exceptions.rst index d558cae..5d50d24 100644 --- a/doc/source/exceptions.rst +++ b/doc/source/exceptions.rst @@ -14,13 +14,45 @@ API: exceptions Deserialize impossible. -.. py:exception:: ExecHelperTimeoutError(ExecHelperError) +.. py:exception:: ExecCalledProcessError(ExecHelperError) + + Base class for process call errors. + +.. py:exception:: ExecHelperTimeoutError(ExecCalledProcessError) Execution timeout. -.. py:exception:: ExecCalledProcessError(ExecHelperError) + .. versionchanged:: 1.3.0 provide full result and timeout inside. + .. versionchanged:: 1.3.0 subclass ExecCalledProcessError - Base class for process call errors. + .. py:method:: __init__(self, result, timeout) + + Exception for error on process calls. + + :param result: execution result + :type result: exec_result.ExecResult + :param timeout: timeout for command + :type timeout: typing.Union[int, float] + + .. py:attribute:: timeout + + ``int`` + + .. py:attribute:: result + + Execution result + + :rtype: ExecResult + + .. py:attribute:: stdout + + ``typing.Text`` + stdout string or brief string + + .. py:attribute:: stderr + + ``typing.Text`` + stdout string or brief string .. py:exception:: CalledProcessError(ExecCalledProcessError) @@ -60,12 +92,12 @@ API: exceptions .. py:attribute:: stdout - ``typing.Any`` + ``typing.Text`` stdout string or brief string .. py:attribute:: stderr - ``typing.Any`` + ``typing.Text`` stdout string or brief string .. py:exception:: ParallelCallExceptions(ExecCalledProcessError) diff --git a/exec_helpers/_ssh_client_base.py b/exec_helpers/_ssh_client_base.py index 65395bc..f0ce5b5 100644 --- a/exec_helpers/_ssh_client_base.py +++ b/exec_helpers/_ssh_client_base.py @@ -735,7 +735,7 @@ def poll_pipes(stop, ): # type: (threading.Event) -> None timeout=timeout ) self.logger.debug(wait_err_msg) - raise exceptions.ExecHelperTimeoutError(wait_err_msg) + raise exceptions.ExecHelperTimeoutError(result=result, timeout=timeout) def execute_through_host( self, diff --git a/exec_helpers/exceptions.py b/exec_helpers/exceptions.py index 07d2cb4..fc01aa7 100644 --- a/exec_helpers/exceptions.py +++ b/exec_helpers/exceptions.py @@ -21,6 +21,7 @@ import typing # noqa # pylint: disable=unused-import from exec_helpers import proc_enums +from exec_helpers import _log_templates __all__ = ( 'ExecHelperError', @@ -44,16 +45,58 @@ class DeserializeValueError(ExecHelperError, ValueError): __slots__ = () -class ExecHelperTimeoutError(ExecHelperError): - """Execution timeout.""" +class ExecCalledProcessError(ExecHelperError): + """Base class for process call errors.""" __slots__ = () -class ExecCalledProcessError(ExecHelperError): - """Base class for process call errors.""" +class ExecHelperTimeoutError(ExecCalledProcessError): + """Execution timeout. - __slots__ = () + .. versionchanged:: 1.3.0 provide full result and timeout inside. + .. versionchanged:: 1.3.0 subclass ExecCalledProcessError + """ + + __slots__ = ( + 'result', + 'timeout', + ) + + def __init__( + self, + result, # type: exec_result.ExecResult + timeout, # type: typing.Union[int, float] + ): # type: (...) -> None + """Exception for error on process calls. + + :param result: execution result + :type result: exec_result.ExecResult + :param timeout: timeout for command + :type timeout: typing.Union[int, float] + """ + self.result = result + self.timeout = timeout + message = _log_templates.CMD_WAIT_ERROR.format( + result=result, + timeout=timeout + ) + super(ExecHelperTimeoutError, self).__init__(message) + + @property + def cmd(self): # type: () -> str + """Failed command.""" + return self.result.cmd + + @property + def stdout(self): # type: () -> typing.Text + """Command stdout.""" + return self.result.stdout_str + + @property + def stderr(self): # type: () -> typing.Text + """Command stderr.""" + return self.result.stderr_str class CalledProcessError(ExecCalledProcessError): @@ -74,9 +117,7 @@ def __init__( :param result: execution result :type result: exec_result.ExecResult :param expected: expected return codes - :type expected: typing.Optional[ - typing.List[typing.Union[int, proc_enums.ExitCodes]] - ] + :type expected: typing.Optional[typing.List[typing.Union[int, proc_enums.ExitCodes]]] .. versionchanged:: 1.1.1 - provide full result """ @@ -89,7 +130,7 @@ def __init__( "\tSTDOUT:\n" "{result.stdout_brief}\n" "\tSTDERR:\n{result.stderr_brief}".format( - result=result, + result=self.result, expected=self.expected ) ) diff --git a/exec_helpers/exceptions.pyi b/exec_helpers/exceptions.pyi index 4a22763..1f4558b 100644 --- a/exec_helpers/exceptions.pyi +++ b/exec_helpers/exceptions.pyi @@ -7,13 +7,32 @@ class ExecHelperError(Exception): class DeserializeValueError(ExecHelperError, ValueError): ... -class ExecHelperTimeoutError(ExecHelperError): - ... class ExecCalledProcessError(ExecHelperError): ... +class ExecHelperTimeoutError(ExecCalledProcessError): + + result: exec_result.ExecResult = ... + timeout: int = ... + + def __init__( + self, + result: exec_result.ExecResult, + timeout: typing.Union[int, float], + ) -> None: ... + + @property + def cmd(self) -> str: ... + + @property + def stdout(self) -> typing.Text: ... + + @property + def stderr(self) -> typing.Text: ... + + class CalledProcessError(ExecCalledProcessError): result: exec_result.ExecResult = ... diff --git a/exec_helpers/subprocess_runner.py b/exec_helpers/subprocess_runner.py index 8b79cc2..b36a05c 100644 --- a/exec_helpers/subprocess_runner.py +++ b/exec_helpers/subprocess_runner.py @@ -269,7 +269,7 @@ def poll_pipes(stop, ): # type: (threading.Event) -> None timeout=timeout ) logger.debug(wait_err_msg) - raise exceptions.ExecHelperTimeoutError(wait_err_msg) + raise exceptions.ExecHelperTimeoutError(result=result, timeout=timeout) def execute_async( self, diff --git a/test/test_ssh_client.py b/test/test_ssh_client.py index 9119773..f02a68f 100644 --- a/test/test_ssh_client.py +++ b/test/test_ssh_client.py @@ -1028,10 +1028,15 @@ def test_025_execute_timeout_fail( logger.reset_mock() - with self.assertRaises(exec_helpers.ExecHelperTimeoutError): + with self.assertRaises(exec_helpers.ExecHelperTimeoutError) as cm: # noinspection PyTypeChecker ssh.execute(command=command, verbose=False, timeout=0.2) + self.assertEqual(cm.exception.timeout, 0.2) + self.assertEqual(cm.exception.cmd, command) + self.assertEqual(cm.exception.stdout, stdout_str) + self.assertEqual(cm.exception.stderr, stderr_str) + execute_async.assert_called_once_with(command, verbose=False) chan.assert_has_calls((mock.call.status_event.is_set(), )) diff --git a/test/test_subprocess_runner.py b/test/test_subprocess_runner.py index 04555c7..18d508d 100644 --- a/test/test_subprocess_runner.py +++ b/test/test_subprocess_runner.py @@ -233,10 +233,15 @@ def test_004_execute_timeout_fail( # noinspection PyTypeChecker - with self.assertRaises(exec_helpers.ExecHelperTimeoutError): + with self.assertRaises(exec_helpers.ExecHelperTimeoutError) as cm: # noinspection PyTypeChecker runner.execute(command, timeout=0.2) + self.assertEqual(cm.exception.timeout, 0.2) + self.assertEqual(cm.exception.cmd, command) + self.assertEqual(cm.exception.stdout, exp_result.stdout_str) + self.assertEqual(cm.exception.stderr, exp_result.stderr_str) + popen.assert_has_calls(( mock.call( args=[command],