From: Hanna Reitz <hreitz@redhat.com>
To: qemu-block@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>,
Peter Maydell <peter.maydell@linaro.org>,
Hanna Reitz <hreitz@redhat.com>,
qemu-devel@nongnu.org
Subject: [PULL 06/25] python/utils: add add_visual_margin() text decoration utility
Date: Tue, 22 Mar 2022 12:56:28 +0100 [thread overview]
Message-ID: <20220322115647.726044-7-hreitz@redhat.com> (raw)
In-Reply-To: <20220322115647.726044-1-hreitz@redhat.com>
From: John Snow <jsnow@redhat.com>
>>> print(add_visual_margin(msg, width=72, name="Commit Message"))
┏━ Commit Message ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ add_visual_margin() takes a chunk of text and wraps it in a visual
┃ container that force-wraps to a specified width. An optional title
┃ label may be given, and any of the individual glyphs used to draw the
┃ box may be replaced or specified as well.
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Acked-by: Hanna Reitz <hreitz@redhat.com>
Message-Id: <20220321201618.903471-2-jsnow@redhat.com>
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
---
python/qemu/utils/__init__.py | 78 +++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/python/qemu/utils/__init__.py b/python/qemu/utils/__init__.py
index 7f1a5138c4..b84c86d004 100644
--- a/python/qemu/utils/__init__.py
+++ b/python/qemu/utils/__init__.py
@@ -15,7 +15,10 @@
# the COPYING file in the top-level directory.
#
+import os
import re
+import shutil
+import textwrap
from typing import Optional
# pylint: disable=import-error
@@ -23,6 +26,7 @@
__all__ = (
+ 'add_visual_margin',
'get_info_usernet_hostfwd_port',
'kvm_available',
'list_accel',
@@ -43,3 +47,77 @@ def get_info_usernet_hostfwd_port(info_usernet_output: str) -> Optional[int]:
if match is not None:
return int(match[1])
return None
+
+
+# pylint: disable=too-many-arguments
+def add_visual_margin(
+ content: str = '',
+ width: Optional[int] = None,
+ name: Optional[str] = None,
+ padding: int = 1,
+ upper_left: str = '┏',
+ lower_left: str = '┗',
+ horizontal: str = '━',
+ vertical: str = '┃',
+) -> str:
+ """
+ Decorate and wrap some text with a visual decoration around it.
+
+ This function assumes that the text decoration characters are single
+ characters that display using a single monospace column.
+
+ ┏━ Example ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+ ┃ This is what this function looks like with text content that's
+ ┃ wrapped to 66 characters. The right-hand margin is left open to
+ ┃ accommodate the occasional unicode character that might make
+ ┃ predicting the total "visual" width of a line difficult. This
+ ┃ provides a visual distinction that's good-enough, though.
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ :param content: The text to wrap and decorate.
+ :param width:
+ The number of columns to use, including for the decoration
+ itself. The default (None) uses the the available width of the
+ current terminal, or a fallback of 72 lines. A negative number
+ subtracts a fixed-width from the default size. The default obeys
+ the COLUMNS environment variable, if set.
+ :param name: A label to apply to the upper-left of the box.
+ :param padding: How many columns of padding to apply inside.
+ :param upper_left: Upper-left single-width text decoration character.
+ :param lower_left: Lower-left single-width text decoration character.
+ :param horizontal: Horizontal single-width text decoration character.
+ :param vertical: Vertical single-width text decoration character.
+ """
+ if width is None or width < 0:
+ avail = shutil.get_terminal_size(fallback=(72, 24))[0]
+ if width is None:
+ _width = avail
+ else:
+ _width = avail + width
+ else:
+ _width = width
+
+ prefix = vertical + (' ' * padding)
+
+ def _bar(name: Optional[str], top: bool = True) -> str:
+ ret = upper_left if top else lower_left
+ if name is not None:
+ ret += f"{horizontal} {name} "
+
+ filler_len = _width - len(ret)
+ ret += f"{horizontal * filler_len}"
+ return ret
+
+ def _wrap(line: str) -> str:
+ return os.linesep.join(
+ textwrap.wrap(
+ line, width=_width - padding, initial_indent=prefix,
+ subsequent_indent=prefix, replace_whitespace=False,
+ drop_whitespace=True, break_on_hyphens=False)
+ )
+
+ return os.linesep.join((
+ _bar(name, top=True),
+ os.linesep.join(_wrap(line) for line in content.splitlines()),
+ _bar(None, top=False),
+ ))
--
2.35.1
next prev parent reply other threads:[~2022-03-22 12:08 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-22 11:56 [PULL 00/25] Block patches for 7.0-rc1 Hanna Reitz
2022-03-22 11:56 ` [PULL 01/25] tests: add (riscv virt) machine mapping to testenv Hanna Reitz
2022-03-22 11:56 ` [PULL 02/25] block/rbd: fix write zeroes with growing images Hanna Reitz
2022-03-22 11:56 ` [PULL 03/25] tests/qemu-iotests: Use GNU sed in two more spots where it is necessary Hanna Reitz
2022-03-22 11:56 ` [PULL 04/25] tests: Do not treat the iotests as separate meson test target anymore Hanna Reitz
2022-03-22 11:56 ` [PULL 05/25] tests/qemu-iotests/testrunner: Supply a test plan in TAP mode Hanna Reitz
2022-03-22 11:56 ` Hanna Reitz [this message]
2022-03-22 11:56 ` [PULL 07/25] python/utils: add VerboseProcessError Hanna Reitz
2022-03-22 11:56 ` [PULL 08/25] iotests: Remove explicit checks for qemu_img() == 0 Hanna Reitz
2022-03-22 11:56 ` [PULL 09/25] iotests: make qemu_img raise on non-zero rc by default Hanna Reitz
2022-03-22 11:56 ` [PULL 10/25] iotests: fortify compare_images() against crashes Hanna Reitz
2022-03-22 11:56 ` [PULL 11/25] iotests: add qemu_img_json() Hanna Reitz
2022-03-22 11:56 ` [PULL 12/25] iotests: use qemu_img_json() when applicable Hanna Reitz
2022-03-22 11:56 ` [PULL 13/25] iotests: add qemu_img_info() Hanna Reitz
2022-03-22 11:56 ` [PULL 14/25] iotests/remove-bitmap-from-backing: use qemu_img_info() Hanna Reitz
2022-03-22 11:56 ` [PULL 15/25] iotests: add qemu_img_map() function Hanna Reitz
2022-03-22 11:56 ` [PULL 16/25] iotests: change supports_quorum to use qemu_img Hanna Reitz
2022-03-22 11:56 ` [PULL 17/25] iotests: replace unchecked calls to qemu_img_pipe() Hanna Reitz
2022-03-22 11:56 ` [PULL 18/25] iotests/149: Remove qemu_img_pipe() call Hanna Reitz
2022-03-22 11:56 ` [PULL 19/25] iotests: remove remaining calls to qemu_img_pipe() Hanna Reitz
2022-03-22 11:56 ` [PULL 20/25] iotests: use qemu_img() in has_working_luks() Hanna Reitz
2022-03-22 11:56 ` [PULL 21/25] iotests: replace qemu_img_log('create', ...) calls Hanna Reitz
2022-03-22 11:56 ` [PULL 22/25] iotests: remove qemu_img_pipe_and_status() Hanna Reitz
2022-03-22 11:56 ` [PULL 23/25] iotests: make qemu_img_log and img_info_log raise on error Hanna Reitz
2022-03-22 11:56 ` [PULL 24/25] iotests.py: Filters for VM.run_job() Hanna Reitz
2022-03-22 11:56 ` [PULL 25/25] iotests/207: Filter host fingerprint Hanna Reitz
2022-03-22 17:05 ` [PULL 00/25] Block patches for 7.0-rc1 Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220322115647.726044-7-hreitz@redhat.com \
--to=hreitz@redhat.com \
--cc=kwolf@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).