Module rpa_logger.utils.terminal
Utilities for printing output to the terminal.
Expand source code
'''Utilities for printing output to the terminal.
'''
import re
from shutil import get_terminal_size
from sys import stdout
from threading import Event
from typing import TextIO
# From cli-spinners (https://www.npmjs.com/package/cli-spinners)
INTERVAL = 0.080 # seconds
ASCII_FRAMES = ['|', '/', '-', '\\']
FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
COLORS = dict(red=31, green=32, yellow=33, blue=34, magenta=35, grey=90)
'''Dictionary of supported color values for `rpa_logger.utils.terminal.color`.
'''
def bold(text: str) -> str:
'''Bold given text with ANSI escape codes.
Args:
text: Text to be formatted.
Returns:
String with formatted text.
'''
return f'\033[1m{text}\033[22m'
def color(text: str, color_name: str) -> str:
'''Color given text with ANSI escape codes.
Args:
text: Text to be formatted.
color_name: Color to format text with. See
`rpa_logger.utils.terminal.COLORS` for available values.
Returns:
String with formatted text.
'''
if color_name not in COLORS:
return text
return f'\033[{COLORS[color_name]}m{text}\033[39m'
def remove_ansi_escapes(text: str) -> str:
'''Remove ANSI escape codes from given string.
Args:
text: String with ANSI escape codes.
Returns:
`text` without any ANSI escape codes.
'''
return re.sub(r'\033\[[^m]+m', '', text)
def fit_to_width(text: str) -> str:
'''Fit formatted text into current terminal width
If text does not fit to current terminal width, the end of the string is
replaced with '…' character. If text contains ANSI escape codes,
formatting is cleared when text is truncated.
Args:
text: Text to fit to terminal width.
Returns:
The possibly truncated string
'''
max_len = get_terminal_size().columns
non_formatted_text = remove_ansi_escapes(text.replace('\r', ''))
if len(non_formatted_text) <= max_len:
return text
i = 0
j = 0
while i < max_len - 2:
match = re.match(r'\r|\033\[[0-9]+m', text[(i + j):])
if match:
j += len(match.group(0))
else:
i += 1
clear_formatting = '\033[0m' if re.search(r'\033\[[0-9]+m', text) else ''
return f'{text[:(i + j)]}{clear_formatting}…'
def clear_current_row(file: TextIO = stdout):
'''Clear current terminal row
Can be used, for example, when progress text is replaced with shorter one.
Args:
file: File to print output to. Defaults to stdout.
'''
width = get_terminal_size().columns
print(f'\r{" " * width}', file=file, end='')
def print_spinner_and_text(
text: str,
stop_event: Event,
file: TextIO = stdout,
ascii_only: bool = False) -> None:
'''Print spinner and text until stop event
Args:
text: Text to print after spinner.
stop_event: Event to stop spinner loop.
file: File to print output to. Defaults to stdout.
ascii_only: If true, use ascii only spinner.
'''
frames = FRAMES if not ascii_only else ASCII_FRAMES
i = 0
while not stop_event.wait(INTERVAL):
print(
fit_to_width(f'\r{frames[i % len(frames)]} {text}'),
file=file,
end='')
i += 1
print('\r', end='')
Global variables
var COLORS
-
Dictionary of supported color values for
color()
.
Functions
def bold(text: str) ‑> str
-
Bold given text with ANSI escape codes.
Args
text
- Text to be formatted.
Returns
String with formatted text.
Expand source code
def bold(text: str) -> str: '''Bold given text with ANSI escape codes. Args: text: Text to be formatted. Returns: String with formatted text. ''' return f'\033[1m{text}\033[22m'
def clear_current_row(file:
= sys.stdout) -
Clear current terminal row
Can be used, for example, when progress text is replaced with shorter one.
Args
file
- File to print output to. Defaults to stdout.
Expand source code
def clear_current_row(file: TextIO = stdout): '''Clear current terminal row Can be used, for example, when progress text is replaced with shorter one. Args: file: File to print output to. Defaults to stdout. ''' width = get_terminal_size().columns print(f'\r{" " * width}', file=file, end='')
def color(text: str, color_name: str) ‑> str
-
Color given text with ANSI escape codes.
Args
text
- Text to be formatted.
color_name
- Color to format text with. See
COLORS
for available values.
Returns
String with formatted text.
Expand source code
def color(text: str, color_name: str) -> str: '''Color given text with ANSI escape codes. Args: text: Text to be formatted. color_name: Color to format text with. See `rpa_logger.utils.terminal.COLORS` for available values. Returns: String with formatted text. ''' if color_name not in COLORS: return text return f'\033[{COLORS[color_name]}m{text}\033[39m'
def fit_to_width(text: str) ‑> str
-
Fit formatted text into current terminal width
If text does not fit to current terminal width, the end of the string is replaced with '…' character. If text contains ANSI escape codes, formatting is cleared when text is truncated.
Args
text
- Text to fit to terminal width.
Returns
The possibly truncated string
Expand source code
def fit_to_width(text: str) -> str: '''Fit formatted text into current terminal width If text does not fit to current terminal width, the end of the string is replaced with '…' character. If text contains ANSI escape codes, formatting is cleared when text is truncated. Args: text: Text to fit to terminal width. Returns: The possibly truncated string ''' max_len = get_terminal_size().columns non_formatted_text = remove_ansi_escapes(text.replace('\r', '')) if len(non_formatted_text) <= max_len: return text i = 0 j = 0 while i < max_len - 2: match = re.match(r'\r|\033\[[0-9]+m', text[(i + j):]) if match: j += len(match.group(0)) else: i += 1 clear_formatting = '\033[0m' if re.search(r'\033\[[0-9]+m', text) else '' return f'{text[:(i + j)]}{clear_formatting}…'
def print_spinner_and_text(text: str, stop_event: threading.Event, file:
= sys.stdout, ascii_only: bool = False) ‑> None -
Print spinner and text until stop event
Args
text
- Text to print after spinner.
stop_event
- Event to stop spinner loop.
file
- File to print output to. Defaults to stdout.
ascii_only
- If true, use ascii only spinner.
Expand source code
def print_spinner_and_text( text: str, stop_event: Event, file: TextIO = stdout, ascii_only: bool = False) -> None: '''Print spinner and text until stop event Args: text: Text to print after spinner. stop_event: Event to stop spinner loop. file: File to print output to. Defaults to stdout. ascii_only: If true, use ascii only spinner. ''' frames = FRAMES if not ascii_only else ASCII_FRAMES i = 0 while not stop_event.wait(INTERVAL): print( fit_to_width(f'\r{frames[i % len(frames)]} {text}'), file=file, end='') i += 1 print('\r', end='')
def remove_ansi_escapes(text: str) ‑> str
-
Remove ANSI escape codes from given string.
Args
text
- String with ANSI escape codes.
Returns
text
without any ANSI escape codes.Expand source code
def remove_ansi_escapes(text: str) -> str: '''Remove ANSI escape codes from given string. Args: text: String with ANSI escape codes. Returns: `text` without any ANSI escape codes. ''' return re.sub(r'\033\[[^m]+m', '', text)