# 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