# Python : Rich > [!NOTE] > Can be added to REPL with: `from rich import pretty; pretty.install()`, or `%load_ext rich` for IPython. ## Overview #### CLI ```bash $ python -m rich.box # demo of box styles $ python -m rich.emoji # demo of emojis $ python -m rich.layout # demo of layouts $ python -m rich.live # demo of live display $ python -m rich.markup # demo of console markup $ python -m rich.pretty # demo of pretty-printed data structures $ python -m rich.progress # demo of progress displays $ python -m rich.prompt # interactive (annoying) demo of prompts $ python -m rich.repr # demo of Repr protocol $ python -m rich.spinner # demo of spinner styles $ python -m rich.status # demo of status widgets? $ python -m rich.table # demo of tables $ python -m rich.tree # demo of Tree class $ python -m rich.json FILE # pretty-print JSON $ python -m rich.markdown FILE # render markdown $ python -m rich.syntax FILE # render with syntax highlighting ``` #### Imports ```python from rich import inspect, print, print_json from rich.box import ( ASCII, ASCII2, ASCII_DOUBLE_HEAD, DOUBLE, DOUBLE_EDUGE, HEAVY, HEAVY_EDGE, HEAVY_HEAD, HORIZONTALS, MARKDOWN, MINIMAL, MINIMAL_DOUBLE_HEAD, MINIMAL_HEAVY_HEAD, ROUNDED, SIMPLE, SIMPLE_HEAD, SIMPLE_HEAVY, SQUARE, SQUQARE_DOUBLE_HEAD, ) from rich.color import Color from rich.columns import Columns from rich.console import Console, ConsoleOptions, Group, RenderResult from rich.json import JSON from rich.layout import Layout from rich.live import Live from rich.logging import RichHandler from rich.markdown import Markdown from rich.markup import escape from rich.padding import Padding from rich.panel import Panel from rich.pretty import Pretty, pprint from rich.prompt import Confirm, FloatPrompt, IntPrompt, Prompt from rich.style import Style from rich.syntax import Syntax from rich.table import Column, Table from rich.text import Text from rich.tree import Tree from rich.theme import Theme ``` #### Printing - `rich.print` takes the same args as built-in `print`, but will do basic syntax highlighting and data structure formatting. - Strings may contain **Console Markup** to add styles and colors. - Non-strings will be converted using `str()` unless they support Rich's "Console Protocol" by implementing `__rich__()`, which should return a string or renderable object (like Text or Table). ```python print( "[italic red]Hello[/italic red] World" ) ``` ## Console The console object handles the mechanics of generating ANSI escape sequences for color and style. It will auto-detect the capabilities of the terminal and convert colors if necessary. Most apps will require one, but it's safe to create multiple. When Rich detects it is not writing to a terminal, it will... - strip control codes from output - remove animations (progress bars, status indicators, etc) ```python c = Console() highlight = False markup = False # to disable markup rendering stderr = True # write to sys.stderr instead of sys.stdout file = fileobj # write to fileobj instead of sys.stdout record = True # save copy of everything you print() or log(), then # use export_text|svg|html() to return string, or # use save_text|svg|html() to save to disk c.color_system -> 'truecolor' c.encoding -> 'utf-8' c.is_terminal -> True c.size -> ConsoleDimensions( width=142, height=58 ) c.style -> None # applied to everything # console.print() works like rich.print(), but takes additional args c.print( "[italic red]Hello[/italic red] World" ) c.print( "goodybe", style="white on blue" ) c.print_json( ... ) c.log( ... ) # prints timestamp on left, file/line on right log_locals=True # include table of local variables c.log( JSON( ... ) ) # logging JSON # for print() & log() highlight = False justify = “default|left|right|center|full” # for print() markup = False overflow = “fold|crop|ellipsis|ignore” c.rule( "TITLE" # print horizontal rule with optional title style="..." # applied to line align="left|center|right" # applied to title ) with c.status( "working..." ): # display message with spinner, then erase ... # spinner=SPINNER c.input( ... ) # like built-in input(), but with rich prompts with c.capture() as capture: # capture output instead of printing to console c.print( ... ) capture.get() -> str # retrieve captured output ``` **Color Systems** - `None` — Disables color entirely. - `"auto"` — Will auto-detect the color system. - `"standard"` — Can display 8 colors, with normal and bright variations, for 16 colors in total. - `"256"` — Can display the 16 colors from “standard” plus a fixed palette of 240 colors. - `"truecolor"` — Can display 16.7 million colors, which is likely all the colors your monitor can display. - `"windows"` — Can display 8 colors in legacy Windows terminal. New Windows terminal can display “truecolor”. ## Styles A style definition is a string containing one or more words to set colors and attributes. **Foreground** To specify a foreground color use one of the 256 [Standard Colors](https://rich.readthedocs.io/en/latest/appendix/colors.html#appendix-colors). ```python # all methods below produce the same result c.print( "Hello", style="magenta" ) c.print( "Hello", style="color(5)" ) c.print( "Hello", style="#af00ff" ) # hex & rgb allow you to select c.print( "Hello", style="rgb(175,0,255)" ) # from full truecolor set (16.7m) ``` **Background** Same, but precede color with "on ": ```python c.print( "Hello", style="on blue" ) c.print( "Hello", style="red on blue" ) # ewww ``` **Default** In either case (foreground or background), return to default value with "default": ```python c.print( "Hello", style="default on blue" ) c.print( "Hello", style="red on default" ) ``` **More Styles** - `"bold"` or `"b"` — bold text - `"blink"` — text that flashes - `"blink2"` — text that flashes rapidly *(not supported by most terminals)* - `"conceal"` — _concealed_ text *(not supported by most terminals)* - `"italic"` or `"i"` — italic text *(not supported on Windows)* - `"reverse"` or `"r"` — text with foreground & background colors reversed - `"strike"` or `"s"` — text with a line through it - `"underline"` or `"u"` — underlined text - `"not STYLE"` — negate previously-enabled style **Link Style** ```python c.print( "Google", style="link https://google.com" ) ``` #### Style Class Styles are parsed and turned into instances of the `Style` class. ```python danger_style = Style( color="red", blink=True, bold=True ) danger_style = Style.parse( "blink bold red" ) c.print( "Danger, Will Robinson!", style=danger_style ) ``` Styles can be combined. ```python c.print( "foo", style=style1 + style2 ) ``` #### Themes Lets you add style definitions to the console which can be referred to by name. ```python custom_theme = Theme({ "info" : "dim cyan", "warning" : "magenta", }) c = Console( theme=custom_theme ) c.print( "This is information", style="info" ) c.print( "[warning]The pod bay doors are locked[/warning]" ) ``` > [!NOTE] > Style names must be lower case, start with a letter, and only contain letters or the characters `"."`, `"-"`, `"_"`. **More** - [Customizing Defaults](https://rich.readthedocs.io/en/latest/style.html#customizing-defaults) - [Loading Theme From File](https://rich.readthedocs.io/en/latest/style.html#loading-themes) ## Markup You can convert a string to styled text with `Text.from_markup( "..." )`. **Styles** ```python "[STYLES]text[/STYLES] more text" # open and close "[STYLES]text more text" # leave open to style rest of string "[bold]Bold[italic] bold and italic [/bold]italic[/italic]" # overlapping "Visit my [link=https://www.willmcgugan.com]blog[/link]!" # links escape( "..." ) # escape "[" and "\" before adding styles ``` **Emojis** - Running `print( ":warning:" )` produces: ⚠️ - Some emojis have two variants — "emoji" (full color) and "text" (monochrome). ```python ":red_heart-emoji:" vs ":red_heart-text:" ``` ## Rich Text Like a string with marked up regions of text. Unlike a built-in `str`, a Text instance is mutable, and most methods operate in-place rather than returning a new instance. ```python text = Text( "Hello, World!" ) text.stylize( "bold magenta", 0, 6 ) # character offsets text.append( "\nGoodbye", style="bold" ) c.print( text ) # combine strings or pairs of string+style text = Text.assemble( ( "Hello", "bold magenta" ), "World!" ) text.highlight_words( ... ) text.highlight_regex( ... ) Text( "...", justify = "left|center|right|full", overflow = "fold|crop|ellipsis", no_wrap = <bool>, tax_size = <int>, ) ``` ## Highlighting Rich will automatically highlight patterns in text, such as numbers, strings, collections, booleans, None, and a few more exotic patterns such as file paths, URLs and UUIDs. [Custom Highlighters](https://rich.readthedocs.io/en/latest/highlighting.html#custom-highlighters) ## Pretty Printing Use `pprint()`. [(details)](https://rich.readthedocs.io/en/latest/pretty.html#pprint-method) Rich offers a `Pretty` class which you can use to insert pretty printed data in to another renderable. [(details)](https://rich.readthedocs.io/en/latest/reference/pretty.html#rich.pretty.Pretty) ```python print( Panel( Pretty( data ) ) ) ``` You can implement `__rich_repr__` to help Rich pretty-print your objects. [(details)](https://rich.readthedocs.io/en/latest/pretty.html#rich-repr-protocol) ## Logging Handler Rich supplies `RichHandler` which will format and colorize text written by Python’s logging module. Also, that class may be configured to use Rich’s `Traceback` class to format exceptions, which provides more context than a built-in exception. ( [logging](https://rich.readthedocs.io/en/latest/logging.html) | [tracebacks](https://rich.readthedocs.io/en/latest/traceback.html) ) ## Prompt ```python name = Prompt.ask( "Enter your name ") name = Prompt.ask( "Enter your name", default="Paul Atreides") name = Prompt.ask( "Enter your name", choices=[ "Paul", "Jessica", "Duncan" ], default="Paul" ) is_rich_great = Confirm.ask( "Do you like rich?" ) ``` ## Columns Prints items from left to right, wrapping, in neat columns. ```python c.print( Columns( iterable, **options ) ) ``` ## Render Groups Combine multiple renderables into one. ```python p = Panel( Group( Panel( ... ), Panel( ... ), ) ) ``` ## Markdown ```python c.print( Markdown( "..." ) ) ``` ## Padding ```python # add 1 space on both sides, and an empty line above & below c.print( Padding( "Hello", 1 ) ) Padding( "Hello", ( 2, 4 ) ) # 2 lines, 4 spaces style="..." # apply to padding expand=False # don't expand to terminal width ``` ## Panel Draws a border around another renderable. Will extend to the full width of the terminal. ```python Panel( "..." title = "...", subtitle = "...", box = rich.box.*, expand = False, # make panel fit context instead of expanding ) Panel.fit( "..." ) # same as Panel( expand=False ) ``` ## Progress Display > [!NOTE] > Built on "Live Display" feature / `Live` class. The default will display a description of the ‘task’, a progress bar, percentage complete, and estimated time remaining. TODO ## Syntax re: syntax highlighting TODO ## Tables TODO ## Tree TODO ## Live Display To create a live display, construct a `Live` object with a renderable and use it as a context manager. The live display will persist for the duration of the context. You can update the renderable to update the display. ```python with Live( renderable ) as live: # refreshs 4/s by default ... live.update( new_renderable ) # change contents live.stop() Options: screen = True # go full screen transient = True # erase renderable on exit refresh_per_second = 4 # set to False to disable refresh console = obj # set custom console object ``` The `Live` class will create an internal `Console` object which you can access via `live.console`. If you print or log to this console, the output will be displayed _above_ the live display. To avoid breaking the live display visuals, Rich will redirect `stdout` and `stderr` so that you can use the builtin `print` statement. This feature is enabled by default, but you can disable by setting `redirect_stdout` or `redirect_stderr` to `False`. Only one Live Display at a time. ## Layout Divides screen into boxes. Can be used with Live Display to create full-screen apps, or just used by itself. ```python layout = Layout() layout.split_column( Layout( name="NAME" ), ... ) # create a column of boxes layout.split_row( Layout( name="NAME" ), ... ) # create a row of boxes layout[ "NAME" ] # access box layout[ "NAME" ].split... # subdivide box further layout[ "NAME" ].update( "...content..." ) # replace content layout[ "NAME" ].size = <int> # unit = rows or chars, depending on orientation layout[ "NAME" ].ratio = <int> # default = 1; sets size relative to the other boxes layout[ "NAME" ].visible = <bool> # default = 1; sets size relative to the other boxes c.print( layout ) c.print( layout.tree ) # display a summary of the layout as a tree ``` ## Console Render TODO