python/utils: add VerboseProcessError
This adds an Exception that extends the Python stdlib subprocess.CalledProcessError. The difference is that the str() method of this exception also adds the stdout/stderr logs. In effect, if this exception goes unhandled, Python will print the output in a visually distinct wrapper to the terminal so that it's easy to spot in a sea of traceback information. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Hanna Reitz <hreitz@redhat.com> Message-Id: <20220321201618.903471-3-jsnow@redhat.com> Signed-off-by: Hanna Reitz <hreitz@redhat.com>
This commit is contained in:
parent
be73231ba8
commit
062fd1dad2
@ -18,6 +18,7 @@ various tasks not directly related to the launching of a VM.
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
from subprocess import CalledProcessError
|
||||||
import textwrap
|
import textwrap
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ from .accel import kvm_available, list_accel, tcg_available
|
|||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'VerboseProcessError',
|
||||||
'add_visual_margin',
|
'add_visual_margin',
|
||||||
'get_info_usernet_hostfwd_port',
|
'get_info_usernet_hostfwd_port',
|
||||||
'kvm_available',
|
'kvm_available',
|
||||||
@ -121,3 +123,40 @@ def add_visual_margin(
|
|||||||
os.linesep.join(_wrap(line) for line in content.splitlines()),
|
os.linesep.join(_wrap(line) for line in content.splitlines()),
|
||||||
_bar(None, top=False),
|
_bar(None, top=False),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class VerboseProcessError(CalledProcessError):
|
||||||
|
"""
|
||||||
|
The same as CalledProcessError, but more verbose.
|
||||||
|
|
||||||
|
This is useful for debugging failed calls during test executions.
|
||||||
|
The return code, signal (if any), and terminal output will be displayed
|
||||||
|
on unhandled exceptions.
|
||||||
|
"""
|
||||||
|
def summary(self) -> str:
|
||||||
|
"""Return the normal CalledProcessError str() output."""
|
||||||
|
return super().__str__()
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
lmargin = ' '
|
||||||
|
width = -len(lmargin)
|
||||||
|
sections = []
|
||||||
|
|
||||||
|
# Does self.stdout contain both stdout and stderr?
|
||||||
|
has_combined_output = self.stderr is None
|
||||||
|
|
||||||
|
name = 'output' if has_combined_output else 'stdout'
|
||||||
|
if self.stdout:
|
||||||
|
sections.append(add_visual_margin(self.stdout, width, name))
|
||||||
|
else:
|
||||||
|
sections.append(f"{name}: N/A")
|
||||||
|
|
||||||
|
if self.stderr:
|
||||||
|
sections.append(add_visual_margin(self.stderr, width, 'stderr'))
|
||||||
|
elif not has_combined_output:
|
||||||
|
sections.append("stderr: N/A")
|
||||||
|
|
||||||
|
return os.linesep.join((
|
||||||
|
self.summary(),
|
||||||
|
textwrap.indent(os.linesep.join(sections), prefix=lmargin),
|
||||||
|
))
|
||||||
|
Loading…
Reference in New Issue
Block a user