Skip to content

Commit

Permalink
python/utils: add add_visual_margin() text decoration utility
Browse files Browse the repository at this point in the history
>>> 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>
  • Loading branch information
jnsnow authored and XanClic committed Mar 22, 2022
1 parent e48093a commit be73231
Showing 1 changed file with 78 additions and 0 deletions.
78 changes: 78 additions & 0 deletions python/qemu/utils/__init__.py
Expand Up @@ -15,14 +15,18 @@
# the COPYING file in the top-level directory.
#

import os
import re
import shutil
import textwrap
from typing import Optional

# pylint: disable=import-error
from .accel import kvm_available, list_accel, tcg_available


__all__ = (
'add_visual_margin',
'get_info_usernet_hostfwd_port',
'kvm_available',
'list_accel',
Expand All @@ -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),
))

0 comments on commit be73231

Please sign in to comment.