]> Git Repo - qemu.git/blob - python/qemu/utils/__init__.py
works with less than base ISA qemu-system-riscv32 -M virt -bios none -kernel output...
[qemu.git] / python / qemu / utils / __init__.py
1 """
2 QEMU development and testing utilities
3
4 This package provides a small handful of utilities for performing
5 various tasks not directly related to the launching of a VM.
6 """
7
8 # Copyright (C) 2021 Red Hat Inc.
9 #
10 # Authors:
11 #  John Snow <[email protected]>
12 #  Cleber Rosa <[email protected]>
13 #
14 # This work is licensed under the terms of the GNU GPL, version 2.  See
15 # the COPYING file in the top-level directory.
16 #
17
18 import os
19 import re
20 import shutil
21 from subprocess import CalledProcessError
22 import textwrap
23 from typing import Optional
24
25 # pylint: disable=import-error
26 from .accel import kvm_available, list_accel, tcg_available
27
28
29 __all__ = (
30     'VerboseProcessError',
31     'add_visual_margin',
32     'get_info_usernet_hostfwd_port',
33     'kvm_available',
34     'list_accel',
35     'tcg_available',
36 )
37
38
39 def get_info_usernet_hostfwd_port(info_usernet_output: str) -> Optional[int]:
40     """
41     Returns the port given to the hostfwd parameter via info usernet
42
43     :param info_usernet_output: output generated by hmp command "info usernet"
44     :return: the port number allocated by the hostfwd option
45     """
46     for line in info_usernet_output.split('\r\n'):
47         regex = r'TCP.HOST_FORWARD.*127\.0\.0\.1\s+(\d+)\s+10\.'
48         match = re.search(regex, line)
49         if match is not None:
50             return int(match[1])
51     return None
52
53
54 # pylint: disable=too-many-arguments
55 def add_visual_margin(
56         content: str = '',
57         width: Optional[int] = None,
58         name: Optional[str] = None,
59         padding: int = 1,
60         upper_left: str = '┏',
61         lower_left: str = '┗',
62         horizontal: str = '━',
63         vertical: str = '┃',
64 ) -> str:
65     """
66     Decorate and wrap some text with a visual decoration around it.
67
68     This function assumes that the text decoration characters are single
69     characters that display using a single monospace column.
70
71     ┏━ Example ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
72     ┃ This is what this function looks like with text content that's
73     ┃ wrapped to 66 characters. The right-hand margin is left open to
74     ┃ accommodate the occasional unicode character that might make
75     ┃ predicting the total "visual" width of a line difficult. This
76     ┃ provides a visual distinction that's good-enough, though.
77     ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
78
79     :param content: The text to wrap and decorate.
80     :param width:
81         The number of columns to use, including for the decoration
82         itself. The default (None) uses the available width of the
83         current terminal, or a fallback of 72 lines. A negative number
84         subtracts a fixed-width from the default size. The default obeys
85         the COLUMNS environment variable, if set.
86     :param name: A label to apply to the upper-left of the box.
87     :param padding: How many columns of padding to apply inside.
88     :param upper_left: Upper-left single-width text decoration character.
89     :param lower_left: Lower-left single-width text decoration character.
90     :param horizontal: Horizontal single-width text decoration character.
91     :param vertical: Vertical single-width text decoration character.
92     """
93     if width is None or width < 0:
94         avail = shutil.get_terminal_size(fallback=(72, 24))[0]
95         if width is None:
96             _width = avail
97         else:
98             _width = avail + width
99     else:
100         _width = width
101
102     prefix = vertical + (' ' * padding)
103
104     def _bar(name: Optional[str], top: bool = True) -> str:
105         ret = upper_left if top else lower_left
106         if name is not None:
107             ret += f"{horizontal} {name} "
108
109         filler_len = _width - len(ret)
110         ret += f"{horizontal * filler_len}"
111         return ret
112
113     def _wrap(line: str) -> str:
114         return os.linesep.join(
115             textwrap.wrap(
116                 line, width=_width - padding, initial_indent=prefix,
117                 subsequent_indent=prefix, replace_whitespace=False,
118                 drop_whitespace=True, break_on_hyphens=False)
119         )
120
121     return os.linesep.join((
122         _bar(name, top=True),
123         os.linesep.join(_wrap(line) for line in content.splitlines()),
124         _bar(None, top=False),
125     ))
126
127
128 class VerboseProcessError(CalledProcessError):
129     """
130     The same as CalledProcessError, but more verbose.
131
132     This is useful for debugging failed calls during test executions.
133     The return code, signal (if any), and terminal output will be displayed
134     on unhandled exceptions.
135     """
136     def summary(self) -> str:
137         """Return the normal CalledProcessError str() output."""
138         return super().__str__()
139
140     def __str__(self) -> str:
141         lmargin = '  '
142         width = -len(lmargin)
143         sections = []
144
145         # Does self.stdout contain both stdout and stderr?
146         has_combined_output = self.stderr is None
147
148         name = 'output' if has_combined_output else 'stdout'
149         if self.stdout:
150             sections.append(add_visual_margin(self.stdout, width, name))
151         else:
152             sections.append(f"{name}: N/A")
153
154         if self.stderr:
155             sections.append(add_visual_margin(self.stderr, width, 'stderr'))
156         elif not has_combined_output:
157             sections.append("stderr: N/A")
158
159         return os.linesep.join((
160             self.summary(),
161             textwrap.indent(os.linesep.join(sections), prefix=lmargin),
162         ))
This page took 0.032023 seconds and 4 git commands to generate.