diff options
author | John Snow | 2022-03-21 21:16:02 +0100 |
---|---|---|
committer | Hanna Reitz | 2022-03-22 10:14:23 +0100 |
commit | 062fd1dad2640d1c2522b71ddde4ba0bbdc8c6d9 (patch) | |
tree | 880e9ff717df3bbcb1eea604b39334d945ad111d /python | |
parent | python/utils: add add_visual_margin() text decoration utility (diff) | |
download | qemu-062fd1dad2640d1c2522b71ddde4ba0bbdc8c6d9.tar.gz qemu-062fd1dad2640d1c2522b71ddde4ba0bbdc8c6d9.tar.xz qemu-062fd1dad2640d1c2522b71ddde4ba0bbdc8c6d9.zip |
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>
Diffstat (limited to 'python')
-rw-r--r-- | python/qemu/utils/__init__.py | 39 |
1 files changed, 39 insertions, 0 deletions
diff --git a/python/qemu/utils/__init__.py b/python/qemu/utils/__init__.py index b84c86d004..9fb273b13d 100644 --- a/python/qemu/utils/__init__.py +++ b/python/qemu/utils/__init__.py @@ -18,6 +18,7 @@ various tasks not directly related to the launching of a VM. import os import re import shutil +from subprocess import CalledProcessError import textwrap from typing import Optional @@ -26,6 +27,7 @@ from .accel import kvm_available, list_accel, tcg_available __all__ = ( + 'VerboseProcessError', 'add_visual_margin', 'get_info_usernet_hostfwd_port', 'kvm_available', @@ -121,3 +123,40 @@ def add_visual_margin( os.linesep.join(_wrap(line) for line in content.splitlines()), _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), + )) |