Skip to content

Commit 5456216

Browse files
authored
CalledProcessError should store execution result
* Do not count coverage on compatibility helpers
1 parent 5a69693 commit 5456216

File tree

7 files changed

+106
-102
lines changed

7 files changed

+106
-102
lines changed

doc/source/SSHClient.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ API: SSHClient and SSHAuth.
182182
Execute command on multiple remotes in async mode.
183183

184184
:param remotes: Connections to execute on
185-
:type remotes: ``typing.Iterable[SSHClientBase]``
185+
:type remotes: typing.Iterable[SSHClient]
186186
:param command: Command for execution
187187
:type command: ``str``
188188
:param timeout: Timeout for command execution.

doc/source/exceptions.rst

+34-24
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,20 @@ API: exceptions
2222
2323
Exception for error on process calls.
2424

25-
.. py:method:: __init__(command, returncode, expected=None, stdout=None, stderr=None)
25+
.. versionchanged:: 1.1.1 - provide full result
2626

27-
:param command: command
28-
:type command: str
27+
.. py:method:: __init__(result, expected=None)
28+
29+
:param result: execution result
30+
:type result: ExecResult
2931
:param returncode: return code
30-
:type returncode: typing.Union[int, proc_enums.ExitCodes]
31-
:param expected: expected return codes
32-
:type expected: typing.Optional[typing.List[typing.Union[int, proc_enums.ExitCodes]]]
33-
:param stdout: stdout string or brief string
34-
:type stdout: typing.Any
35-
:param stderr: stderr string or brief string
36-
:type stderr: typing.Any
32+
:type returncode: typing.Union[int, ExitCodes]
33+
34+
.. py:attribute:: result
35+
36+
Execution result
37+
38+
:rtype: ExecResult
3739

3840
.. py:attribute:: cmd
3941
@@ -42,14 +44,16 @@ API: exceptions
4244

4345
.. py:attribute:: returncode
4446
45-
``typing.Union[int, proc_enums.ExitCodes]``
4647
return code
4748

49+
:rtype: typing.Union[int, ExitCodes]
50+
4851
.. py:attribute:: expected
4952
50-
``typing.List[typing.Union[int, proc_enums.ExitCodes]]``
5153
expected return codes
5254

