meerschaum.utils.formatting
Utilities for formatting output text
1#! /usr/bin/env python 2# -*- coding: utf-8 -*- 3# vim:fenc=utf-8 4 5""" 6Utilities for formatting output text 7""" 8 9from __future__ import annotations 10import platform 11import os 12import sys 13import meerschaum as mrsm 14from meerschaum.utils.typing import Optional, Union, Any, Dict, Iterable 15from meerschaum.utils.formatting._shell import make_header 16from meerschaum.utils.formatting._pprint import pprint 17from meerschaum.utils.formatting._dataframe import pprint_df, format_dataframe 18from meerschaum.utils.formatting._pipes import ( 19 pprint_pipes, 20 highlight_pipes, 21 format_pipe_success_tuple, 22 print_pipes_results, 23 extract_stats_from_message, 24 pipe_repr, 25) 26from meerschaum.utils.threading import Lock, RLock 27 28_attrs = { 29 'ANSI': None, 30 'UNICODE': None, 31 'CHARSET': None, 32 'RESET': '\033[0m', 33} 34__all__ = sorted([ 35 'ANSI', 'CHARSET', 'UNICODE', 'RESET', 36 'colored', 37 'translate_rich_to_termcolor', 38 'get_console', 39 'format_success_tuple', 40 'print_tuple', 41 'print_options', 42 'fill_ansi', 43 'pprint', 44 'pprint_df', 45 'format_dataframe', 46 'highlight_pipes', 47 'pprint_pipes', 48 'make_header', 49 'pipe_repr', 50 'print_pipes_results', 51 'extract_stats_from_message', 52 'format_bytes', 53]) 54__pdoc__ = {} 55_locks = { 56 '_colorama_init': RLock(), 57} 58 59 60def colored_fallback(*args, **kw): 61 return ' '.join(args) 62 63 64def format_bytes(num_bytes: Optional[Union[int, float]], precision: int = 1) -> str: 65 """ 66 Return a human-readable representation of a number of bytes. 67 68 Parameters 69 ---------- 70 num_bytes: Optional[Union[int, float]] 71 The number of bytes to format. If `None`, return `'?'`. 72 73 precision: int, default 1 74 The number of decimal places to display for non-byte units. 75 76 Returns 77 ------- 78 A human-readable string such as `'1.2 MB'` or `'340.0 kB'`. 79 80 Examples 81 -------- 82 >>> format_bytes(0) 83 '0 B' 84 >>> format_bytes(1536) 85 '1.5 kB' 86 >>> format_bytes(None) 87 '?' 88 """ 89 if num_bytes is None: 90 return '?' 91 try: 92 value = float(num_bytes) 93 except (TypeError, ValueError): 94 return '?' 95 96 sign = '-' if value < 0 else '' 97 value = abs(value) 98 units = ('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB') 99 unit_index = 0 100 while value >= 1000.0 and unit_index < len(units) - 1: 101 value /= 1000.0 102 unit_index += 1 103 104 if unit_index == 0: 105 return f"{sign}{int(value)} {units[unit_index]}" 106 return f"{sign}{value:.{precision}f} {units[unit_index]}" 107 108def translate_rich_to_termcolor(*colors) -> tuple: 109 """Translate between rich and more_termcolor terminology.""" 110 _colors = [] 111 for c in colors: 112 _c_list = [] 113 ### handle 'bright' 114 c = c.replace('bright_', 'bright ') 115 116 ### handle 'on' 117 if ' on ' in c: 118 _on = c.split(' on ') 119 _colors.append(_on[0]) 120 for _c in _on[1:]: 121 _c_list.append('on ' + _c) 122 else: 123 _c_list += [c] 124 125 _colors += _c_list 126 127 return tuple(_colors) 128 129 130def rich_text_to_str(text: 'rich.text.Text') -> str: 131 """Convert a `rich.text.Text` object to a string with ANSI in-tact.""" 132 _console = get_console() 133 if _console is None: 134 return str(text) 135 with console.capture() as cap: 136 console.print(text) 137 string = cap.get() 138 return string[:-1] 139 140 141def _init(): 142 """ 143 Initial color settings (mostly for Windows). 144 """ 145 if platform.system() != "Windows": 146 return 147 if 'PYTHONIOENCODING' not in os.environ: 148 os.environ['PYTHONIOENCODING'] = 'utf-8' 149 if 'PYTHONLEGACYWINDOWSSTDIO' not in os.environ: 150 os.environ['PYTHONLEGACYWINDOWSSTDIO'] = 'utf-8' 151 sys.stdin.reconfigure(encoding='utf-8') 152 sys.stdout.reconfigure(encoding='utf-8') 153 sys.stderr.reconfigure(encoding='utf-8') 154 155 from ctypes import windll 156 k = windll.kernel32 157 k.SetConsoleMode(k.GetStdHandle(-11), 7) 158 os.system("color") 159 160 from meerschaum.utils.packages import attempt_import 161 ### init colorama for Windows color output 162 colorama, more_termcolor = attempt_import( 163 'colorama', 164 'more_termcolor', 165 lazy = False, 166 warn = False, 167 color = False, 168 ) 169 try: 170 colorama.init(autoreset=False) 171 success = True 172 except Exception: 173 import traceback 174 traceback.print_exc() 175 _attrs['ANSI'], _attrs['UNICODE'], _attrs['CHARSET'] = False, False, 'ascii' 176 success = False 177 178 if more_termcolor is None: 179 _attrs['ANSI'], _attrs['UNICODE'], _attrs['CHARSET'] = False, False, 'ascii' 180 success = False 181 182 return success 183 184_colorama_init = False 185def colored(text: str, *colors, as_rich_text: bool=False, **kw) -> Union[str, 'rich.text.Text']: 186 """Apply colors and rich styles to a string. 187 If a `style` keyword is provided, a `rich.text.Text` object will be parsed into a string. 188 Otherwise attempt to use the legacy `more_termcolor.colored` method. 189 190 Parameters 191 ---------- 192 text: str 193 The string to apply formatting to. 194 195 *colors: 196 A list of colors to pass to `more_termcolor.colored()`. 197 Has no effect if `style` is provided. 198 199 style: str, default None 200 If provided, pass to `rich` for processing. 201 202 as_rich_text: bool, default False 203 If `True`, return a `rich.Text` object. 204 `style` must be provided. 205 206 **kw: 207 Keyword arguments to pass to `rich.text.Text` or `more_termcolor`. 208 209 210 Returns 211 ------- 212 An ANSI-formatted string or a `rich.text.Text` object if `as_rich_text` is `True`. 213 214 """ 215 from meerschaum.utils.packages import import_rich, attempt_import 216 global _colorama_init 217 _init() 218 with _locks['_colorama_init']: 219 if not _colorama_init: 220 _colorama_init = _init() 221 222 if 'style' in kw: 223 rich = import_rich() 224 rich_text = attempt_import('rich.text') 225 text_obj = rich_text.Text(text, **kw) 226 if as_rich_text: 227 return text_obj 228 return rich_text_to_str(text_obj) 229 230 more_termcolor = attempt_import('more_termcolor', lazy=False) 231 try: 232 colored_text = more_termcolor.colored(text, *colors, **kw) 233 except Exception as e: 234 colored_text = None 235 236 if colored_text is not None: 237 return colored_text 238 239 try: 240 _colors = translate_rich_to_termcolor(*colors) 241 colored_text = more_termcolor.colored(text, *_colors, **kw) 242 except Exception as e: 243 colored_text = None 244 245 if colored_text is None: 246 ### NOTE: warn here? 247 return text 248 249 return colored_text 250 251console = None 252def get_console(): 253 """Get the rich console.""" 254 global console 255 if console is not None: 256 return console 257 from meerschaum.utils.packages import import_rich, attempt_import 258 rich = import_rich() 259 rich_console = attempt_import('rich.console') 260 try: 261 console = rich_console.Console(force_terminal=True, color_system='truecolor') 262 except Exception: 263 import traceback 264 traceback.print_exc() 265 console = None 266 return console 267 268 269def print_tuple( 270 tup: mrsm.SuccessTuple, 271 skip_common: bool = True, 272 common_only: bool = False, 273 upper_padding: int = 1, 274 lower_padding: int = 1, 275 left_padding: int = 1, 276 calm: bool = False, 277 _progress: Optional['rich.progress.Progress'] = None, 278) -> None: 279 """ 280 Format `meerschaum.utils.typing.SuccessTuple`. 281 282 Parameters 283 ---------- 284 skip_common: bool, default True 285 If `True`, do not print common success tuples (i.e. `(True, "Success")`). 286 287 common_only: bool, default False 288 If `True`, only print if the success tuple is common. 289 290 upper_padding: int, default 0 291 How many newlines to prepend to the message. 292 293 lower_padding: int, default 0 294 How many newlines to append to the message. 295 296 left_padding: int, default 1 297 How mant spaces to preprend to the message. 298 299 calm: bool, default False 300 If `True`, use the default emoji and color scheme. 301 302 """ 303 from meerschaum._internal.static import STATIC_CONFIG 304 do_print = True 305 306 omit_messages = STATIC_CONFIG['system']['success']['ignore'] 307 308 if common_only: 309 skip_common = False 310 do_print = tup[1].strip() in omit_messages 311 312 if skip_common: 313 do_print = tup[1].strip() not in omit_messages 314 315 if not do_print: 316 return 317 318 print(format_success_tuple( 319 tup, 320 upper_padding=upper_padding, 321 lower_padding=lower_padding, 322 calm=calm, 323 _progress=_progress, 324 )) 325 326 327def format_success_tuple( 328 tup: mrsm.SuccessTuple, 329 upper_padding: int = 0, 330 lower_padding: int = 0, 331 left_padding: int = 1, 332 calm: bool = False, 333 _progress: Optional['rich.progress.Progress'] = None, 334) -> str: 335 """ 336 Format `meerschaum.utils.typing.SuccessTuple`. 337 338 Parameters 339 ---------- 340 upper_padding: int, default 0 341 How many newlines to prepend to the message. 342 343 lower_padding: int, default 0 344 How many newlines to append to the message. 345 346 left_padding: int, default 1 347 How mant spaces to preprend to the message. 348 349 calm: bool, default False 350 If `True`, use the default emoji and color scheme. 351 """ 352 _init() 353 try: 354 status = 'success' if tup[0] else 'failure' 355 except TypeError: 356 status = 'failure' 357 tup = None, None 358 359 if calm: 360 status += '_calm' 361 362 ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET') 363 from meerschaum.config import get_config 364 status_config = get_config('formatting', status, patch=True) 365 366 msg = (' ' * left_padding) + status_config[CHARSET]['icon'] + ' ' + str(highlight_pipes(tup[1])) 367 lines = msg.split('\n') 368 lines = [lines[0]] + [ 369 ((' ' + line if not line.startswith(' ') else line)) 370 for line in lines[1:] 371 ] 372 if ANSI: 373 lines[0] = fill_ansi(lines[0], **status_config['ansi']['rich']) 374 375 msg = '\n'.join(lines) 376 msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding) 377 return msg 378 379 380def print_options( 381 options: Optional[Iterable[Any]] = None, 382 nopretty: bool = False, 383 no_rich: bool = False, 384 name: str = 'options', 385 header: Optional[str] = None, 386 num_cols: Optional[int] = None, 387 adjust_cols: bool = True, 388 sort_options: bool = False, 389 number_options: bool = False, 390 **kw 391) -> None: 392 """ 393 Print items in an iterable as a fancy table. 394 395 Parameters 396 ---------- 397 options: Optional[Dict[str, Any]], default None 398 The iterable to be printed. 399 400 nopretty: bool, default False 401 If `True`, don't use fancy formatting. 402 403 no_rich: bool, default False 404 If `True`, don't use `rich` to format the output. 405 406 name: str, default 'options' 407 The text in the default header after `'Available'`. 408 409 header: Optional[str], default None 410 If provided, override `name` and use this as the header text. 411 412 num_cols: Optional[int], default None 413 How many columns in the table. Depends on the terminal size. If `None`, use 8. 414 415 adjust_cols: bool, default True 416 If `True`, adjust the number of columns depending on the terminal size. 417 418 sort_options: bool, default False 419 If `True`, print the options in sorted order. 420 421 number_options: bool, default False 422 If `True`, print the option's number in the list (1 index). 423 424 """ 425 from meerschaum.utils.packages import import_rich 426 from meerschaum.utils.formatting import highlight_pipes 427 from meerschaum.utils.misc import get_cols_lines, string_width 428 429 if options is None: 430 options = {} 431 _options = [] 432 for o in options: 433 _options.append(str(o)) 434 if sort_options: 435 _options = sorted(_options) 436 _header = f"\nAvailable {name}" if header is None else header 437 438 if num_cols is None: 439 num_cols = 8 440 441 def _print_options_no_rich(): 442 if not nopretty: 443 print() 444 print(make_header(_header)) 445 ### print actions 446 for i, option in enumerate(_options): 447 marker = '-' if not number_options else (str(i + 1) + '.') 448 if not nopretty: 449 print(f" {marker} ", end="") 450 print(option) 451 if not nopretty: 452 print() 453 454 rich = import_rich() 455 if rich is None or nopretty or no_rich: 456 _print_options_no_rich() 457 return None 458 459 ### Prevent too many options from being truncated on small terminals. 460 if adjust_cols and _options: 461 _cols, _lines = get_cols_lines() 462 while num_cols > 1: 463 cell_len = int(((_cols - 4) - (3 * (num_cols - 1))) / num_cols) 464 num_too_big = sum([(1 if string_width(o) > cell_len else 0) for o in _options]) 465 if num_too_big > int(len(_options) / 3): 466 num_cols -= 1 467 continue 468 break 469 470 from meerschaum.utils.packages import attempt_import 471 rich_table = attempt_import('rich.table') 472 Text = attempt_import('rich.text').Text 473 box = attempt_import('rich.box') 474 Table = rich_table.Table 475 476 if _header is not None: 477 table = Table( 478 title=_header, 479 box=box.SIMPLE, 480 show_header=False, 481 show_footer=False, 482 title_style='', 483 expand = True, 484 ) 485 else: 486 table = Table.grid(padding=(0, 2)) 487 for i in range(num_cols): 488 table.add_column() 489 490 if len(_options) < 12: 491 ### If fewer than 12 items, use a single column 492 for i, option in enumerate(_options): 493 item = highlight_pipes(option) 494 if number_options: 495 item = str(i + 1) + '. ' + item 496 table.add_row(Text.from_ansi(item)) 497 else: 498 ### Otherwise, use multiple columns as before 499 num_rows = (len(_options) + num_cols - 1) // num_cols 500 item_ix = 0 501 for i in range(num_rows): 502 row = [] 503 for j in range(num_cols): 504 index = i + j * num_rows 505 if index < len(_options): 506 item = highlight_pipes(_options[index]) 507 if number_options: 508 item = str(i + 1) + '. ' + item 509 row.append(Text.from_ansi(item)) 510 item_ix += 1 511 else: 512 row.append('') 513 table.add_row(*row) 514 515 get_console().print(table) 516 return None 517 518 519def fill_ansi(string: str, style: str = '') -> str: 520 """ 521 Fill in non-formatted segments of ANSI text. 522 523 Parameters 524 ---------- 525 string: str 526 A string which contains ANSI escape codes. 527 528 style: str 529 Style arguments to pass to `rich.text.Text`. 530 531 Returns 532 ------- 533 A string with ANSI styling applied to the segments which don't yet have a style applied. 534 """ 535 from meerschaum.utils.packages import import_rich, attempt_import 536 from meerschaum.utils.misc import iterate_chunks 537 _ = import_rich() 538 rich_ansi, rich_text = attempt_import('rich.ansi', 'rich.text') 539 Text = rich_text.Text 540 try: 541 msg = Text.from_ansi(string) 542 except AttributeError: 543 import traceback 544 traceback.print_exc() 545 msg = '' 546 547 plain_indices = [] 548 for left_span, right_span in iterate_chunks(msg.spans, 2, fillvalue=len(msg)): 549 left = left_span.end 550 right = right_span.start if not isinstance(right_span, int) else right_span 551 if left != right: 552 plain_indices.append((left, right)) 553 if msg.spans: 554 if msg.spans[0].start != 0: 555 plain_indices = [(0, msg.spans[0].start)] + plain_indices 556 if plain_indices and msg.spans[-1].end != len(msg) and plain_indices[-1][1] != len(msg): 557 plain_indices.append((msg.spans[-1].end, len(msg))) 558 559 if plain_indices: 560 for left, right in plain_indices: 561 msg.stylize(style, left, right) 562 else: 563 msg = Text(str(msg), style) 564 565 return rich_text_to_str(msg) 566 567 568def __getattr__(name: str) -> str: 569 """ 570 Lazily load module-level variables. 571 """ 572 if name.startswith('__') and name.endswith('__'): 573 raise AttributeError("Cannot import dunders from this module.") 574 575 if name in _attrs: 576 if _attrs[name] is not None: 577 return _attrs[name] 578 from meerschaum.config import get_config 579 if name.lower() in get_config('formatting'): 580 _attrs[name] = get_config('formatting', name.lower()) 581 elif name == 'CHARSET': 582 _attrs[name] = 'unicode' if __getattr__('UNICODE') else 'ascii' 583 return _attrs[name] 584 585 if name == '__wrapped__': 586 import sys 587 return sys.modules[__name__] 588 if name == '__all__': 589 return __all__ 590 591 try: 592 return globals()[name] 593 except KeyError: 594 raise AttributeError(f"Could not find '{name}'")
186def colored(text: str, *colors, as_rich_text: bool=False, **kw) -> Union[str, 'rich.text.Text']: 187 """Apply colors and rich styles to a string. 188 If a `style` keyword is provided, a `rich.text.Text` object will be parsed into a string. 189 Otherwise attempt to use the legacy `more_termcolor.colored` method. 190 191 Parameters 192 ---------- 193 text: str 194 The string to apply formatting to. 195 196 *colors: 197 A list of colors to pass to `more_termcolor.colored()`. 198 Has no effect if `style` is provided. 199 200 style: str, default None 201 If provided, pass to `rich` for processing. 202 203 as_rich_text: bool, default False 204 If `True`, return a `rich.Text` object. 205 `style` must be provided. 206 207 **kw: 208 Keyword arguments to pass to `rich.text.Text` or `more_termcolor`. 209 210 211 Returns 212 ------- 213 An ANSI-formatted string or a `rich.text.Text` object if `as_rich_text` is `True`. 214 215 """ 216 from meerschaum.utils.packages import import_rich, attempt_import 217 global _colorama_init 218 _init() 219 with _locks['_colorama_init']: 220 if not _colorama_init: 221 _colorama_init = _init() 222 223 if 'style' in kw: 224 rich = import_rich() 225 rich_text = attempt_import('rich.text') 226 text_obj = rich_text.Text(text, **kw) 227 if as_rich_text: 228 return text_obj 229 return rich_text_to_str(text_obj) 230 231 more_termcolor = attempt_import('more_termcolor', lazy=False) 232 try: 233 colored_text = more_termcolor.colored(text, *colors, **kw) 234 except Exception as e: 235 colored_text = None 236 237 if colored_text is not None: 238 return colored_text 239 240 try: 241 _colors = translate_rich_to_termcolor(*colors) 242 colored_text = more_termcolor.colored(text, *_colors, **kw) 243 except Exception as e: 244 colored_text = None 245 246 if colored_text is None: 247 ### NOTE: warn here? 248 return text 249 250 return colored_text
Apply colors and rich styles to a string.
If a style keyword is provided, a rich.text.Text object will be parsed into a string.
Otherwise attempt to use the legacy more_termcolor.colored method.
Parameters
- text (str): The string to apply formatting to.
- *colors:: A list of colors to pass to
more_termcolor.colored(). Has no effect ifstyleis provided. - style (str, default None):
If provided, pass to
richfor processing. - as_rich_text (bool, default False):
If
True, return arich.Textobject.stylemust be provided. - **kw:: Keyword arguments to pass to
rich.text.Textormore_termcolor.
Returns
- An ANSI-formatted string or a
rich.text.Textobject ifas_rich_textisTrue.
487def extract_stats_from_message( 488 message: str, 489 stat_keys: Optional[List[str]] = None, 490) -> Dict[str, int]: 491 """ 492 Given a sync message, return the insert, update, upsert stats from within. 493 494 Parameters 495 ---------- 496 message: str 497 The message to parse for statistics. 498 499 stat_keys: Optional[List[str]], default None 500 If provided, search for these words (case insensitive) in the message. 501 Defaults to `['inserted', 'updated', 'upserted']`. 502 503 Returns 504 ------- 505 A dictionary mapping the stat keys to the total number of rows affected. 506 """ 507 stat_keys = stat_keys or ['inserted', 'updated', 'upserted', 'checked'] 508 lines_stats = [extract_stats_from_line(line, stat_keys) for line in message.split('\n')] 509 message_stats = { 510 stat_key: sum(stats.get(stat_key, 0) for stats in lines_stats) 511 for stat_key in stat_keys 512 } 513 return message_stats
Given a sync message, return the insert, update, upsert stats from within.
Parameters
- message (str): The message to parse for statistics.
- stat_keys (Optional[List[str]], default None):
If provided, search for these words (case insensitive) in the message.
Defaults to
['inserted', 'updated', 'upserted'].
Returns
- A dictionary mapping the stat keys to the total number of rows affected.
520def fill_ansi(string: str, style: str = '') -> str: 521 """ 522 Fill in non-formatted segments of ANSI text. 523 524 Parameters 525 ---------- 526 string: str 527 A string which contains ANSI escape codes. 528 529 style: str 530 Style arguments to pass to `rich.text.Text`. 531 532 Returns 533 ------- 534 A string with ANSI styling applied to the segments which don't yet have a style applied. 535 """ 536 from meerschaum.utils.packages import import_rich, attempt_import 537 from meerschaum.utils.misc import iterate_chunks 538 _ = import_rich() 539 rich_ansi, rich_text = attempt_import('rich.ansi', 'rich.text') 540 Text = rich_text.Text 541 try: 542 msg = Text.from_ansi(string) 543 except AttributeError: 544 import traceback 545 traceback.print_exc() 546 msg = '' 547 548 plain_indices = [] 549 for left_span, right_span in iterate_chunks(msg.spans, 2, fillvalue=len(msg)): 550 left = left_span.end 551 right = right_span.start if not isinstance(right_span, int) else right_span 552 if left != right: 553 plain_indices.append((left, right)) 554 if msg.spans: 555 if msg.spans[0].start != 0: 556 plain_indices = [(0, msg.spans[0].start)] + plain_indices 557 if plain_indices and msg.spans[-1].end != len(msg) and plain_indices[-1][1] != len(msg): 558 plain_indices.append((msg.spans[-1].end, len(msg))) 559 560 if plain_indices: 561 for left, right in plain_indices: 562 msg.stylize(style, left, right) 563 else: 564 msg = Text(str(msg), style) 565 566 return rich_text_to_str(msg)
Fill in non-formatted segments of ANSI text.
Parameters
- string (str): A string which contains ANSI escape codes.
- style (str):
Style arguments to pass to
rich.text.Text.
Returns
- A string with ANSI styling applied to the segments which don't yet have a style applied.
65def format_bytes(num_bytes: Optional[Union[int, float]], precision: int = 1) -> str: 66 """ 67 Return a human-readable representation of a number of bytes. 68 69 Parameters 70 ---------- 71 num_bytes: Optional[Union[int, float]] 72 The number of bytes to format. If `None`, return `'?'`. 73 74 precision: int, default 1 75 The number of decimal places to display for non-byte units. 76 77 Returns 78 ------- 79 A human-readable string such as `'1.2 MB'` or `'340.0 kB'`. 80 81 Examples 82 -------- 83 >>> format_bytes(0) 84 '0 B' 85 >>> format_bytes(1536) 86 '1.5 kB' 87 >>> format_bytes(None) 88 '?' 89 """ 90 if num_bytes is None: 91 return '?' 92 try: 93 value = float(num_bytes) 94 except (TypeError, ValueError): 95 return '?' 96 97 sign = '-' if value < 0 else '' 98 value = abs(value) 99 units = ('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB') 100 unit_index = 0 101 while value >= 1000.0 and unit_index < len(units) - 1: 102 value /= 1000.0 103 unit_index += 1 104 105 if unit_index == 0: 106 return f"{sign}{int(value)} {units[unit_index]}" 107 return f"{sign}{value:.{precision}f} {units[unit_index]}"
Return a human-readable representation of a number of bytes.
Parameters
- num_bytes (Optional[Union[int, float]]):
The number of bytes to format. If
None, return'?'. - precision (int, default 1): The number of decimal places to display for non-byte units.
Returns
- A human-readable string such as
'1.2 MB'or'340.0 kB'.
Examples
>>> format_bytes(0)
'0 B'
>>> format_bytes(1536)
'1.5 kB'
>>> format_bytes(None)
'?'
20def format_dataframe(df: Any, max_rows: int | None = None) -> str: 21 """ 22 Return a full, untruncated string representation of a DataFrame. 23 24 Parameters 25 ---------- 26 df: pandas.DataFrame 27 The DataFrame to format. 28 29 max_rows: int | None, default None 30 If set, only render the first `max_rows` rows and append a note. 31 `None` renders every row. 32 33 Returns 34 ------- 35 A Markdown table (falling back to a plain fixed-width table if `tabulate` 36 is unavailable), followed by a `[rows x columns]` shape footer. 37 """ 38 from meerschaum.utils.packages import attempt_import 39 pd = attempt_import('pandas', lazy=False) 40 41 n_rows, n_cols = df.shape 42 render_df = df if (max_rows is None or n_rows <= max_rows) else df.head(max_rows) 43 truncated_rows = n_rows - len(render_df) 44 45 ### Never let pandas insert `...` for columns, rows, or cell contents. 46 options = [ 47 'display.max_columns', None, 48 'display.max_rows', None, 49 'display.width', None, 50 'display.max_colwidth', None, 51 ] 52 53 body = None 54 with pd.option_context(*options): 55 try: 56 tabulate = attempt_import('tabulate', warn=False) 57 if tabulate is not None: 58 body = render_df.to_markdown(index=False) 59 except Exception: 60 body = None 61 62 if body is None: 63 ### Fall back to the built-in fixed-width table (no extra dependency). 64 body = render_df.to_string(index=False, max_rows=None, max_cols=None) 65 66 footer = ( 67 f"\n[{n_rows} row{'s' if n_rows != 1 else ''} " 68 f"x {n_cols} column{'s' if n_cols != 1 else ''}]" 69 ) 70 if truncated_rows > 0: 71 footer = ( 72 f"\n[showing first {len(render_df)} of {n_rows} rows " 73 f"x {n_cols} column{'s' if n_cols != 1 else ''}]" 74 ) 75 return body + footer
Return a full, untruncated string representation of a DataFrame.
Parameters
- df (pandas.DataFrame): The DataFrame to format.
- max_rows (int | None, default None):
If set, only render the first
max_rowsrows and append a note.Nonerenders every row.
Returns
- A Markdown table (falling back to a plain fixed-width table if
tabulate - is unavailable), followed by a
[rows x columns]shape footer.
328def format_success_tuple( 329 tup: mrsm.SuccessTuple, 330 upper_padding: int = 0, 331 lower_padding: int = 0, 332 left_padding: int = 1, 333 calm: bool = False, 334 _progress: Optional['rich.progress.Progress'] = None, 335) -> str: 336 """ 337 Format `meerschaum.utils.typing.SuccessTuple`. 338 339 Parameters 340 ---------- 341 upper_padding: int, default 0 342 How many newlines to prepend to the message. 343 344 lower_padding: int, default 0 345 How many newlines to append to the message. 346 347 left_padding: int, default 1 348 How mant spaces to preprend to the message. 349 350 calm: bool, default False 351 If `True`, use the default emoji and color scheme. 352 """ 353 _init() 354 try: 355 status = 'success' if tup[0] else 'failure' 356 except TypeError: 357 status = 'failure' 358 tup = None, None 359 360 if calm: 361 status += '_calm' 362 363 ANSI, CHARSET = __getattr__('ANSI'), __getattr__('CHARSET') 364 from meerschaum.config import get_config 365 status_config = get_config('formatting', status, patch=True) 366 367 msg = (' ' * left_padding) + status_config[CHARSET]['icon'] + ' ' + str(highlight_pipes(tup[1])) 368 lines = msg.split('\n') 369 lines = [lines[0]] + [ 370 ((' ' + line if not line.startswith(' ') else line)) 371 for line in lines[1:] 372 ] 373 if ANSI: 374 lines[0] = fill_ansi(lines[0], **status_config['ansi']['rich']) 375 376 msg = '\n'.join(lines) 377 msg = ('\n' * upper_padding) + msg + ('\n' * lower_padding) 378 return msg
Format meerschaum.utils.typing.SuccessTuple.
Parameters
- upper_padding (int, default 0): How many newlines to prepend to the message.
- lower_padding (int, default 0): How many newlines to append to the message.
- left_padding (int, default 1): How mant spaces to preprend to the message.
- calm (bool, default False):
If
True, use the default emoji and color scheme.
253def get_console(): 254 """Get the rich console.""" 255 global console 256 if console is not None: 257 return console 258 from meerschaum.utils.packages import import_rich, attempt_import 259 rich = import_rich() 260 rich_console = attempt_import('rich.console') 261 try: 262 console = rich_console.Console(force_terminal=True, color_system='truecolor') 263 except Exception: 264 import traceback 265 traceback.print_exc() 266 console = None 267 return console
Get the rich console.
345def highlight_pipes(message: str) -> str: 346 """ 347 Add syntax highlighting to an info message containing stringified `meerschaum.Pipe` objects. 348 """ 349 if 'Pipe(' not in message: 350 return message 351 352 from meerschaum.utils.misc import parse_arguments_str 353 segments = message.split('Pipe(') 354 msg = '' 355 for i, segment in enumerate(segments): 356 if i == 0: 357 msg += segment 358 continue 359 360 paren_index = segment.find(')') 361 if paren_index == -1: 362 msg += 'Pipe(' + segment 363 continue 364 365 pipe_args_str = segment[:paren_index] 366 try: 367 args, kwargs = parse_arguments_str(pipe_args_str) 368 pipe_dict = { 369 'connector_keys': args[0], 370 'metric_key': args[1], 371 } 372 if len(args) > 2: 373 pipe_dict['location_key'] = args[2] 374 if 'instance' in kwargs: 375 pipe_dict['instance_keys'] = kwargs['instance'] 376 377 _to_add = pipe_repr(pipe_dict) + segment[paren_index + 1:] 378 except Exception: 379 _to_add = 'Pipe(' + segment 380 msg += _to_add 381 return msg
Add syntax highlighting to an info message containing stringified meerschaum.Pipe objects.
17def make_header( 18 message: str, 19 ruler: str = '─', 20 left_pad: int = 2, 21 top: bool = False, 22 top_pad: int = 1, 23) -> str: 24 """ 25 Format a message string with a ruler or box. 26 Length of the ruler is the length of the longest word. 27 28 Example: 29 'My\nheader' -> ' My\n header\n ──────' 30 """ 31 32 from meerschaum.utils.formatting import ANSI, UNICODE, colored 33 if not UNICODE: 34 ruler = '-' 35 words = message.split('\n') 36 max_length = 0 37 for w in words: 38 length = len(w) 39 if length > max_length: 40 max_length = length 41 42 left_buffer = left_pad * ' ' 43 44 return ( 45 (('\n' * top_pad) if top else "") 46 + left_buffer 47 + (((ruler * max_length) + '\n') if top else "") 48 + message.replace('\n', '\n' + left_buffer) 49 + "\n" 50 + left_buffer 51 + (ruler * max_length) 52 )
Format a message string with a ruler or box. Length of the ruler is the length of the longest word.
Example:
'My
header' -> ' My header ──────'
280def pipe_repr( 281 pipe: Union[mrsm.Pipe, Dict[str, Any]], 282 as_rich_text: bool = False, 283 ansi: Optional[bool] = None, 284) -> Union[str, 'rich.text.Text']: 285 """ 286 Return a formatted string for representing a `meerschaum.Pipe`. 287 """ 288 from meerschaum.utils.formatting import ANSI, colored, rich_text_to_str 289 from meerschaum.utils.packages import import_rich, attempt_import 290 import meerschaum as mrsm 291 292 _ = import_rich() 293 Text = attempt_import('rich.text').Text 294 295 if isinstance(pipe, mrsm.Pipe): 296 connector_keys = pipe.connector_keys 297 metric_key = pipe.metric_key 298 location_key = pipe.location_key 299 instance_keys = pipe.instance_keys 300 else: 301 connector_keys = pipe.get('connector_keys') 302 metric_key = pipe.get('metric_key') 303 location_key = pipe.get('location_key') 304 instance_keys = pipe.get('instance_keys', get_config('meerschaum', 'instance')) 305 306 styles = get_config('formatting', 'pipes', '__repr__', 'ansi', 'styles') 307 if not ANSI or (ansi is False): 308 styles = {k: '' for k in styles} 309 _pipe_style_prefix, _pipe_style_suffix = ( 310 (("[" + styles['Pipe'] + "]"), ("[/" + styles['Pipe'] + "]")) if styles['Pipe'] 311 else ('', '') 312 ) 313 text_obj = ( 314 Text.from_markup(_pipe_style_prefix + "Pipe(" + _pipe_style_suffix) 315 + colored(("'" + connector_keys + "'"), style=styles['connector'], as_rich_text=True) 316 + Text.from_markup(_pipe_style_prefix + ", " + _pipe_style_suffix) 317 + colored(("'" + metric_key + "'"), style=styles['metric'], as_rich_text=True) 318 + ( 319 ( 320 colored(', ', style=styles['punctuation'], as_rich_text=True) 321 + colored( 322 ("'" + location_key + "'"), 323 style=styles['location'], as_rich_text=True 324 ) 325 ) if location_key is not None 326 else colored('', style='', as_rich_text=True) 327 ) + ( 328 ( ### Add the `instance=` argument. 329 colored(', instance=', style=styles['punctuation'], as_rich_text=True) 330 + colored( 331 ("'" + instance_keys + "'"), 332 style=styles['instance'], as_rich_text=True 333 ) 334 ) if instance_keys != get_config('meerschaum', 'instance') 335 else colored('', style='', as_rich_text=True) 336 ) 337 + Text.from_markup(_pipe_style_prefix + ")" + _pipe_style_suffix) 338 ) 339 if as_rich_text: 340 return text_obj 341 return rich_text_to_str(text_obj).replace('\n', '')
Return a formatted string for representing a meerschaum.Pipe.
10def pprint( 11 *args, 12 detect_password: bool = True, 13 nopretty: bool = False, 14 **kw 15) -> None: 16 """Pretty print an object according to the configured ANSI and UNICODE settings. 17 If detect_password is True (default), search and replace passwords with '*' characters. 18 Does not mutate objects. 19 """ 20 import copy 21 import json 22 from meerschaum.utils.packages import attempt_import, import_rich 23 from meerschaum.utils.formatting import ANSI, get_console, print_tuple 24 from meerschaum.utils.warnings import error 25 from meerschaum.utils.misc import replace_password, dict_from_od, filter_keywords 26 from collections import OrderedDict 27 28 if ( 29 len(args) == 1 30 and 31 isinstance(args[0], tuple) 32 and 33 len(args[0]) == 2 34 and 35 isinstance(args[0][0], bool) 36 and 37 isinstance(args[0][1], str) 38 ): 39 return print_tuple(args[0], **filter_keywords(print_tuple, **kw)) 40 41 modify = True 42 rich_pprint = None 43 if ANSI and not nopretty: 44 rich = import_rich() 45 if rich is not None: 46 rich_pretty = attempt_import('rich.pretty') 47 if rich_pretty is not None: 48 def _rich_pprint(*args, **kw): 49 _console = get_console() 50 _kw = filter_keywords(_console.print, **kw) 51 _console.print(*args, **_kw) 52 rich_pprint = _rich_pprint 53 elif not nopretty: 54 pprintpp = attempt_import('pprintpp', warn=False) 55 try: 56 _pprint = pprintpp.pprint 57 except Exception : 58 import pprint as _pprint_module 59 _pprint = _pprint_module.pprint 60 61 func = ( 62 _pprint if rich_pprint is None else rich_pprint 63 ) if not nopretty else print 64 65 try: 66 args_copy = copy.deepcopy(args) 67 except Exception: 68 args_copy = args 69 modify = False 70 71 _args = [] 72 for a in args: 73 c = a 74 ### convert OrderedDict into dict 75 if isinstance(a, OrderedDict) or issubclass(type(a), OrderedDict): 76 c = dict_from_od(copy.deepcopy(c)) 77 _args.append(c) 78 args = _args 79 80 _args = list(args) 81 if detect_password and modify: 82 _args = [] 83 for a in args: 84 c = a 85 if isinstance(c, dict): 86 c = replace_password(copy.deepcopy(c)) 87 if nopretty: 88 try: 89 c = json.dumps(c) 90 is_json = True 91 except Exception: 92 is_json = False 93 if not is_json: 94 try: 95 c = str(c) 96 except Exception: 97 pass 98 _args.append(c) 99 100 ### filter out unsupported keywords 101 func_kw = filter_keywords(func, **kw) if not nopretty else {} 102 error_msg = None 103 try: 104 func(*_args, **func_kw) 105 except Exception as e: 106 error_msg = e 107 if error_msg is not None: 108 error(error_msg)
Pretty print an object according to the configured ANSI and UNICODE settings. If detect_password is True (default), search and replace passwords with '*' characters. Does not mutate objects.
78def pprint_df(df: Any, max_rows: int | None = None) -> None: 79 """ 80 Print a DataFrame in full (no column truncation), Markdown-formatted. 81 82 See `format_dataframe` for details. 83 """ 84 print(format_dataframe(df, max_rows=max_rows))
Print a DataFrame in full (no column truncation), Markdown-formatted.
See format_dataframe for details.
17def pprint_pipes(pipes: PipesDict) -> None: 18 """Print a stylized tree of a Pipes dictionary. 19 Supports ANSI and UNICODE global settings.""" 20 from meerschaum.utils.warnings import error 21 from meerschaum.utils.packages import attempt_import, import_rich 22 from meerschaum.utils.misc import sorted_dict, replace_pipes_in_dict 23 from meerschaum.utils.formatting import UNICODE, ANSI, CHARSET, pprint, colored, get_console 24 import copy 25 rich = import_rich('rich', warn=False) 26 Text = None 27 if rich is not None: 28 rich_text = attempt_import('rich.text', lazy=False) 29 Text = rich_text.Text 30 31 icons = get_config('formatting', 'pipes', CHARSET, 'icons') 32 styles = get_config('formatting', 'pipes', 'ansi', 'styles') 33 if not ANSI: 34 styles = {k: '' for k in styles} 35 print() 36 37 def ascii_print_pipes(): 38 """Print the dictionary with no unicode allowed. Also works in case rich fails to import 39 (though rich should auto-install when `attempt_import()` is called).""" 40 asciitree = attempt_import('asciitree') 41 ascii_dict, replace_dict = {}, {'connector': {}, 'metric': {}, 'location': {}} 42 for conn_keys, metrics in pipes.items(): 43 _colored_conn_key = colored(icons['connector'] + conn_keys, style=styles['connector']) 44 if Text is not None: 45 replace_dict['connector'][_colored_conn_key] = ( 46 Text(conn_keys, style=styles['connector']) 47 ) 48 ascii_dict[_colored_conn_key] = {} 49 for metric, locations in metrics.items(): 50 _colored_metric_key = colored(icons['metric'] + metric, style=styles['metric']) 51 if Text is not None: 52 replace_dict['metric'][_colored_metric_key] = ( 53 Text(metric, style=styles['metric']) 54 ) 55 ascii_dict[_colored_conn_key][_colored_metric_key] = {} 56 for location, pipe in locations.items(): 57 _location_style = styles[('none' if location is None else 'location')] 58 pipe_addendum = '\n ' + pipe.__repr__() + '\n' 59 _colored_location = colored( 60 icons['location'] + str(location), style=_location_style 61 ) 62 _colored_location_key = _colored_location + pipe_addendum 63 if Text is not None: 64 replace_dict['location'][_colored_location] = ( 65 Text(str(location), style=_location_style) 66 ) 67 ascii_dict[_colored_conn_key][_colored_metric_key][_colored_location_key] = {} 68 69 tree = asciitree.LeftAligned() 70 output = '' 71 cols = [] 72 73 ### This is pretty terrible, unreadable code. 74 ### Please know that I'm normally better than this. 75 key_str = ( 76 (Text(" ") if Text is not None else " ") + 77 ( 78 Text("Key", style='underline') if Text is not None else 79 colored("Key", style='underline') 80 ) + (Text('\n\n ') if Text is not None else '\n\n ') + 81 ( 82 Text("Connector", style=styles['connector']) if Text is not None else 83 colored("Connector", style=styles['connector']) 84 ) + (Text('\n +-- ') if Text is not None else '\n +-- ') + 85 ( 86 Text("Metric", style=styles['metric']) if Text is not None else 87 colored("Metric", style=styles['metric']) 88 ) + (Text('\n +-- ') if Text is not None else '\n +-- ') + 89 ( 90 Text("Location", style=styles['location']) if Text is not None else 91 colored("Location", style=styles['location']) 92 ) + (Text('\n\n') if Text is not None else '\n\n') 93 ) 94 95 output += str(key_str) 96 cols.append(key_str) 97 98 def replace_tree_text(tree_str : str) -> Text: 99 """Replace the colored words with stylized Text instead. 100 Is not executed if ANSI and UNICODE are disabled.""" 101 tree_text = Text(tree_str) if Text is not None else None 102 for k, v in replace_dict.items(): 103 for _colored, _text in v.items(): 104 parts = [] 105 lines = tree_text.split(_colored) 106 for part in lines: 107 parts += [part, _text] 108 if lines[-1] != Text(''): 109 parts = parts[:-1] 110 _tree_text = Text('') 111 for part in parts: 112 _tree_text += part 113 tree_text = _tree_text 114 return tree_text 115 116 tree_output = "" 117 for k, v in ascii_dict.items(): 118 branch = {k : v} 119 tree_output += tree(branch) + '\n\n' 120 if not UNICODE and not ANSI: 121 _col = (Text(tree(branch)) if Text is not None else tree(branch)) 122 else: 123 _col = replace_tree_text(tree(branch)) 124 cols.append(_col) 125 if len(output) > 0: 126 tree_output = tree_output[:-2] 127 output += tree_output 128 129 if rich is None: 130 return print(output) 131 132 rich_columns = attempt_import('rich.columns') 133 Columns = rich_columns.Columns 134 columns = Columns(cols) 135 get_console().print(columns) 136 137 if not UNICODE: 138 return ascii_print_pipes() 139 140 rich_panel, rich_tree, rich_text, rich_columns, rich_table = attempt_import( 141 'rich.panel', 142 'rich.tree', 143 'rich.text', 144 'rich.columns', 145 'rich.table', 146 ) 147 from rich import box 148 Panel = rich_panel.Panel 149 Tree = rich_tree.Tree 150 Text = rich_text.Text 151 Columns = rich_columns.Columns 152 Table = rich_table.Table 153 154 key_panel = Panel( 155 ( 156 Text("\n") + 157 Text(icons['connector'] + "Connector", style=styles['connector']) + Text("\n\n") + 158 Text(icons['metric'] + "Metric", style=styles['metric']) + Text("\n\n") + 159 Text(icons['location'] + "Location", style=styles['location']) + Text("\n") 160 ), 161 title = Text(icons['key'] + "Keys", style=styles['guide']), 162 border_style = styles['guide'], 163 expand = True 164 ) 165 166 cols = [] 167 conn_trees = {} 168 metric_trees = {} 169 pipes = sorted_dict(pipes) 170 for conn_keys, metrics in pipes.items(): 171 conn_trees[conn_keys] = Tree( 172 Text( 173 icons['connector'] + conn_keys, 174 style = styles['connector'], 175 ), 176 guide_style = styles['connector'] 177 ) 178 metric_trees[conn_keys] = {} 179 for metric, locations in metrics.items(): 180 metric_trees[conn_keys][metric] = Tree( 181 Text( 182 icons['metric'] + metric, 183 style = styles['metric'] 184 ), 185 guide_style = styles['metric'] 186 ) 187 conn_trees[conn_keys].add(metric_trees[conn_keys][metric]) 188 for location, pipe in locations.items(): 189 _location = ( 190 Text(str(location), style=styles['none']) if location is None 191 else Text(location, style=styles['location']) 192 ) 193 _location = ( 194 Text(icons['location']) 195 + _location + Text('\n') 196 + pipe_repr(pipe, as_rich_text=True) + Text('\n') 197 ) 198 metric_trees[conn_keys][metric].add(_location) 199 200 cols += [key_panel] 201 for k, t in conn_trees.items(): 202 cols.append(t) 203 204 columns = Columns(cols) 205 get_console().print(columns)
Print a stylized tree of a Pipes dictionary. Supports ANSI and UNICODE global settings.
381def print_options( 382 options: Optional[Iterable[Any]] = None, 383 nopretty: bool = False, 384 no_rich: bool = False, 385 name: str = 'options', 386 header: Optional[str] = None, 387 num_cols: Optional[int] = None, 388 adjust_cols: bool = True, 389 sort_options: bool = False, 390 number_options: bool = False, 391 **kw 392) -> None: 393 """ 394 Print items in an iterable as a fancy table. 395 396 Parameters 397 ---------- 398 options: Optional[Dict[str, Any]], default None 399 The iterable to be printed. 400 401 nopretty: bool, default False 402 If `True`, don't use fancy formatting. 403 404 no_rich: bool, default False 405 If `True`, don't use `rich` to format the output. 406 407 name: str, default 'options' 408 The text in the default header after `'Available'`. 409 410 header: Optional[str], default None 411 If provided, override `name` and use this as the header text. 412 413 num_cols: Optional[int], default None 414 How many columns in the table. Depends on the terminal size. If `None`, use 8. 415 416 adjust_cols: bool, default True 417 If `True`, adjust the number of columns depending on the terminal size. 418 419 sort_options: bool, default False 420 If `True`, print the options in sorted order. 421 422 number_options: bool, default False 423 If `True`, print the option's number in the list (1 index). 424 425 """ 426 from meerschaum.utils.packages import import_rich 427 from meerschaum.utils.formatting import highlight_pipes 428 from meerschaum.utils.misc import get_cols_lines, string_width 429 430 if options is None: 431 options = {} 432 _options = [] 433 for o in options: 434 _options.append(str(o)) 435 if sort_options: 436 _options = sorted(_options) 437 _header = f"\nAvailable {name}" if header is None else header 438 439 if num_cols is None: 440 num_cols = 8 441 442 def _print_options_no_rich(): 443 if not nopretty: 444 print() 445 print(make_header(_header)) 446 ### print actions 447 for i, option in enumerate(_options): 448 marker = '-' if not number_options else (str(i + 1) + '.') 449 if not nopretty: 450 print(f" {marker} ", end="") 451 print(option) 452 if not nopretty: 453 print() 454 455 rich = import_rich() 456 if rich is None or nopretty or no_rich: 457 _print_options_no_rich() 458 return None 459 460 ### Prevent too many options from being truncated on small terminals. 461 if adjust_cols and _options: 462 _cols, _lines = get_cols_lines() 463 while num_cols > 1: 464 cell_len = int(((_cols - 4) - (3 * (num_cols - 1))) / num_cols) 465 num_too_big = sum([(1 if string_width(o) > cell_len else 0) for o in _options]) 466 if num_too_big > int(len(_options) / 3): 467 num_cols -= 1 468 continue 469 break 470 471 from meerschaum.utils.packages import attempt_import 472 rich_table = attempt_import('rich.table') 473 Text = attempt_import('rich.text').Text 474 box = attempt_import('rich.box') 475 Table = rich_table.Table 476 477 if _header is not None: 478 table = Table( 479 title=_header, 480 box=box.SIMPLE, 481 show_header=False, 482 show_footer=False, 483 title_style='', 484 expand = True, 485 ) 486 else: 487 table = Table.grid(padding=(0, 2)) 488 for i in range(num_cols): 489 table.add_column() 490 491 if len(_options) < 12: 492 ### If fewer than 12 items, use a single column 493 for i, option in enumerate(_options): 494 item = highlight_pipes(option) 495 if number_options: 496 item = str(i + 1) + '. ' + item 497 table.add_row(Text.from_ansi(item)) 498 else: 499 ### Otherwise, use multiple columns as before 500 num_rows = (len(_options) + num_cols - 1) // num_cols 501 item_ix = 0 502 for i in range(num_rows): 503 row = [] 504 for j in range(num_cols): 505 index = i + j * num_rows 506 if index < len(_options): 507 item = highlight_pipes(_options[index]) 508 if number_options: 509 item = str(i + 1) + '. ' + item 510 row.append(Text.from_ansi(item)) 511 item_ix += 1 512 else: 513 row.append('') 514 table.add_row(*row) 515 516 get_console().print(table) 517 return None
Print items in an iterable as a fancy table.
Parameters
- options (Optional[Dict[str, Any]], default None): The iterable to be printed.
- nopretty (bool, default False):
If
True, don't use fancy formatting. - no_rich (bool, default False):
If
True, don't userichto format the output. - name (str, default 'options'):
The text in the default header after
'Available'. - header (Optional[str], default None):
If provided, override
nameand use this as the header text. - num_cols (Optional[int], default None):
How many columns in the table. Depends on the terminal size. If
None, use 8. - adjust_cols (bool, default True):
If
True, adjust the number of columns depending on the terminal size. - sort_options (bool, default False):
If
True, print the options in sorted order. - number_options (bool, default False):
If
True, print the option's number in the list (1 index).
433def print_pipes_results( 434 pipes_results: Dict[mrsm.Pipe, SuccessTuple], 435 success_header: Optional[str] = 'Successes', 436 failure_header: Optional[str] = 'Failures', 437 nopretty: bool = False, 438 **kwargs: Any 439 ) -> None: 440 """ 441 Print the pipes and their result SuccessTuples. 442 443 Parameters 444 ---------- 445 pipes_results: Dict[mrsm.Pipe, SuccessTuple] 446 A dictionary mapping pipes to their resulting SuccessTuples. 447 448 success_header: Optional[str], default 'Successes' 449 The header to print above the successful pipes. 450 451 failure_header: Optional[str], default 'Fails' 452 The header to print above the failed pipes. 453 454 kwargs: Any 455 All other keyword arguments are passed to `meerschaum.utils.misc.print_options`. 456 """ 457 from meerschaum.utils.misc import print_options 458 successes = [pipe for pipe, (success, msg) in pipes_results.items() if success] 459 fails = [pipe for pipe, (success, msg) in pipes_results.items() if success] 460 success_options = [ 461 format_pipe_success_tuple(pipe, success_tuple, nopretty=nopretty) 462 for pipe, success_tuple in pipes_results.items() 463 if success_tuple[0] 464 ] 465 failure_options = [ 466 format_pipe_success_tuple(pipe, success_tuple, nopretty=nopretty) 467 for pipe, success_tuple in pipes_results.items() 468 if not success_tuple[0] 469 ] 470 471 if success_options: 472 print_options( 473 success_options, 474 header = success_header, 475 nopretty = nopretty, 476 **kwargs 477 ) 478 if failure_options: 479 print_options( 480 failure_options, 481 header = failure_header, 482 nopretty = nopretty, 483 **kwargs 484 )
Print the pipes and their result SuccessTuples.
Parameters
- pipes_results (Dict[mrsm.Pipe, SuccessTuple]): A dictionary mapping pipes to their resulting SuccessTuples.
- success_header (Optional[str], default 'Successes'): The header to print above the successful pipes.
- failure_header (Optional[str], default 'Fails'): The header to print above the failed pipes.
- kwargs (Any):
All other keyword arguments are passed to
meerschaum.utils.misc.print_options.
270def print_tuple( 271 tup: mrsm.SuccessTuple, 272 skip_common: bool = True, 273 common_only: bool = False, 274 upper_padding: int = 1, 275 lower_padding: int = 1, 276 left_padding: int = 1, 277 calm: bool = False, 278 _progress: Optional['rich.progress.Progress'] = None, 279) -> None: 280 """ 281 Format `meerschaum.utils.typing.SuccessTuple`. 282 283 Parameters 284 ---------- 285 skip_common: bool, default True 286 If `True`, do not print common success tuples (i.e. `(True, "Success")`). 287 288 common_only: bool, default False 289 If `True`, only print if the success tuple is common. 290 291 upper_padding: int, default 0 292 How many newlines to prepend to the message. 293 294 lower_padding: int, default 0 295 How many newlines to append to the message. 296 297 left_padding: int, default 1 298 How mant spaces to preprend to the message. 299 300 calm: bool, default False 301 If `True`, use the default emoji and color scheme. 302 303 """ 304 from meerschaum._internal.static import STATIC_CONFIG 305 do_print = True 306 307 omit_messages = STATIC_CONFIG['system']['success']['ignore'] 308 309 if common_only: 310 skip_common = False 311 do_print = tup[1].strip() in omit_messages 312 313 if skip_common: 314 do_print = tup[1].strip() not in omit_messages 315 316 if not do_print: 317 return 318 319 print(format_success_tuple( 320 tup, 321 upper_padding=upper_padding, 322 lower_padding=lower_padding, 323 calm=calm, 324 _progress=_progress, 325 ))
Format meerschaum.utils.typing.SuccessTuple.
Parameters
- skip_common (bool, default True):
If
True, do not print common success tuples (i.e.(True, "Success")). - common_only (bool, default False):
If
True, only print if the success tuple is common. - upper_padding (int, default 0): How many newlines to prepend to the message.
- lower_padding (int, default 0): How many newlines to append to the message.
- left_padding (int, default 1): How mant spaces to preprend to the message.
- calm (bool, default False):
If
True, use the default emoji and color scheme.
109def translate_rich_to_termcolor(*colors) -> tuple: 110 """Translate between rich and more_termcolor terminology.""" 111 _colors = [] 112 for c in colors: 113 _c_list = [] 114 ### handle 'bright' 115 c = c.replace('bright_', 'bright ') 116 117 ### handle 'on' 118 if ' on ' in c: 119 _on = c.split(' on ') 120 _colors.append(_on[0]) 121 for _c in _on[1:]: 122 _c_list.append('on ' + _c) 123 else: 124 _c_list += [c] 125 126 _colors += _c_list 127 128 return tuple(_colors)
Translate between rich and more_termcolor terminology.