From: John Snow <jsnow@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Beraldo Leal <bleal@redhat.com>,
qemu-block@nongnu.org, John Snow <jsnow@redhat.com>,
Hanna Reitz <hreitz@redhat.com>, Cleber Rosa <crosa@redhat.com>,
Eric Blake <eblake@redhat.com>
Subject: [PATCH v5 01/18] python/utils: add add_visual_margin() text decoration utility
Date: Mon, 21 Mar 2022 16:16:01 -0400 [thread overview]
Message-ID: <20220321201618.903471-2-jsnow@redhat.com> (raw)
In-Reply-To: <20220321201618.903471-1-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>
---
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.34.1
next prev parent reply other threads:[~2022-03-21 20:21 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-21 20:16 [PATCH v5 00/18] iotests: add enhanced debugging info to qemu-img failures John Snow
2022-03-21 20:16 ` John Snow [this message]
2022-03-21 20:16 ` [PATCH v5 02/18] python/utils: add VerboseProcessError John Snow
2022-03-21 20:16 ` [PATCH v5 03/18] iotests: Remove explicit checks for qemu_img() == 0 John Snow
2022-03-21 20:16 ` [PATCH v5 04/18] iotests: make qemu_img raise on non-zero rc by default John Snow
2022-03-21 20:16 ` [PATCH v5 05/18] iotests: fortify compare_images() against crashes John Snow
2022-03-21 20:16 ` [PATCH v5 06/18] iotests: add qemu_img_json() John Snow
2022-03-21 20:16 ` [PATCH v5 07/18] iotests: use qemu_img_json() when applicable John Snow
2022-03-21 20:16 ` [PATCH v5 08/18] iotests: add qemu_img_info() John Snow
2022-03-21 20:16 ` [PATCH v5 09/18] iotests/remove-bitmap-from-backing: use qemu_img_info() John Snow
2022-03-21 20:16 ` [PATCH v5 10/18] iotests: add qemu_img_map() function John Snow
2022-03-21 20:16 ` [PATCH v5 11/18] iotests: change supports_quorum to use qemu_img John Snow
2022-03-21 20:16 ` [PATCH v5 12/18] iotests: replace unchecked calls to qemu_img_pipe() John Snow
2022-03-21 20:16 ` [PATCH v5 13/18] iotests/149: Remove qemu_img_pipe() call John Snow
2022-03-21 20:16 ` [PATCH v5 14/18] iotests: remove remaining calls to qemu_img_pipe() John Snow
2022-03-21 20:16 ` [PATCH v5 15/18] iotests: use qemu_img() in has_working_luks() John Snow
2022-03-21 20:16 ` [PATCH v5 16/18] iotests: replace qemu_img_log('create', ...) calls John Snow
2022-03-21 20:16 ` [PATCH v5 17/18] iotests: remove qemu_img_pipe_and_status() John Snow
2022-03-21 21:31 ` Eric Blake
2022-03-21 20:16 ` [PATCH v5 18/18] iotests: make qemu_img_log and img_info_log raise on error John Snow
2022-03-21 21:33 ` Eric Blake
2022-03-22 9:42 ` [PATCH v5 00/18] iotests: add enhanced debugging info to qemu-img failures Hanna Reitz
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=20220321201618.903471-2-jsnow@redhat.com \
--to=jsnow@redhat.com \
--cc=bleal@redhat.com \
--cc=crosa@redhat.com \
--cc=eblake@redhat.com \
--cc=hreitz@redhat.com \
--cc=kwolf@redhat.com \
--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).