55+
:rtype: typing.List[typing.Union[int, ExitCodes]]
56+
5357
.. py:attribute:: stdout
5458
5559
``typing.Any``
@@ -71,11 +75,11 @@ API: exceptions
7175
:param exceptions: Exception on connections
7276
:type exceptions: ``typing.Dict[typing.Tuple[str, int], Exception]``
7377
:param errors: results with errors
74-
:type errors: ``typing.Dict[typing.Tuple[str, int], ExecResult]``
78+
:type errors: typing.Dict[typing.Tuple[str, int], ExecResult]
7579
:param results: all results
76-
:type results: ``typing.Dict[typing.Tuple[str, int], ExecResult]``
80+
:type results: typing.Dict[typing.Tuple[str, int], ExecResult]
7781
:param expected: expected return codes
78-
:type expected: ``typing.Optional[typing.List[typing.List[typing.Union[int, proc_enums.ExitCodes]]]``
82+
:type expected: typing.Optional[typing.List[typing.List[typing.Union[int, ExitCodes]]]
7983

8084
.. versionchanged:: 1.0 - fixed inheritance
8185

@@ -91,19 +95,22 @@ API: exceptions
9195

9296
.. py:attribute:: errors
9397
94-
``typing.Dict[typing.Tuple[str, int], ExecResult]``
9598
results with errors
9699

100+
:rtype: typing.Dict[typing.Tuple[str, int], ExecResult]
101+
97102
.. py:attribute:: results
98103
99-
``typing.Dict[typing.Tuple[str, int], ExecResult]``
100104
all results
101105

106+
:rtype: typing.Dict[typing.Tuple[str, int], ExecResult]
107+
102108
.. py:attribute:: expected
103109
104-
``typing.List[typing.Union[int, proc_enums.ExitCodes]]``
105110
expected return codes
106111

112+
:rtype: typing.List[typing.Union[int, ExitCodes]]
113+
107114
.. py:exception:: ParallelCallProcessError(ExecCalledProcessError)
108115
109116
Exception during parallel execution.
@@ -113,11 +120,11 @@ API: exceptions
113120
:param command: command
114121
:type command: ``str``
115122
:param errors: results with errors
116-
:type errors: ``typing.Dict[typing.Tuple[str, int], ExecResult]``
123+
:type errors: typing.Dict[typing.Tuple[str, int], ExecResult]
117124
:param results: all results
118-
:type results: ``typing.Dict[typing.Tuple[str, int], ExecResult]``
125+
:type results: typing.Dict[typing.Tuple[str, int], ExecResult]
119126
:param expected: expected return codes
120-
:type expected: ``typing.Optional[typing.List[typing.List[typing.Union[int, proc_enums.ExitCodes]]]``
127+
:type expected: typing.Optional[typing.List[typing.List[typing.Union[int, ExitCodes]]]
121128

122129
.. versionchanged:: 1.0 - fixed inheritance
123130

@@ -128,15 +135,18 @@ API: exceptions
128135

129136
.. py:attribute:: errors
130137
131-
``typing.Dict[typing.Tuple[str, int], ExecResult]``
132138
results with errors
133139

140+
:rtype: typing.Dict[typing.Tuple[str, int], ExecResult]
141+
134142
.. py:attribute:: results
135143
136-
``typing.Dict[typing.Tuple[str, int], ExecResult]``
137144
all results
138145

146+
:rtype: typing.Dict[typing.Tuple[str, int], ExecResult]
147+
139148
.. py:attribute:: expected
140149
141-
``typing.List[typing.Union[int, proc_enums.ExitCodes]]``
142150
expected return codes
151+
152+
:rtype: typing.List[typing.Union[int, ExitCodes]]

exec_helpers/_ssh_client_base.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ def close_connections(
430430
mcs.__cache[key].close()
431431

432432

433-
def _py2_str(src):
433+
def _py2_str(src): # pragma: no cover
434434
"""Convert text to correct python type."""
435435
if not six.PY3 and isinstance(src, six.text_type):
436436
return src.encode(
@@ -699,7 +699,7 @@ def __del__(self):
699699
"""
700700
try:
701701
self.__ssh.close()
702-
except BaseException as e:
702+
except BaseException as e: # pragma: no cover
703703
self.logger.debug(
704704
'Exception in {self!s} destructor call: {exc}'.format(
705705
self=self,
@@ -989,10 +989,8 @@ def check_call(
989989
self.logger.error(message)
990990
if raise_on_err:
991991
raise exceptions.CalledProcessError(
992-
command, ret.exit_code,
993-
expected=expected,
994-
stdout=ret.stdout_brief,
995-
stderr=ret.stderr_brief
992+
result=ret,
993+
expected=expected
996994
)
997995
return ret
998996

@@ -1034,11 +1032,8 @@ def check_stderr(
10341032
self.logger.error(message)
10351033
if raise_on_err:
10361034
raise exceptions.CalledProcessError(
1037-
command,
1038-
ret.exit_code,
1035+
result=ret,
10391036
expected=kwargs.get('expected'),
1040-
stdout=ret.stdout_brief,
1041-
stderr=ret.stderr_brief
10421037
)
10431038
return ret
10441039

exec_helpers/exceptions.py

+37-40
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import typing
2020

21-
import six
22-
2321
from exec_helpers import proc_enums
2422

2523
__all__ = (
@@ -52,69 +50,68 @@ class ExecCalledProcessError(ExecHelperError):
5250

5351
__slots__ = ()
5452

55-
@staticmethod
56-
def _makestr(data): # type: (typing.Any) -> six.text_type
57-
"""Make string from object."""
58-
if isinstance(data, six.binary_type):
59-
return data.decode('utf-8', errors='backslashreplace')
60-
elif isinstance(data, six.text_type):
61-
return data
62-
return repr(data)
63-
6453

6554
class CalledProcessError(ExecCalledProcessError):
6655
"""Exception for error on process calls."""
6756

6857
__slots__ = (
69-
'cmd',
70-
'returncode',
58+
'result',
7159
'expected',
72-
'stdout',
73-
'stderr'
7460
)
7561

7662
def __init__(
7763
self,
78-
command, # type: str
79-
returncode, # type: typing.Union[int, proc_enums.ExitCodes]
64+
result=None, # type: exec_result.ExecResult
8065
expected=None, # type: typing.Optional[typing.List[_type_exit_codes]]
81-
stdout=None, # type: typing.Any
82-
stderr=None # type: typing.Any
8366
):
8467
"""Exception for error on process calls.
8568
86-
:param command: command
87-
:type command: str
88-
:param returncode: return code
89-
:type returncode: typing.Union[int, proc_enums.ExitCodes]
69+
:param result: execution result
70+
:type result: exec_result.ExecResult
9071
:param expected: expected return codes
9172
:type expected: typing.Optional[
9273
typing.List[typing.Union[int, proc_enums.ExitCodes]]
9374
]
94-
:param stdout: stdout string or brief string
95-
:type stdout: typing.Any
96-
:param stderr: stderr string or brief string
97-
:type stderr: typing.Any
75+
76+
.. versionchanged:: 1.1.1 - provide full result
9877
"""
99-
self.returncode = returncode
78+
self.result = result
10079
expected = expected or [proc_enums.ExitCodes.EX_OK]
10180
self.expected = proc_enums.exit_codes_to_enums(expected)
102-
self.cmd = command
103-
self.stdout = stdout
104-
self.stderr = stderr
10581
message = (
106-
"Command {cmd!r} returned exit code {code} while "
107-
"expected {expected}".format(
108-
cmd=self._makestr(self.cmd),
109-
code=self.returncode,
82+
"Command {result.cmd!r} returned exit code {result.exit_code} "
83+
"while expected {expected}\n"
84+
"\tSTDOUT:\n"
85+
"{result.stdout_brief}\n"
86+
"\tSTDERR:\n{result.stderr_brief}".format(
87+
result=result,
11088
expected=self.expected
111-
))
112-
if self.stdout:
113-
message += "\n\tSTDOUT:\n{}".format(self._makestr(self.stdout))
114-
if self.stderr:
115-
message += "\n\tSTDERR:\n{}".format(self._makestr(self.stderr))
89+
)
90+
)
11691
super(CalledProcessError, self).__init__(message)
11792

93+
@property
94+
def returncode(
95+
self
96+
): # type: () -> typing.Union[int, proc_enums.ExitCodes]
97+
"""Command return code."""
98+
return self.result.exit_code
99+
100+
@property
101+
def cmd(self): # type: () -> str
102+
"""Failed command."""
103+
return self.result.cmd
104+
105+
@property
106+
def stdout(self): # type: () -> str
107+
"""Command stdout."""
108+
return self.result.stdout_str
109+
110+
@property
111+
def stderr(self): # type: () -> str
112+
"""Command stderr."""
113+
return self.result.stderr_str
114+
118115

119116
class ParallelCallExceptions(ExecCalledProcessError):
120117
"""Exception raised during parallel call as result of exceptions."""

exec_helpers/proc_enums.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ def __str__(self):
7979
)
8080

8181

82-
if six.PY3:
82+
if six.PY3: # pragma: no cover
8383
digit_type = int
84-
else:
84+
else: # pragma: no cover
8585
# noinspection PyUnresolvedReferences
8686
digit_type = long # noqa # pylint: disable=undefined-variable
8787

exec_helpers/subprocess_runner.py

+4-9
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def __call__(cls, *args, **kwargs):
5858
return cls._instances[cls]
5959

6060

61-
def _py2_str(src):
61+
def _py2_str(src): # pragma: no cover
6262
"""Convert text to correct python type."""
6363
if not six.PY3 and isinstance(src, six.text_type):
6464
return src.encode(
@@ -311,11 +311,9 @@ def check_call(
311311
logger.error(message)
312312
if raise_on_err:
313313
raise exceptions.CalledProcessError(
314-
command,
315-
ret.exit_code,
314+
result=ret,
316315
expected=expected,
317-
stdout=ret.stdout_brief,
318-
stderr=ret.stderr_brief)
316+
)
319317
return ret
320318

321319
def check_stderr(
@@ -351,10 +349,7 @@ def check_stderr(
351349
logger.error(message)
352350
if raise_on_err:
353351
raise exceptions.CalledProcessError(
354-
command,
355-
ret['exit_code'],
352+
result=ret,
356353
expected=kwargs.get('expected'),
357-
stdout=ret['stdout_brief'],
358-
stderr=ret['stderr_brief'],
359354
)
360355
return ret

0 commit comments

Comments
 (0)