This page documents the Live class in rich/live.py, which provides auto-refreshing, in-place terminal output. It covers the context manager lifecycle, the background refresh thread, cursor management via LiveRender, stdout/stderr redirection via FileProxy, screen mode, vertical overflow handling, and nesting behavior.
For the higher-level Status spinner built on top of Live, see Status and Spinners. For the Progress bar system, which also uses Live internally, see Progress Bars.
Live renders a single renderable (or a dynamically supplied one) in a fixed region of the terminal. Each refresh erases the previous render using cursor control sequences and redraws it in place, giving the appearance of animation. The class is the foundation for Progress, Status, and any custom live display.
User code → live.update() → Live._renderable
↓
_RefreshThread → live.refresh()
↓
LiveRender (__rich_console__)
↓
Console (RenderHook chain)
↓
Terminal output
Sources: rich/live.py1-96 rich/live_render.py1-117
Class hierarchy and component relationships:
Sources: rich/live.py41-97 rich/live_render.py13-30 rich/file_proxy.py11-55
| Parameter | Type | Default | Description |
|---|---|---|---|
renderable | RenderableType | None | Initial renderable to display |
console | Console | global console | Console to render into |
screen | bool | False | Enable alternate screen mode |
auto_refresh | bool | True | Start background refresh thread |
refresh_per_second | float | 4 | Refresh rate when auto_refresh=True |
transient | bool | False | Clear display on exit |
redirect_stdout | bool | True | Redirect sys.stdout through FileProxy |
redirect_stderr | bool | True | Redirect sys.stderr through FileProxy |
vertical_overflow | VerticalOverflowMethod | "ellipsis" | How to handle content taller than terminal |
get_renderable | Callable[[], RenderableType] | None | Callable alternative to a fixed renderable |
When screen=True, transient is forced to True regardless of the argument provided.
Sources: rich/live.py57-96
Live is designed to be used as a context manager. __enter__ calls start() and __exit__ calls stop().
Lifecycle of a Live session:
Sources: rich/live.py111-181
start()console.set_live(self) to register the instance. If another Live is active, _nested is set to True and the instance defers to the outer one.console.set_alt_screen(True)).console.show_cursor(False))._enable_redirect_io() to wrap sys.stdout and sys.stderr.RenderHook via console.push_render_hook(self).auto_refresh=True, creates and starts a _RefreshThread.stop()_RefreshThread if running.vertical_overflow = "visible" so the final frame renders in full.refresh() (unless in alternate screen or Jupyter)._disable_redirect_io() to restore sys.stdout and sys.stderr.console.pop_render_hook()).transient=True, calls console.control(self._live_render.restore_cursor()) to erase the output._RefreshThreadWhen auto_refresh=True (the default), a _RefreshThread is started. It is a daemon thread that sleeps for 1 / refresh_per_second seconds between each call to live.refresh(). It uses a threading.Event (done) to stop cleanly on stop().
The thread holds live._lock (an RLock) while calling refresh() to prevent concurrent modification of the renderable.
When auto_refresh=False, you must call live.refresh() manually, or pass refresh=True to live.update().
Sources: rich/live.py22-38 rich/live.py84-96
Live implements the RenderHook interface defined in rich/console.py. Specifically, it implements process_renderables().
How Live integrates into the Console render pipeline:
The key method is process_renderables() rich/live.py278-297 When the console is interactive, it:
Control that moves the cursor back to the top of the live display area (LiveRender.position_cursor()).LiveRender object itself, which redraws the current renderable.This means any console.print() call that happens while Live is active will scroll the live display down, print above it, and then redraw it below the printed content.
Sources: rich/live.py278-297 rich/live_render.py51-70
LiveRender: Cursor ManagementLiveRender is the renderable object that Live uses to track and repaint the live area. It stores the shape (width and height in characters) of the most recently rendered output in _shape.
| Method | Returns | Description |
|---|---|---|
set_renderable(renderable) | None | Replace the stored renderable |
position_cursor() | Control | ANSI codes to move cursor to top-left of live area |
restore_cursor() | Control | ANSI codes to erase live area and restore cursor position |
last_render_height | int | Number of lines in the last rendered output |
Cursor positioning sequence (for a 3-line render):
\r ← carriage return to start of line
\x1b<FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/2K ← erase current line\n\\x1b[1A ← cursor up 1\n\\x1b[2K ← erase current line\n\\x1b[1A ← cursor up 1\n\\x1b[2K ← erase current line\n```\n\nThe `position_cursor()` method issues this sequence, and then `Live.process_renderables()` appends a fresh render of the content.\n\nSources#LNaN-LNaN" NaN file-path="2K ← erase current line\n\\x1b[1A ← cursor up 1\n\\x1b[2K ← erase current line\n\\x1b[1A ← cursor up 1\n\\x1b[2K ← erase current line\n```\n\nThe `position_cursor()` method issues this sequence, and then `Live.process_renderables()` appends a fresh render of the content.\n\nSources">Hii</FileRef>
---
## Vertical Overflow
When the renderable is taller than the terminal, `LiveRender.__rich_console__` <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live_render.py#L86-L116" min=86 max=116 file-path="rich/live_render.py">Hii</FileRef> applies one of three strategies, set via the `vertical_overflow` parameter:
| Value | Behavior |
|---|---|
| `"ellipsis"` | Show `terminal_height - 1` lines, then a centered `...` row (default) |
| `"crop"` | Show only the first `terminal_height` lines; rest is hidden |
| `"visible"` | Render all lines; the display cannot be cleanly erased in this mode |
When `stop()` is called, `vertical_overflow` is temporarily set to `"visible"` so the final frame always renders completely.
Sources: <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live_render.py#L94-L109" min=94 max=109 file-path="rich/live_render.py">Hii</FileRef> <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L161-L161" min=161 file-path="rich/live.py">Hii</FileRef>
---
## Transient vs. Persistent Display
| Mode | On exit behavior |
|---|---|
| `transient=False` (default) | Last rendered frame remains in the terminal; cursor moves to the line below it |
| `transient=True` | `LiveRender.restore_cursor()` is called, erasing all lines of the display |
| `screen=True` | Forces `transient=True`; alternate screen buffer is restored, original terminal view returns |
Sources: <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L86-L86" min=86 file-path="rich/live.py">Hii</FileRef> <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L178-L179" min=178 max=179 file-path="rich/live.py">Hii</FileRef>
---
## Screen Mode
When `screen=True`, `Live` activates the terminal's alternate screen buffer via `console.set_alt_screen(True)`, which emits `\x1b<FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/?1049h`. On `stop()`, it emits `\\x1b[?1049l` to return to the primary screen.\n\nIn screen mode#LNaN-LNaN" NaN file-path="?1049h`. On `stop()`, it emits `\\x1b[?1049l` to return to the primary screen.\n\nIn screen mode">Hii</FileRef> to fill the entire terminal area.
- `transient` is always `True`.
This is the intended mechanism for building full-screen terminal applications, often combined with the `Layout` class.
Sources: <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L75-L76" min=75 max=76 file-path="rich/live.py">Hii</FileRef> <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L126-L128" min=126 max=128 file-path="rich/live.py">Hii</FileRef> <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L176-L179" min=176 max=179 file-path="rich/live.py">Hii</FileRef> <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L228-L228" min=228 file-path="rich/live.py">Hii</FileRef>
---
## stdout/stderr Redirection
<FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/live.py#L195-L212" min=195 max=212 file-path="rich/live.py">Hii</FileRef>
When the console is a terminal or Jupyter notebook, `Live` replaces `sys.stdout` and `sys.stderr` with `FileProxy` instances on `start()` and restores them on `stop()`.
`FileProxy` <FileRef file-url="https://github.com/Textualize/rich/blob/fc41075a/rich/file_proxy.py#L11-L55" min=11 max=55 file-path="rich/file_proxy.py">Hii</FileRef> extends `io.TextIOBase` and buffers writes until a newline is encountered, then routes each complete line through `console.print()`. This ensures that ordinary `print()` statements do not corrupt the live display — they become properly formatted console output that scrolls the display upward.
**Redirection flow:**
```mermaid
flowchart LR
A["print('hello')"] --> B["sys.stdout.write('hello\\n')"]
B --> C["FileProxy.write('hello\\n')"]
C --> D["AnsiDecoder.decode_line(line)"]
D --> E["console.print(Text)"]
E --> F["Live.process_renderables()"]
F --> G["Terminal: 'hello' above live display"]
Redirection only happens if sys.stdout is not already a FileProxy (guard against double-wrapping in nested Live scenarios). To disable, pass redirect_stdout=False or redirect_stderr=False to the constructor.
Sources: rich/live.py195-212 rich/file_proxy.py28-48
Live InstancesMultiple Live instances can be active simultaneously. When an inner Live is started while an outer one is already running rich/live.py122-124:
console.set_live(self) returns False, and _nested = True is set.Live is responsible for rendering the combined output. Its renderable property rich/live.py214-228 detects that it is the first entry in console._live_stack and wraps all stacked Live renderables in a Group.Live.refresh() delegates upward: it calls self.console._live_stack[0].refresh().This means nested Live displays are stacked vertically, with the outermost instance on top.
Prior to Rich v14.0.0, nested
Liveinstances raised aLiveError.
Sources: rich/live.py122-124 rich/live.py214-228 rich/live.py248-251
| Method | Signature | Description |
|---|---|---|
update() | update(renderable, *, refresh=False) | Replace the current renderable. Pass refresh=True to force an immediate repaint. |
refresh() | refresh() | Repaints the live area immediately by printing a no-op Control() to trigger process_renderables(). |
Strings passed to update() are automatically converted to Text via console.render_str() rich/live.py237-238
The get_renderable constructor parameter accepts a callable. If provided, get_renderable() is called on every refresh instead of using the stored _renderable. This allows the display to pull from a shared mutable object without requiring explicit update() calls.
Sources: rich/live.py230-276
Live extends JupyterMixin and has a dedicated Jupyter code path in refresh() rich/live.py253-268 When console.is_jupyter is True:
ipywidgets.Output widget (ipy_widget) is created and display()-ed once.refresh(), the widget's output is cleared (clear_output(wait=True)) and the current renderable is printed into it.If ipywidgets is not installed, a UserWarning is emitted.
Sources: rich/live.py253-268
When console.is_terminal is False (e.g., output is redirected to a file or pipe), the live display does not use cursor controls. Instead, process_renderables() rich/live.py292-295 only appends the LiveRender once: at the final stop() call, producing a single static snapshot of the last renderable state.
This means a file console will show only the final rendered frame, not a stream of intermediate states.
Sources: rich/live.py292-295 rich/live.py272-276
Live uses a single threading.RLock (_lock) to protect the _renderable field and calls to refresh(). Both update() and refresh() acquire _lock. The _RefreshThread also acquires _lock before calling refresh().
The RLock (reentrant lock) is used because stop() acquires the lock and then calls refresh() internally, which also tries to acquire the same lock.
Sources: rich/live.py82 rich/live.py117 rich/live.py146 rich/live.py239 rich/live.py246 rich/live.py285 rich/live.py35-38
| File | Key Symbol | Role |
|---|---|---|
rich/live.py | Live | Main class; context manager, refresh loop, render hook |
rich/live.py | _RefreshThread | Background thread for auto-refresh |
rich/live_render.py | LiveRender | Tracks render area shape; generates cursor movement codes |
rich/live_render.py | VerticalOverflowMethod | Literal type: "crop", "ellipsis", "visible" |
rich/file_proxy.py | FileProxy | Wraps sys.stdout/sys.stderr; routes writes through Console |
rich/control.py | Control | Emits ANSI control sequences (cursor movement, screen modes) |
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.