2021-04-13 04:02:29 +00:00
import inspect
2022-11-07 18:06:49 +00:00
import io
2021-04-13 04:02:29 +00:00
import os
import platform
import sys
import threading
2022-11-07 18:06:49 +00:00
import zlib
2021-04-13 04:02:29 +00:00
from abc import ABC , abstractmethod
from dataclasses import dataclass , field
from datetime import datetime
from functools import wraps
from getpass import getpass
2022-01-24 04:07:52 +00:00
from html import escape
from inspect import isclass
2021-04-13 04:02:29 +00:00
from itertools import islice
2022-11-07 18:06:49 +00:00
from math import ceil
2021-04-13 04:02:29 +00:00
from time import monotonic
2022-01-24 04:07:52 +00:00
from types import FrameType , ModuleType , TracebackType
2021-04-13 04:02:29 +00:00
from typing import (
IO ,
TYPE_CHECKING ,
Any ,
Callable ,
Dict ,
Iterable ,
List ,
Mapping ,
NamedTuple ,
Optional ,
TextIO ,
2022-01-24 04:07:52 +00:00
Tuple ,
Type ,
2021-04-13 04:02:29 +00:00
Union ,
cast ,
)
2022-11-07 18:06:49 +00:00
from rich . _null_file import NULL_FILE
2022-01-24 04:07:52 +00:00
if sys . version_info > = ( 3 , 8 ) :
from typing import Literal , Protocol , runtime_checkable
else :
from typing_extensions import (
Literal ,
Protocol ,
runtime_checkable ,
) # pragma: no cover
2021-04-13 04:02:29 +00:00
from . import errors , themes
from . _emoji_replace import _emoji_replace
2022-11-07 18:06:49 +00:00
from . _export_format import CONSOLE_HTML_FORMAT , CONSOLE_SVG_FORMAT
2021-04-13 04:02:29 +00:00
from . _log_render import FormatTimeCallable , LogRender
from . align import Align , AlignMethod
2022-11-07 18:06:49 +00:00
from . color import ColorSystem , blend_rgb
2021-04-13 04:02:29 +00:00
from . control import Control
2022-01-24 04:07:52 +00:00
from . emoji import EmojiVariant
2021-04-13 04:02:29 +00:00
from . highlighter import NullHighlighter , ReprHighlighter
from . markup import render as render_markup
from . measure import Measurement , measure_renderables
from . pager import Pager , SystemPager
2022-01-24 04:07:52 +00:00
from . pretty import Pretty , is_expandable
from . protocol import rich_cast
2021-04-13 04:02:29 +00:00
from . region import Region
from . scope import render_scope
from . screen import Screen
from . segment import Segment
from . style import Style , StyleType
from . styled import Styled
2022-11-07 18:06:49 +00:00
from . terminal_theme import DEFAULT_TERMINAL_THEME , SVG_EXPORT_THEME , TerminalTheme
2021-04-13 04:02:29 +00:00
from . text import Text , TextType
from . theme import Theme , ThemeStack
if TYPE_CHECKING :
from . _windows import WindowsConsoleFeatures
from . live import Live
from . status import Status
2022-11-07 18:06:49 +00:00
JUPYTER_DEFAULT_COLUMNS = 115
JUPYTER_DEFAULT_LINES = 100
2021-04-13 04:02:29 +00:00
WINDOWS = platform . system ( ) == " Windows "
HighlighterType = Callable [ [ Union [ str , " Text " ] ] , " Text " ]
JustifyMethod = Literal [ " default " , " left " , " center " , " right " , " full " ]
OverflowMethod = Literal [ " fold " , " crop " , " ellipsis " , " ignore " ]
class NoChange :
pass
NO_CHANGE = NoChange ( )
2022-11-07 18:06:49 +00:00
try :
_STDIN_FILENO = sys . __stdin__ . fileno ( )
except Exception :
_STDIN_FILENO = 0
try :
_STDOUT_FILENO = sys . __stdout__ . fileno ( )
except Exception :
_STDOUT_FILENO = 1
try :
_STDERR_FILENO = sys . __stderr__ . fileno ( )
except Exception :
_STDERR_FILENO = 2
_STD_STREAMS = ( _STDIN_FILENO , _STDOUT_FILENO , _STDERR_FILENO )
_STD_STREAMS_OUTPUT = ( _STDOUT_FILENO , _STDERR_FILENO )
_TERM_COLORS = {
" kitty " : ColorSystem . EIGHT_BIT ,
" 256color " : ColorSystem . EIGHT_BIT ,
" 16color " : ColorSystem . STANDARD ,
}
2021-04-13 04:02:29 +00:00
class ConsoleDimensions ( NamedTuple ) :
""" Size of the terminal. """
width : int
""" The width of the console in ' cells ' . """
height : int
""" The height of the console in lines. """
@dataclass
class ConsoleOptions :
""" Options for __rich_console__ method. """
size : ConsoleDimensions
""" Size of console. """
legacy_windows : bool
""" legacy_windows: flag for legacy windows. """
min_width : int
""" Minimum width of renderable. """
max_width : int
""" Maximum width of renderable. """
is_terminal : bool
""" True if the target is a terminal, otherwise False. """
encoding : str
""" Encoding of terminal. """
2022-01-24 04:07:52 +00:00
max_height : int
""" Height of container (starts as terminal) """
2021-04-13 04:02:29 +00:00
justify : Optional [ JustifyMethod ] = None
""" Justify value override for renderable. """
overflow : Optional [ OverflowMethod ] = None
""" Overflow value override for renderable. """
no_wrap : Optional [ bool ] = False
""" Disable wrapping for text. """
highlight : Optional [ bool ] = None
""" Highlight override for render_str. """
markup : Optional [ bool ] = None
""" Enable markup when rendering strings. """
height : Optional [ int ] = None
@property
def ascii_only ( self ) - > bool :
""" Check if renderables should use ascii only. """
return not self . encoding . startswith ( " utf " )
def copy ( self ) - > " ConsoleOptions " :
""" Return a copy of the options.
Returns :
ConsoleOptions : a copy of self .
"""
2022-01-24 04:07:52 +00:00
options : ConsoleOptions = ConsoleOptions . __new__ ( ConsoleOptions )
2021-04-13 04:02:29 +00:00
options . __dict__ = self . __dict__ . copy ( )
return options
def update (
self ,
* ,
width : Union [ int , NoChange ] = NO_CHANGE ,
min_width : Union [ int , NoChange ] = NO_CHANGE ,
max_width : Union [ int , NoChange ] = NO_CHANGE ,
justify : Union [ Optional [ JustifyMethod ] , NoChange ] = NO_CHANGE ,
overflow : Union [ Optional [ OverflowMethod ] , NoChange ] = NO_CHANGE ,
no_wrap : Union [ Optional [ bool ] , NoChange ] = NO_CHANGE ,
highlight : Union [ Optional [ bool ] , NoChange ] = NO_CHANGE ,
markup : Union [ Optional [ bool ] , NoChange ] = NO_CHANGE ,
height : Union [ Optional [ int ] , NoChange ] = NO_CHANGE ,
) - > " ConsoleOptions " :
""" Update values, return a copy. """
options = self . copy ( )
if not isinstance ( width , NoChange ) :
options . min_width = options . max_width = max ( 0 , width )
if not isinstance ( min_width , NoChange ) :
options . min_width = min_width
if not isinstance ( max_width , NoChange ) :
options . max_width = max_width
if not isinstance ( justify , NoChange ) :
options . justify = justify
if not isinstance ( overflow , NoChange ) :
options . overflow = overflow
if not isinstance ( no_wrap , NoChange ) :
options . no_wrap = no_wrap
if not isinstance ( highlight , NoChange ) :
options . highlight = highlight
if not isinstance ( markup , NoChange ) :
options . markup = markup
if not isinstance ( height , NoChange ) :
2022-01-24 04:07:52 +00:00
if height is not None :
options . max_height = height
2021-04-13 04:02:29 +00:00
options . height = None if height is None else max ( 0 , height )
return options
def update_width ( self , width : int ) - > " ConsoleOptions " :
""" Update just the width, return a copy.
Args :
width ( int ) : New width ( sets both min_width and max_width )
Returns :
~ ConsoleOptions : New console options instance .
"""
options = self . copy ( )
options . min_width = options . max_width = max ( 0 , width )
return options
2022-01-24 04:07:52 +00:00
def update_height ( self , height : int ) - > " ConsoleOptions " :
""" Update the height, and return a copy.
Args :
height ( int ) : New height
Returns :
~ ConsoleOptions : New Console options instance .
"""
options = self . copy ( )
options . max_height = options . height = height
return options
2022-11-07 18:06:49 +00:00
def reset_height ( self ) - > " ConsoleOptions " :
""" Return a copy of the options with height set to ``None``.
Returns :
~ ConsoleOptions : New console options instance .
"""
options = self . copy ( )
options . height = None
return options
2021-04-13 04:02:29 +00:00
def update_dimensions ( self , width : int , height : int ) - > " ConsoleOptions " :
""" Update the width and height, and return a copy.
Args :
width ( int ) : New width ( sets both min_width and max_width ) .
height ( int ) : New height .
Returns :
~ ConsoleOptions : New console options instance .
"""
options = self . copy ( )
options . min_width = options . max_width = max ( 0 , width )
2022-01-24 04:07:52 +00:00
options . height = options . max_height = height
2021-04-13 04:02:29 +00:00
return options
@runtime_checkable
class RichCast ( Protocol ) :
""" An object that may be ' cast ' to a console renderable. """
2022-11-07 18:06:49 +00:00
def __rich__ (
self ,
) - > Union [ " ConsoleRenderable " , " RichCast " , str ] : # pragma: no cover
2021-04-13 04:02:29 +00:00
. . .
@runtime_checkable
class ConsoleRenderable ( Protocol ) :
""" An object that supports the console protocol. """
def __rich_console__ (
self , console : " Console " , options : " ConsoleOptions "
) - > " RenderResult " : # pragma: no cover
. . .
2022-01-24 04:07:52 +00:00
# A type that may be rendered by Console.
2021-04-13 04:02:29 +00:00
RenderableType = Union [ ConsoleRenderable , RichCast , str ]
2022-01-24 04:07:52 +00:00
# The result of calling a __rich_console__ method.
2021-04-13 04:02:29 +00:00
RenderResult = Iterable [ Union [ RenderableType , Segment ] ]
_null_highlighter = NullHighlighter ( )
class CaptureError ( Exception ) :
""" An error in the Capture context manager. """
class NewLine :
""" A renderable to generate new line(s) """
def __init__ ( self , count : int = 1 ) - > None :
self . count = count
def __rich_console__ (
self , console : " Console " , options : " ConsoleOptions "
) - > Iterable [ Segment ] :
yield Segment ( " \n " * self . count )
class ScreenUpdate :
""" Render a list of lines at a given offset. """
def __init__ ( self , lines : List [ List [ Segment ] ] , x : int , y : int ) - > None :
self . _lines = lines
self . x = x
self . y = y
def __rich_console__ (
self , console : " Console " , options : ConsoleOptions
) - > RenderResult :
x = self . x
move_to = Control . move_to
for offset , line in enumerate ( self . _lines , self . y ) :
yield move_to ( x , offset )
yield from line
class Capture :
""" Context manager to capture the result of printing to the console.
See : meth : ` ~ rich . console . Console . capture ` for how to use .
Args :
console ( Console ) : A console instance to capture output .
"""
def __init__ ( self , console : " Console " ) - > None :
self . _console = console
self . _result : Optional [ str ] = None
def __enter__ ( self ) - > " Capture " :
self . _console . begin_capture ( )
return self
2022-01-24 04:07:52 +00:00
def __exit__ (
self ,
exc_type : Optional [ Type [ BaseException ] ] ,
exc_val : Optional [ BaseException ] ,
exc_tb : Optional [ TracebackType ] ,
) - > None :
2021-04-13 04:02:29 +00:00
self . _result = self . _console . end_capture ( )
def get ( self ) - > str :
""" Get the result of the capture. """
if self . _result is None :
raise CaptureError (
" Capture result is not available until context manager exits. "
)
return self . _result
class ThemeContext :
""" A context manager to use a temporary theme. See :meth:`~rich.console.Console.use_theme` for usage. """
def __init__ ( self , console : " Console " , theme : Theme , inherit : bool = True ) - > None :
self . console = console
self . theme = theme
self . inherit = inherit
def __enter__ ( self ) - > " ThemeContext " :
self . console . push_theme ( self . theme )
return self
2022-01-24 04:07:52 +00:00
def __exit__ (
self ,
exc_type : Optional [ Type [ BaseException ] ] ,
exc_val : Optional [ BaseException ] ,
exc_tb : Optional [ TracebackType ] ,
) - > None :
2021-04-13 04:02:29 +00:00
self . console . pop_theme ( )
class PagerContext :
""" A context manager that ' pages ' content. See :meth:`~rich.console.Console.pager` for usage. """
def __init__ (
self ,
console : " Console " ,
2022-01-24 04:07:52 +00:00
pager : Optional [ Pager ] = None ,
2021-04-13 04:02:29 +00:00
styles : bool = False ,
links : bool = False ,
) - > None :
self . _console = console
self . pager = SystemPager ( ) if pager is None else pager
self . styles = styles
self . links = links
def __enter__ ( self ) - > " PagerContext " :
self . _console . _enter_buffer ( )
return self
2022-01-24 04:07:52 +00:00
def __exit__ (
self ,
exc_type : Optional [ Type [ BaseException ] ] ,
exc_val : Optional [ BaseException ] ,
exc_tb : Optional [ TracebackType ] ,
) - > None :
2021-04-13 04:02:29 +00:00
if exc_type is None :
with self . _console . _lock :
buffer : List [ Segment ] = self . _console . _buffer [ : ]
del self . _console . _buffer [ : ]
segments : Iterable [ Segment ] = buffer
if not self . styles :
segments = Segment . strip_styles ( segments )
elif not self . links :
segments = Segment . strip_links ( segments )
content = self . _console . _render_buffer ( segments )
self . pager . show ( content )
self . _console . _exit_buffer ( )
class ScreenContext :
""" A context manager that enables an alternative screen. See :meth:`~rich.console.Console.screen` for usage. """
def __init__ (
self , console : " Console " , hide_cursor : bool , style : StyleType = " "
) - > None :
self . console = console
self . hide_cursor = hide_cursor
self . screen = Screen ( style = style )
self . _changed = False
2022-01-24 04:07:52 +00:00
def update (
self , * renderables : RenderableType , style : Optional [ StyleType ] = None
) - > None :
2021-04-13 04:02:29 +00:00
""" Update the screen.
Args :
renderable ( RenderableType , optional ) : Optional renderable to replace current renderable ,
or None for no change . Defaults to None .
style : ( Style , optional ) : Replacement style , or None for no change . Defaults to None .
"""
if renderables :
self . screen . renderable = (
2022-01-24 04:07:52 +00:00
Group ( * renderables ) if len ( renderables ) > 1 else renderables [ 0 ]
2021-04-13 04:02:29 +00:00
)
if style is not None :
self . screen . style = style
self . console . print ( self . screen , end = " " )
def __enter__ ( self ) - > " ScreenContext " :
self . _changed = self . console . set_alt_screen ( True )
if self . _changed and self . hide_cursor :
self . console . show_cursor ( False )
return self
2022-01-24 04:07:52 +00:00
def __exit__ (
self ,
exc_type : Optional [ Type [ BaseException ] ] ,
exc_val : Optional [ BaseException ] ,
exc_tb : Optional [ TracebackType ] ,
) - > None :
2021-04-13 04:02:29 +00:00
if self . _changed :
self . console . set_alt_screen ( False )
if self . hide_cursor :
self . console . show_cursor ( True )
2022-01-24 04:07:52 +00:00
class Group :
2021-04-13 04:02:29 +00:00
""" Takes a group of renderables and returns a renderable object that renders the group.
Args :
renderables ( Iterable [ RenderableType ] ) : An iterable of renderable objects .
fit ( bool , optional ) : Fit dimension of group to contents , or fill available space . Defaults to True .
"""
def __init__ ( self , * renderables : " RenderableType " , fit : bool = True ) - > None :
self . _renderables = renderables
self . fit = fit
self . _render : Optional [ List [ RenderableType ] ] = None
@property
def renderables ( self ) - > List [ " RenderableType " ] :
if self . _render is None :
self . _render = list ( self . _renderables )
return self . _render
def __rich_measure__ (
self , console : " Console " , options : " ConsoleOptions "
) - > " Measurement " :
if self . fit :
return measure_renderables ( console , options , self . renderables )
else :
return Measurement ( options . max_width , options . max_width )
def __rich_console__ (
self , console : " Console " , options : " ConsoleOptions "
) - > RenderResult :
yield from self . renderables
2022-01-24 04:07:52 +00:00
def group ( fit : bool = True ) - > Callable [ . . . , Callable [ . . . , Group ] ] :
2021-04-13 04:02:29 +00:00
""" A decorator that turns an iterable of renderables in to a group.
Args :
fit ( bool , optional ) : Fit dimension of group to contents , or fill available space . Defaults to True .
"""
2022-01-24 04:07:52 +00:00
def decorator (
method : Callable [ . . . , Iterable [ RenderableType ] ]
) - > Callable [ . . . , Group ] :
""" Convert a method that returns an iterable of renderables in to a Group. """
2021-04-13 04:02:29 +00:00
@wraps ( method )
2022-01-24 04:07:52 +00:00
def _replace ( * args : Any , * * kwargs : Any ) - > Group :
2021-04-13 04:02:29 +00:00
renderables = method ( * args , * * kwargs )
2022-01-24 04:07:52 +00:00
return Group ( * renderables , fit = fit )
2021-04-13 04:02:29 +00:00
return _replace
return decorator
def _is_jupyter ( ) - > bool : # pragma: no cover
""" Check if we ' re running in a Jupyter notebook. """
try :
2022-11-07 18:06:49 +00:00
get_ipython # type: ignore[name-defined]
2021-04-13 04:02:29 +00:00
except NameError :
return False
2022-11-07 18:06:49 +00:00
ipython = get_ipython ( ) # type: ignore[name-defined]
2022-01-24 04:07:52 +00:00
shell = ipython . __class__ . __name__
2022-11-07 18:06:49 +00:00
if (
" google.colab " in str ( ipython . __class__ )
or os . getenv ( " DATABRICKS_RUNTIME_VERSION " )
or shell == " ZMQInteractiveShell "
) :
2021-04-13 04:02:29 +00:00
return True # Jupyter notebook or qtconsole
elif shell == " TerminalInteractiveShell " :
return False # Terminal running IPython
else :
return False # Other type (?)
COLOR_SYSTEMS = {
" standard " : ColorSystem . STANDARD ,
" 256 " : ColorSystem . EIGHT_BIT ,
" truecolor " : ColorSystem . TRUECOLOR ,
" windows " : ColorSystem . WINDOWS ,
}
_COLOR_SYSTEMS_NAMES = { system : name for name , system in COLOR_SYSTEMS . items ( ) }
@dataclass
class ConsoleThreadLocals ( threading . local ) :
""" Thread local values for Console context. """
theme_stack : ThemeStack
buffer : List [ Segment ] = field ( default_factory = list )
buffer_index : int = 0
class RenderHook ( ABC ) :
""" Provides hooks in to the render process. """
@abstractmethod
def process_renderables (
self , renderables : List [ ConsoleRenderable ]
) - > List [ ConsoleRenderable ] :
""" Called with a list of objects to render.
This method can return a new list of renderables , or modify and return the same list .
Args :
renderables ( List [ ConsoleRenderable ] ) : A number of renderable objects .
Returns :
List [ ConsoleRenderable ] : A replacement list of renderables .
"""
_windows_console_features : Optional [ " WindowsConsoleFeatures " ] = None
def get_windows_console_features ( ) - > " WindowsConsoleFeatures " : # pragma: no cover
global _windows_console_features
if _windows_console_features is not None :
return _windows_console_features
from . _windows import get_windows_console_features
_windows_console_features = get_windows_console_features ( )
return _windows_console_features
def detect_legacy_windows ( ) - > bool :
""" Detect legacy Windows. """
return WINDOWS and not get_windows_console_features ( ) . vt
class Console :
""" A high level console interface.
Args :
color_system ( str , optional ) : The color system supported by your terminal ,
either ` ` " standard " ` ` , ` ` " 256 " ` ` or ` ` " truecolor " ` ` . Leave as ` ` " auto " ` ` to autodetect .
force_terminal ( Optional [ bool ] , optional ) : Enable / disable terminal control codes , or None to auto - detect terminal . Defaults to None .
force_jupyter ( Optional [ bool ] , optional ) : Enable / disable Jupyter rendering , or None to auto - detect Jupyter . Defaults to None .
force_interactive ( Optional [ bool ] , optional ) : Enable / disable interactive mode , or None to auto detect . Defaults to None .
soft_wrap ( Optional [ bool ] , optional ) : Set soft wrap default on print method . Defaults to False .
theme ( Theme , optional ) : An optional style theme object , or ` ` None ` ` for default theme .
stderr ( bool , optional ) : Use stderr rather than stdout if ` ` file ` ` is not specified . Defaults to False .
file ( IO , optional ) : A file object where the console should write to . Defaults to stdout .
quiet ( bool , Optional ) : Boolean to suppress all output . Defaults to False .
width ( int , optional ) : The width of the terminal . Leave as default to auto - detect width .
height ( int , optional ) : The height of the terminal . Leave as default to auto - detect height .
style ( StyleType , optional ) : Style to apply to all output , or None for no style . Defaults to None .
no_color ( Optional [ bool ] , optional ) : Enabled no color mode , or None to auto detect . Defaults to None .
tab_size ( int , optional ) : Number of spaces used to replace a tab character . Defaults to 8.
record ( bool , optional ) : Boolean to enable recording of terminal output ,
2022-11-07 18:06:49 +00:00
required to call : meth : ` export_html ` , : meth : ` export_svg ` , and : meth : ` export_text ` . Defaults to False .
2021-04-13 04:02:29 +00:00
markup ( bool , optional ) : Boolean to enable : ref : ` console_markup ` . Defaults to True .
emoji ( bool , optional ) : Enable emoji code . Defaults to True .
2022-01-24 04:07:52 +00:00
emoji_variant ( str , optional ) : Optional emoji variant , either " text " or " emoji " . Defaults to None .
2021-04-13 04:02:29 +00:00
highlight ( bool , optional ) : Enable automatic highlighting . Defaults to True .
log_time ( bool , optional ) : Boolean to enable logging of time by : meth : ` log ` methods . Defaults to True .
log_path ( bool , optional ) : Boolean to enable the logging of the caller by : meth : ` log ` . Defaults to True .
log_time_format ( Union [ str , TimeFormatterCallable ] , optional ) : If ` ` log_time ` ` is enabled , either string for strftime or callable that formats the time . Defaults to " [ %X ] " .
highlighter ( HighlighterType , optional ) : Default highlighter .
legacy_windows ( bool , optional ) : Enable legacy Windows mode , or ` ` None ` ` to auto detect . Defaults to ` ` None ` ` .
safe_box ( bool , optional ) : Restrict box options that don ' t render on legacy Windows.
get_datetime ( Callable [ [ ] , datetime ] , optional ) : Callable that gets the current time as a datetime . datetime object ( used by Console . log ) ,
or None for datetime . now .
get_time ( Callable [ [ ] , time ] , optional ) : Callable that gets the current time in seconds , default uses time . monotonic .
"""
_environ : Mapping [ str , str ] = os . environ
def __init__ (
self ,
* ,
color_system : Optional [
Literal [ " auto " , " standard " , " 256 " , " truecolor " , " windows " ]
] = " auto " ,
2022-01-24 04:07:52 +00:00
force_terminal : Optional [ bool ] = None ,
force_jupyter : Optional [ bool ] = None ,
force_interactive : Optional [ bool ] = None ,
2021-04-13 04:02:29 +00:00
soft_wrap : bool = False ,
2022-01-24 04:07:52 +00:00
theme : Optional [ Theme ] = None ,
2021-04-13 04:02:29 +00:00
stderr : bool = False ,
2022-01-24 04:07:52 +00:00
file : Optional [ IO [ str ] ] = None ,
2021-04-13 04:02:29 +00:00
quiet : bool = False ,
2022-01-24 04:07:52 +00:00
width : Optional [ int ] = None ,
height : Optional [ int ] = None ,
style : Optional [ StyleType ] = None ,
no_color : Optional [ bool ] = None ,
2021-04-13 04:02:29 +00:00
tab_size : int = 8 ,
record : bool = False ,
markup : bool = True ,
emoji : bool = True ,
2022-01-24 04:07:52 +00:00
emoji_variant : Optional [ EmojiVariant ] = None ,
2021-04-13 04:02:29 +00:00
highlight : bool = True ,
log_time : bool = True ,
log_path : bool = True ,
log_time_format : Union [ str , FormatTimeCallable ] = " [ %X ] " ,
highlighter : Optional [ " HighlighterType " ] = ReprHighlighter ( ) ,
2022-01-24 04:07:52 +00:00
legacy_windows : Optional [ bool ] = None ,
2021-04-13 04:02:29 +00:00
safe_box : bool = True ,
2022-01-24 04:07:52 +00:00
get_datetime : Optional [ Callable [ [ ] , datetime ] ] = None ,
get_time : Optional [ Callable [ [ ] , float ] ] = None ,
_environ : Optional [ Mapping [ str , str ] ] = None ,
2021-04-13 04:02:29 +00:00
) :
# Copy of os.environ allows us to replace it for testing
if _environ is not None :
self . _environ = _environ
self . is_jupyter = _is_jupyter ( ) if force_jupyter is None else force_jupyter
if self . is_jupyter :
2022-11-07 18:06:49 +00:00
if width is None :
jupyter_columns = self . _environ . get ( " JUPYTER_COLUMNS " )
if jupyter_columns is not None and jupyter_columns . isdigit ( ) :
width = int ( jupyter_columns )
else :
width = JUPYTER_DEFAULT_COLUMNS
if height is None :
jupyter_lines = self . _environ . get ( " JUPYTER_LINES " )
if jupyter_lines is not None and jupyter_lines . isdigit ( ) :
height = int ( jupyter_lines )
else :
height = JUPYTER_DEFAULT_LINES
2021-04-13 04:02:29 +00:00
self . tab_size = tab_size
self . record = record
self . _markup = markup
self . _emoji = emoji
2022-01-24 04:07:52 +00:00
self . _emoji_variant : Optional [ EmojiVariant ] = emoji_variant
2021-04-13 04:02:29 +00:00
self . _highlight = highlight
self . legacy_windows : bool = (
( detect_legacy_windows ( ) and not self . is_jupyter )
if legacy_windows is None
else legacy_windows
)
2022-11-07 18:06:49 +00:00
2022-01-24 04:07:52 +00:00
if width is None :
columns = self . _environ . get ( " COLUMNS " )
if columns is not None and columns . isdigit ( ) :
width = int ( columns ) - self . legacy_windows
if height is None :
lines = self . _environ . get ( " LINES " )
if lines is not None and lines . isdigit ( ) :
height = int ( lines )
self . soft_wrap = soft_wrap
self . _width = width
self . _height = height
2021-04-13 04:02:29 +00:00
self . _color_system : Optional [ ColorSystem ]
2022-11-07 18:06:49 +00:00
self . _force_terminal = None
if force_terminal is not None :
self . _force_terminal = force_terminal
else :
# If FORCE_COLOR env var has any value at all, we force terminal.
force_color = self . _environ . get ( " FORCE_COLOR " )
if force_color is not None :
self . _force_terminal = True
2021-04-13 04:02:29 +00:00
self . _file = file
self . quiet = quiet
self . stderr = stderr
if color_system is None :
self . _color_system = None
elif color_system == " auto " :
self . _color_system = self . _detect_color_system ( )
else :
self . _color_system = COLOR_SYSTEMS [ color_system ]
self . _lock = threading . RLock ( )
self . _log_render = LogRender (
show_time = log_time ,
show_path = log_path ,
time_format = log_time_format ,
)
self . highlighter : HighlighterType = highlighter or _null_highlighter
self . safe_box = safe_box
self . get_datetime = get_datetime or datetime . now
self . get_time = get_time or monotonic
self . style = style
self . no_color = (
no_color if no_color is not None else " NO_COLOR " in self . _environ
)
self . is_interactive = (
( self . is_terminal and not self . is_dumb_terminal )
if force_interactive is None
else force_interactive
)
self . _record_buffer_lock = threading . RLock ( )
self . _thread_locals = ConsoleThreadLocals (
theme_stack = ThemeStack ( themes . DEFAULT if theme is None else theme )
)
self . _record_buffer : List [ Segment ] = [ ]
self . _render_hooks : List [ RenderHook ] = [ ]
self . _live : Optional [ " Live " ] = None
self . _is_alt_screen = False
def __repr__ ( self ) - > str :
return f " <console width= { self . width } { str ( self . _color_system ) } > "
@property
def file ( self ) - > IO [ str ] :
""" Get the file object to write to. """
file = self . _file or ( sys . stderr if self . stderr else sys . stdout )
file = getattr ( file , " rich_proxied_file " , file )
2022-11-07 18:06:49 +00:00
if file is None :
file = NULL_FILE
2021-04-13 04:02:29 +00:00
return file
@file.setter
def file ( self , new_file : IO [ str ] ) - > None :
""" Set a new file object. """
self . _file = new_file
@property
def _buffer ( self ) - > List [ Segment ] :
""" Get a thread local buffer. """
return self . _thread_locals . buffer
@property
def _buffer_index ( self ) - > int :
""" Get a thread local buffer. """
return self . _thread_locals . buffer_index
@_buffer_index.setter
def _buffer_index ( self , value : int ) - > None :
self . _thread_locals . buffer_index = value
@property
def _theme_stack ( self ) - > ThemeStack :
""" Get the thread local theme stack. """
return self . _thread_locals . theme_stack
def _detect_color_system ( self ) - > Optional [ ColorSystem ] :
""" Detect color system from env vars. """
if self . is_jupyter :
return ColorSystem . TRUECOLOR
if not self . is_terminal or self . is_dumb_terminal :
return None
if WINDOWS : # pragma: no cover
if self . legacy_windows : # pragma: no cover
return ColorSystem . WINDOWS
windows_console_features = get_windows_console_features ( )
return (
ColorSystem . TRUECOLOR
if windows_console_features . truecolor
else ColorSystem . EIGHT_BIT
)
else :
color_term = self . _environ . get ( " COLORTERM " , " " ) . strip ( ) . lower ( )
if color_term in ( " truecolor " , " 24bit " ) :
return ColorSystem . TRUECOLOR
term = self . _environ . get ( " TERM " , " " ) . strip ( ) . lower ( )
2022-01-24 04:07:52 +00:00
_term_name , _hyphen , colors = term . rpartition ( " - " )
2021-04-13 04:02:29 +00:00
color_system = _TERM_COLORS . get ( colors , ColorSystem . STANDARD )
return color_system
def _enter_buffer ( self ) - > None :
""" Enter in to a buffer context, and buffer all output. """
self . _buffer_index + = 1
def _exit_buffer ( self ) - > None :
""" Leave buffer context, and render content if required. """
self . _buffer_index - = 1
self . _check_buffer ( )
def set_live ( self , live : " Live " ) - > None :
""" Set Live instance. Used by Live context manager.
Args :
live ( Live ) : Live instance using this Console .
Raises :
errors . LiveError : If this Console has a Live context currently active .
"""
with self . _lock :
if self . _live is not None :
raise errors . LiveError ( " Only one live display may be active at once " )
self . _live = live
def clear_live ( self ) - > None :
""" Clear the Live instance. """
with self . _lock :
self . _live = None
def push_render_hook ( self , hook : RenderHook ) - > None :
""" Add a new render hook to the stack.
Args :
hook ( RenderHook ) : Render hook instance .
"""
2022-01-24 04:07:52 +00:00
with self . _lock :
self . _render_hooks . append ( hook )
2021-04-13 04:02:29 +00:00
def pop_render_hook ( self ) - > None :
""" Pop the last renderhook from the stack. """
2022-01-24 04:07:52 +00:00
with self . _lock :
self . _render_hooks . pop ( )
2021-04-13 04:02:29 +00:00
def __enter__ ( self ) - > " Console " :
""" Own context manager to enter buffer context. """
self . _enter_buffer ( )
return self
2022-01-24 04:07:52 +00:00
def __exit__ ( self , exc_type : Any , exc_value : Any , traceback : Any ) - > None :
2021-04-13 04:02:29 +00:00
""" Exit buffer context. """
self . _exit_buffer ( )
def begin_capture ( self ) - > None :
""" Begin capturing console output. Call :meth:`end_capture` to exit capture mode and return output. """
self . _enter_buffer ( )
def end_capture ( self ) - > str :
""" End capture mode and return captured string.
Returns :
str : Console output .
"""
render_result = self . _render_buffer ( self . _buffer )
del self . _buffer [ : ]
self . _exit_buffer ( )
return render_result
def push_theme ( self , theme : Theme , * , inherit : bool = True ) - > None :
""" Push a new theme on to the top of the stack, replacing the styles from the previous theme.
Generally speaking , you should call : meth : ` ~ rich . console . Console . use_theme ` to get a context manager , rather
than calling this method directly .
Args :
theme ( Theme ) : A theme instance .
inherit ( bool , optional ) : Inherit existing styles . Defaults to True .
"""
self . _theme_stack . push_theme ( theme , inherit = inherit )
def pop_theme ( self ) - > None :
""" Remove theme from top of stack, restoring previous theme. """
self . _theme_stack . pop_theme ( )
def use_theme ( self , theme : Theme , * , inherit : bool = True ) - > ThemeContext :
""" Use a different theme for the duration of the context manager.
Args :
theme ( Theme ) : Theme instance to user .
inherit ( bool , optional ) : Inherit existing console styles . Defaults to True .
Returns :
ThemeContext : [ description ]
"""
return ThemeContext ( self , theme , inherit )
@property
def color_system ( self ) - > Optional [ str ] :
""" Get color system string.
Returns :
Optional [ str ] : " standard " , " 256 " or " truecolor " .
"""
if self . _color_system is not None :
return _COLOR_SYSTEMS_NAMES [ self . _color_system ]
else :
return None
@property
def encoding ( self ) - > str :
""" Get the encoding of the console file, e.g. `` " utf-8 " ``.
Returns :
str : A standard encoding string .
"""
return ( getattr ( self . file , " encoding " , " utf-8 " ) or " utf-8 " ) . lower ( )
@property
def is_terminal ( self ) - > bool :
""" Check if the console is writing to a terminal.
Returns :
bool : True if the console writing to a device capable of
understanding terminal codes , otherwise False .
"""
if self . _force_terminal is not None :
return self . _force_terminal
2022-11-07 18:06:49 +00:00
if hasattr ( sys . stdin , " __module__ " ) and sys . stdin . __module__ . startswith (
" idlelib "
) :
# Return False for Idle which claims to be a tty but can't handle ansi codes
return False
2022-01-24 04:07:52 +00:00
isatty : Optional [ Callable [ [ ] , bool ] ] = getattr ( self . file , " isatty " , None )
try :
return False if isatty is None else isatty ( )
except ValueError :
# in some situation (at the end of a pytest run for example) isatty() can raise
# ValueError: I/O operation on closed file
# return False because we aren't in a terminal anymore
return False
2021-04-13 04:02:29 +00:00
@property
def is_dumb_terminal ( self ) - > bool :
""" Detect dumb terminal.
Returns :
bool : True if writing to a dumb terminal , otherwise False .
"""
_term = self . _environ . get ( " TERM " , " " )
is_dumb = _term . lower ( ) in ( " dumb " , " unknown " )
return self . is_terminal and is_dumb
@property
def options ( self ) - > ConsoleOptions :
""" Get default console options. """
return ConsoleOptions (
2022-01-24 04:07:52 +00:00
max_height = self . size . height ,
2021-04-13 04:02:29 +00:00
size = self . size ,
legacy_windows = self . legacy_windows ,
min_width = 1 ,
max_width = self . width ,
encoding = self . encoding ,
is_terminal = self . is_terminal ,
)
@property
def size ( self ) - > ConsoleDimensions :
""" Get the size of the console.
Returns :
ConsoleDimensions : A named tuple containing the dimensions .
"""
if self . _width is not None and self . _height is not None :
2022-01-24 04:07:52 +00:00
return ConsoleDimensions ( self . _width - self . legacy_windows , self . _height )
2021-04-13 04:02:29 +00:00
if self . is_dumb_terminal :
return ConsoleDimensions ( 80 , 25 )
width : Optional [ int ] = None
height : Optional [ int ] = None
2022-01-24 04:07:52 +00:00
2021-04-13 04:02:29 +00:00
if WINDOWS : # pragma: no cover
2022-01-24 04:07:52 +00:00
try :
width , height = os . get_terminal_size ( )
2022-11-07 18:06:49 +00:00
except ( AttributeError , ValueError , OSError ) : # Probably not a terminal
2022-01-24 04:07:52 +00:00
pass
2021-04-13 04:02:29 +00:00
else :
2022-11-07 18:06:49 +00:00
for file_descriptor in _STD_STREAMS :
2021-04-13 04:02:29 +00:00
try :
2022-11-07 18:06:49 +00:00
width , height = os . get_terminal_size ( file_descriptor )
2021-04-13 04:02:29 +00:00
except ( AttributeError , ValueError , OSError ) :
pass
2022-11-07 18:06:49 +00:00
else :
break
2021-04-13 04:02:29 +00:00
2022-01-24 04:07:52 +00:00
columns = self . _environ . get ( " COLUMNS " )
if columns is not None and columns . isdigit ( ) :
width = int ( columns )
lines = self . _environ . get ( " LINES " )
if lines is not None and lines . isdigit ( ) :
height = int ( lines )
2021-04-13 04:02:29 +00:00
# get_terminal_size can report 0, 0 if run from pseudo-terminal
width = width or 80
height = height or 25
return ConsoleDimensions (
2022-01-24 04:07:52 +00:00
width - self . legacy_windows if self . _width is None else self . _width ,
2021-04-13 04:02:29 +00:00
height if self . _height is None else self . _height ,
)
2022-01-24 04:07:52 +00:00
@size.setter
def size ( self , new_size : Tuple [ int , int ] ) - > None :
""" Set a new size for the terminal.
Args :
new_size ( Tuple [ int , int ] ) : New width and height .
"""
width , height = new_size
self . _width = width
self . _height = height
2021-04-13 04:02:29 +00:00
@property
def width ( self ) - > int :
""" Get the width of the console.
Returns :
int : The width ( in characters ) of the console .
"""
2022-01-24 04:07:52 +00:00
return self . size . width
@width.setter
def width ( self , width : int ) - > None :
""" Set width.
Args :
width ( int ) : New width .
"""
self . _width = width
2021-04-13 04:02:29 +00:00
@property
def height ( self ) - > int :
""" Get the height of the console.
Returns :
int : The height ( in lines ) of the console .
"""
2022-01-24 04:07:52 +00:00
return self . size . height
@height.setter
def height ( self , height : int ) - > None :
""" Set height.
Args :
height ( int ) : new height .
"""
self . _height = height
2021-04-13 04:02:29 +00:00
def bell ( self ) - > None :
""" Play a ' bell ' sound (if supported by the terminal). """
self . control ( Control . bell ( ) )
def capture ( self ) - > Capture :
""" A context manager to *capture* the result of print() or log() in a string,
rather than writing it to the console .
Example :
>> > from rich . console import Console
>> > console = Console ( )
>> > with console . capture ( ) as capture :
. . . console . print ( " [bold magenta]Hello World[/] " )
>> > print ( capture . get ( ) )
Returns :
Capture : Context manager with disables writing to the terminal .
"""
capture = Capture ( self )
return capture
def pager (
2022-01-24 04:07:52 +00:00
self , pager : Optional [ Pager ] = None , styles : bool = False , links : bool = False
2021-04-13 04:02:29 +00:00
) - > PagerContext :
""" A context manager to display anything printed within a " pager " . The pager application
is defined by the system and will typically support at least pressing a key to scroll .
Args :
2022-01-24 04:07:52 +00:00
pager ( Pager , optional ) : A pager object , or None to use : class : ` ~ rich . pager . SystemPager ` . Defaults to None .
2021-04-13 04:02:29 +00:00
styles ( bool , optional ) : Show styles in pager . Defaults to False .
links ( bool , optional ) : Show links in pager . Defaults to False .
Example :
>> > from rich . console import Console
>> > from rich . __main__ import make_test_card
>> > console = Console ( )
>> > with console . pager ( ) :
console . print ( make_test_card ( ) )
Returns :
PagerContext : A context manager .
"""
return PagerContext ( self , pager = pager , styles = styles , links = links )
def line ( self , count : int = 1 ) - > None :
""" Write new line(s).
Args :
count ( int , optional ) : Number of new lines . Defaults to 1.
"""
assert count > = 0 , " count must be >= 0 "
self . print ( NewLine ( count ) )
def clear ( self , home : bool = True ) - > None :
""" Clear the screen.
Args :
home ( bool , optional ) : Also move the cursor to ' home ' position . Defaults to True .
"""
if home :
self . control ( Control . clear ( ) , Control . home ( ) )
else :
self . control ( Control . clear ( ) )
def status (
self ,
status : RenderableType ,
* ,
spinner : str = " dots " ,
spinner_style : str = " status.spinner " ,
speed : float = 1.0 ,
refresh_per_second : float = 12.5 ,
) - > " Status " :
""" Display a status and spinner.
Args :
status ( RenderableType ) : A status renderable ( str or Text typically ) .
spinner ( str , optional ) : Name of spinner animation ( see python - m rich . spinner ) . Defaults to " dots " .
spinner_style ( StyleType , optional ) : Style of spinner . Defaults to " status.spinner " .
speed ( float , optional ) : Speed factor for spinner animation . Defaults to 1.0 .
refresh_per_second ( float , optional ) : Number of refreshes per second . Defaults to 12.5 .
Returns :
Status : A Status object that may be used as a context manager .
"""
from . status import Status
status_renderable = Status (
status ,
console = self ,
spinner = spinner ,
spinner_style = spinner_style ,
speed = speed ,
refresh_per_second = refresh_per_second ,
)
return status_renderable
def show_cursor ( self , show : bool = True ) - > bool :
""" Show or hide the cursor.
Args :
show ( bool , optional ) : Set visibility of the cursor .
"""
2022-11-07 18:06:49 +00:00
if self . is_terminal :
2021-04-13 04:02:29 +00:00
self . control ( Control . show_cursor ( show ) )
return True
return False
def set_alt_screen ( self , enable : bool = True ) - > bool :
""" Enables alternative screen mode.
Note , if you enable this mode , you should ensure that is disabled before
the application exits . See : meth : ` ~ rich . Console . screen ` for a context manager
that handles this for you .
Args :
enable ( bool , optional ) : Enable ( True ) or disable ( False ) alternate screen . Defaults to True .
Returns :
bool : True if the control codes were written .
"""
changed = False
if self . is_terminal and not self . legacy_windows :
self . control ( Control . alt_screen ( enable ) )
changed = True
self . _is_alt_screen = enable
return changed
@property
def is_alt_screen ( self ) - > bool :
""" Check if the alt screen was enabled.
Returns :
bool : True if the alt screen was enabled , otherwise False .
"""
return self . _is_alt_screen
2022-11-07 18:06:49 +00:00
def set_window_title ( self , title : str ) - > bool :
""" Set the title of the console terminal window.
Warning : There is no means within Rich of " resetting " the window title to its
previous value , meaning the title you set will persist even after your application
exits .
` ` fish ` ` shell resets the window title before and after each command by default ,
negating this issue . Windows Terminal and command prompt will also reset the title for you .
Most other shells and terminals , however , do not do this .
Some terminals may require configuration changes before you can set the title .
Some terminals may not support setting the title at all .
Other software ( including the terminal itself , the shell , custom prompts , plugins , etc . )
may also set the terminal window title . This could result in whatever value you write
using this method being overwritten .
Args :
title ( str ) : The new title of the terminal window .
Returns :
bool : True if the control code to change the terminal title was
written , otherwise False . Note that a return value of True
does not guarantee that the window title has actually changed ,
since the feature may be unsupported / disabled in some terminals .
"""
if self . is_terminal :
self . control ( Control . title ( title ) )
return True
return False
2021-04-13 04:02:29 +00:00
def screen (
2022-01-24 04:07:52 +00:00
self , hide_cursor : bool = True , style : Optional [ StyleType ] = None
2021-04-13 04:02:29 +00:00
) - > " ScreenContext " :
""" Context manager to enable and disable ' alternative screen ' mode.
Args :
hide_cursor ( bool , optional ) : Also hide the cursor . Defaults to False .
style ( Style , optional ) : Optional style for screen . Defaults to None .
Returns :
~ ScreenContext : Context which enables alternate screen on enter , and disables it on exit .
"""
return ScreenContext ( self , hide_cursor = hide_cursor , style = style or " " )
2022-01-24 04:07:52 +00:00
def measure (
self , renderable : RenderableType , * , options : Optional [ ConsoleOptions ] = None
) - > Measurement :
""" Measure a renderable. Returns a :class:`~rich.measure.Measurement` object which contains
information regarding the number of characters required to print the renderable .
Args :
renderable ( RenderableType ) : Any renderable or string .
options ( Optional [ ConsoleOptions ] , optional ) : Options to use when measuring , or None
to use default options . Defaults to None .
Returns :
Measurement : A measurement of the renderable .
"""
measurement = Measurement . get ( self , options or self . options , renderable )
return measurement
2021-04-13 04:02:29 +00:00
def render (
2022-01-24 04:07:52 +00:00
self , renderable : RenderableType , options : Optional [ ConsoleOptions ] = None
2021-04-13 04:02:29 +00:00
) - > Iterable [ Segment ] :
""" Render an object in to an iterable of `Segment` instances.
This method contains the logic for rendering objects with the console protocol .
You are unlikely to need to use it directly , unless you are extending the library .
Args :
renderable ( RenderableType ) : An object supporting the console protocol , or
an object that may be converted to a string .
options ( ConsoleOptions , optional ) : An options object , or None to use self . options . Defaults to None .
Returns :
Iterable [ Segment ] : An iterable of segments that may be rendered .
"""
_options = options or self . options
if _options . max_width < 1 :
# No space to render anything. This prevents potential recursion errors.
return
render_iterable : RenderResult
2022-01-24 04:07:52 +00:00
renderable = rich_cast ( renderable )
if hasattr ( renderable , " __rich_console__ " ) and not isclass ( renderable ) :
2022-11-07 18:06:49 +00:00
render_iterable = renderable . __rich_console__ ( self , _options ) # type: ignore[union-attr]
2021-04-13 04:02:29 +00:00
elif isinstance ( renderable , str ) :
text_renderable = self . render_str (
renderable , highlight = _options . highlight , markup = _options . markup
)
2022-01-24 04:07:52 +00:00
render_iterable = text_renderable . __rich_console__ ( self , _options )
2021-04-13 04:02:29 +00:00
else :
raise errors . NotRenderableError (
f " Unable to render { renderable !r} ; "
" A str, Segment or object with __rich_console__ method is required "
)
try :
iter_render = iter ( render_iterable )
except TypeError :
raise errors . NotRenderableError (
f " object { render_iterable !r} is not renderable "
)
_Segment = Segment
2022-11-07 18:06:49 +00:00
_options = _options . reset_height ( )
2021-04-13 04:02:29 +00:00
for render_output in iter_render :
if isinstance ( render_output , _Segment ) :
yield render_output
else :
yield from self . render ( render_output , _options )
def render_lines (
self ,
renderable : RenderableType ,
options : Optional [ ConsoleOptions ] = None ,
* ,
style : Optional [ Style ] = None ,
pad : bool = True ,
new_lines : bool = False ,
) - > List [ List [ Segment ] ] :
""" Render objects in to a list of lines.
The output of render_lines is useful when further formatting of rendered console text
is required , such as the Panel class which draws a border around any renderable object .
Args :
renderable ( RenderableType ) : Any object renderable in the console .
options ( Optional [ ConsoleOptions ] , optional ) : Console options , or None to use self . options . Default to ` ` None ` ` .
style ( Style , optional ) : Optional style to apply to renderables . Defaults to ` ` None ` ` .
pad ( bool , optional ) : Pad lines shorter than render width . Defaults to ` ` True ` ` .
new_lines ( bool , optional ) : Include " \n " characters at end of lines .
Returns :
List [ List [ Segment ] ] : A list of lines , where a line is a list of Segment objects .
"""
with self . _lock :
render_options = options or self . options
_rendered = self . render ( renderable , render_options )
if style :
_rendered = Segment . apply_style ( _rendered , style )
2022-11-07 18:06:49 +00:00
render_height = render_options . height
if render_height is not None :
render_height = max ( 0 , render_height )
2021-04-13 04:02:29 +00:00
lines = list (
islice (
Segment . split_and_crop_lines (
_rendered ,
render_options . max_width ,
include_new_lines = new_lines ,
pad = pad ,
2022-11-07 18:06:49 +00:00
style = style ,
2021-04-13 04:02:29 +00:00
) ,
None ,
2022-11-07 18:06:49 +00:00
render_height ,
2021-04-13 04:02:29 +00:00
)
)
if render_options . height is not None :
extra_lines = render_options . height - len ( lines )
if extra_lines > 0 :
pad_line = [
[ Segment ( " " * render_options . max_width , style ) , Segment ( " \n " ) ]
if new_lines
else [ Segment ( " " * render_options . max_width , style ) ]
]
lines . extend ( pad_line * extra_lines )
return lines
def render_str (
self ,
text : str ,
* ,
style : Union [ str , Style ] = " " ,
2022-01-24 04:07:52 +00:00
justify : Optional [ JustifyMethod ] = None ,
overflow : Optional [ OverflowMethod ] = None ,
emoji : Optional [ bool ] = None ,
markup : Optional [ bool ] = None ,
highlight : Optional [ bool ] = None ,
highlighter : Optional [ HighlighterType ] = None ,
2021-04-13 04:02:29 +00:00
) - > " Text " :
2022-11-07 18:06:49 +00:00
""" Convert a string to a Text instance. This is called automatically if
2021-04-13 04:02:29 +00:00
you print or log a string .
Args :
text ( str ) : Text to render .
style ( Union [ str , Style ] , optional ) : Style to apply to rendered text .
justify ( str , optional ) : Justify method : " default " , " left " , " center " , " full " , or " right " . Defaults to ` ` None ` ` .
overflow ( str , optional ) : Overflow method : " crop " , " fold " , or " ellipsis " . Defaults to ` ` None ` ` .
emoji ( Optional [ bool ] , optional ) : Enable emoji , or ` ` None ` ` to use Console default .
markup ( Optional [ bool ] , optional ) : Enable markup , or ` ` None ` ` to use Console default .
highlight ( Optional [ bool ] , optional ) : Enable highlighting , or ` ` None ` ` to use Console default .
highlighter ( HighlighterType , optional ) : Optional highlighter to apply .
Returns :
ConsoleRenderable : Renderable object .
"""
emoji_enabled = emoji or ( emoji is None and self . _emoji )
markup_enabled = markup or ( markup is None and self . _markup )
highlight_enabled = highlight or ( highlight is None and self . _highlight )
if markup_enabled :
2022-01-24 04:07:52 +00:00
rich_text = render_markup (
text ,
style = style ,
emoji = emoji_enabled ,
emoji_variant = self . _emoji_variant ,
)
2021-04-13 04:02:29 +00:00
rich_text . justify = justify
rich_text . overflow = overflow
else :
rich_text = Text (
2022-01-24 04:07:52 +00:00
_emoji_replace ( text , default_variant = self . _emoji_variant )
if emoji_enabled
else text ,
2021-04-13 04:02:29 +00:00
justify = justify ,
overflow = overflow ,
style = style ,
)
_highlighter = ( highlighter or self . highlighter ) if highlight_enabled else None
if _highlighter is not None :
highlight_text = _highlighter ( str ( rich_text ) )
highlight_text . copy_styles ( rich_text )
return highlight_text
return rich_text
def get_style (
2022-01-24 04:07:52 +00:00
self , name : Union [ str , Style ] , * , default : Optional [ Union [ Style , str ] ] = None
2021-04-13 04:02:29 +00:00
) - > Style :
2022-11-07 18:06:49 +00:00
""" Get a Style instance by its theme name or parse a definition.
2021-04-13 04:02:29 +00:00
Args :
name ( str ) : The name of a style or a style definition .
Returns :
Style : A Style object .
Raises :
MissingStyle : If no style could be parsed from name .
"""
if isinstance ( name , Style ) :
return name
try :
style = self . _theme_stack . get ( name )
if style is None :
style = Style . parse ( name )
return style . copy ( ) if style . link else style
except errors . StyleSyntaxError as error :
if default is not None :
return self . get_style ( default )
raise errors . MissingStyle (
f " Failed to get style { name !r} ; { error } "
) from None
def _collect_renderables (
self ,
objects : Iterable [ Any ] ,
sep : str ,
end : str ,
* ,
2022-01-24 04:07:52 +00:00
justify : Optional [ JustifyMethod ] = None ,
emoji : Optional [ bool ] = None ,
markup : Optional [ bool ] = None ,
highlight : Optional [ bool ] = None ,
2021-04-13 04:02:29 +00:00
) - > List [ ConsoleRenderable ] :
""" Combine a number of renderables and text into one renderable.
Args :
objects ( Iterable [ Any ] ) : Anything that Rich can render .
sep ( str ) : String to write between print data .
end ( str ) : String to write at end of print data .
justify ( str , optional ) : One of " left " , " right " , " center " , or " full " . Defaults to ` ` None ` ` .
emoji ( Optional [ bool ] , optional ) : Enable emoji code , or ` ` None ` ` to use console default .
markup ( Optional [ bool ] , optional ) : Enable markup , or ` ` None ` ` to use console default .
highlight ( Optional [ bool ] , optional ) : Enable automatic highlighting , or ` ` None ` ` to use console default .
Returns :
List [ ConsoleRenderable ] : A list of things to render .
"""
renderables : List [ ConsoleRenderable ] = [ ]
_append = renderables . append
text : List [ Text ] = [ ]
append_text = text . append
append = _append
if justify in ( " left " , " center " , " right " ) :
def align_append ( renderable : RenderableType ) - > None :
_append ( Align ( renderable , cast ( AlignMethod , justify ) ) )
append = align_append
_highlighter : HighlighterType = _null_highlighter
if highlight or ( highlight is None and self . _highlight ) :
_highlighter = self . highlighter
def check_text ( ) - > None :
if text :
sep_text = Text ( sep , justify = justify , end = end )
append ( sep_text . join ( text ) )
del text [ : ]
for renderable in objects :
2022-01-24 04:07:52 +00:00
renderable = rich_cast ( renderable )
2021-04-13 04:02:29 +00:00
if isinstance ( renderable , str ) :
append_text (
self . render_str (
renderable , emoji = emoji , markup = markup , highlighter = _highlighter
)
)
2022-01-24 04:07:52 +00:00
elif isinstance ( renderable , Text ) :
append_text ( renderable )
2021-04-13 04:02:29 +00:00
elif isinstance ( renderable , ConsoleRenderable ) :
check_text ( )
append ( renderable )
elif is_expandable ( renderable ) :
check_text ( )
append ( Pretty ( renderable , highlighter = _highlighter ) )
else :
append_text ( _highlighter ( str ( renderable ) ) )
check_text ( )
if self . style is not None :
style = self . get_style ( self . style )
renderables = [ Styled ( renderable , style ) for renderable in renderables ]
return renderables
def rule (
self ,
title : TextType = " " ,
* ,
characters : str = " ─ " ,
style : Union [ str , Style ] = " rule.line " ,
align : AlignMethod = " center " ,
) - > None :
""" Draw a line with optional centered title.
Args :
title ( str , optional ) : Text to render over the rule . Defaults to " " .
characters ( str , optional ) : Character ( s ) to form the line . Defaults to " ─ " .
style ( str , optional ) : Style of line . Defaults to " rule.line " .
align ( str , optional ) : How to align the title , one of " left " , " center " , or " right " . Defaults to " center " .
"""
from . rule import Rule
rule = Rule ( title = title , characters = characters , style = style , align = align )
self . print ( rule )
def control ( self , * control : Control ) - > None :
""" Insert non-printing control codes.
Args :
control_codes ( str ) : Control codes , such as those that may move the cursor .
"""
if not self . is_dumb_terminal :
2022-01-24 04:07:52 +00:00
with self :
self . _buffer . extend ( _control . segment for _control in control )
2021-04-13 04:02:29 +00:00
def out (
self ,
* objects : Any ,
2022-01-24 04:07:52 +00:00
sep : str = " " ,
end : str = " \n " ,
style : Optional [ Union [ str , Style ] ] = None ,
highlight : Optional [ bool ] = None ,
2021-04-13 04:02:29 +00:00
) - > None :
""" Output to the terminal. This is a low-level way of writing to the terminal which unlike
: meth : ` ~ rich . console . Console . print ` won ' t pretty print, wrap text, or apply markup, but will
optionally apply highlighting and a basic style .
Args :
sep ( str , optional ) : String to write between print data . Defaults to " " .
end ( str , optional ) : String to write at end of print data . Defaults to " \\ \\ n " .
style ( Union [ str , Style ] , optional ) : A style to apply to output . Defaults to None .
highlight ( Optional [ bool ] , optional ) : Enable automatic highlighting , or ` ` None ` ` to use
console default . Defaults to ` ` None ` ` .
"""
raw_output : str = sep . join ( str ( _object ) for _object in objects )
self . print (
raw_output ,
style = style ,
highlight = highlight ,
emoji = False ,
markup = False ,
no_wrap = True ,
overflow = " ignore " ,
crop = False ,
end = end ,
)
def print (
self ,
* objects : Any ,
2022-01-24 04:07:52 +00:00
sep : str = " " ,
end : str = " \n " ,
style : Optional [ Union [ str , Style ] ] = None ,
justify : Optional [ JustifyMethod ] = None ,
overflow : Optional [ OverflowMethod ] = None ,
no_wrap : Optional [ bool ] = None ,
emoji : Optional [ bool ] = None ,
markup : Optional [ bool ] = None ,
highlight : Optional [ bool ] = None ,
width : Optional [ int ] = None ,
height : Optional [ int ] = None ,
2021-04-13 04:02:29 +00:00
crop : bool = True ,
2022-01-24 04:07:52 +00:00
soft_wrap : Optional [ bool ] = None ,
new_line_start : bool = False ,
2021-04-13 04:02:29 +00:00
) - > None :
""" Print to the console.
Args :
objects ( positional args ) : Objects to log to the terminal .
sep ( str , optional ) : String to write between print data . Defaults to " " .
end ( str , optional ) : String to write at end of print data . Defaults to " \\ \\ n " .
style ( Union [ str , Style ] , optional ) : A style to apply to output . Defaults to None .
justify ( str , optional ) : Justify method : " default " , " left " , " right " , " center " , or " full " . Defaults to ` ` None ` ` .
overflow ( str , optional ) : Overflow method : " ignore " , " crop " , " fold " , or " ellipsis " . Defaults to None .
no_wrap ( Optional [ bool ] , optional ) : Disable word wrapping . Defaults to None .
emoji ( Optional [ bool ] , optional ) : Enable emoji code , or ` ` None ` ` to use console default . Defaults to ` ` None ` ` .
markup ( Optional [ bool ] , optional ) : Enable markup , or ` ` None ` ` to use console default . Defaults to ` ` None ` ` .
highlight ( Optional [ bool ] , optional ) : Enable automatic highlighting , or ` ` None ` ` to use console default . Defaults to ` ` None ` ` .
width ( Optional [ int ] , optional ) : Width of output , or ` ` None ` ` to auto - detect . Defaults to ` ` None ` ` .
crop ( Optional [ bool ] , optional ) : Crop output to width of terminal . Defaults to True .
2022-01-24 04:07:52 +00:00
soft_wrap ( bool , optional ) : Enable soft wrap mode which disables word wrapping and cropping of text or ` ` None ` ` for
2021-04-13 04:02:29 +00:00
Console default . Defaults to ` ` None ` ` .
2022-01-24 04:07:52 +00:00
new_line_start ( bool , False ) : Insert a new line at the start if the output contains more than one line . Defaults to ` ` False ` ` .
2021-04-13 04:02:29 +00:00
"""
if not objects :
objects = ( NewLine ( ) , )
if soft_wrap is None :
soft_wrap = self . soft_wrap
if soft_wrap :
if no_wrap is None :
no_wrap = True
if overflow is None :
overflow = " ignore "
crop = False
2022-01-24 04:07:52 +00:00
render_hooks = self . _render_hooks [ : ]
2021-04-13 04:02:29 +00:00
with self :
renderables = self . _collect_renderables (
objects ,
sep ,
end ,
justify = justify ,
emoji = emoji ,
markup = markup ,
highlight = highlight ,
)
2022-01-24 04:07:52 +00:00
for hook in render_hooks :
2021-04-13 04:02:29 +00:00
renderables = hook . process_renderables ( renderables )
render_options = self . options . update (
justify = justify ,
overflow = overflow ,
width = min ( width , self . width ) if width is not None else NO_CHANGE ,
height = height ,
no_wrap = no_wrap ,
markup = markup ,
2022-01-24 04:07:52 +00:00
highlight = highlight ,
2021-04-13 04:02:29 +00:00
)
new_segments : List [ Segment ] = [ ]
extend = new_segments . extend
render = self . render
if style is None :
for renderable in renderables :
extend ( render ( renderable , render_options ) )
else :
for renderable in renderables :
extend (
Segment . apply_style (
render ( renderable , render_options ) , self . get_style ( style )
)
)
2022-01-24 04:07:52 +00:00
if new_line_start :
if (
len ( " " . join ( segment . text for segment in new_segments ) . splitlines ( ) )
> 1
) :
new_segments . insert ( 0 , Segment . line ( ) )
2021-04-13 04:02:29 +00:00
if crop :
buffer_extend = self . _buffer . extend
for line in Segment . split_and_crop_lines (
new_segments , self . width , pad = False
) :
buffer_extend ( line )
else :
self . _buffer . extend ( new_segments )
2022-01-24 04:07:52 +00:00
def print_json (
self ,
json : Optional [ str ] = None ,
* ,
data : Any = None ,
indent : Union [ None , int , str ] = 2 ,
highlight : bool = True ,
skip_keys : bool = False ,
2022-11-07 18:06:49 +00:00
ensure_ascii : bool = False ,
2022-01-24 04:07:52 +00:00
check_circular : bool = True ,
allow_nan : bool = True ,
default : Optional [ Callable [ [ Any ] , Any ] ] = None ,
sort_keys : bool = False ,
) - > None :
""" Pretty prints JSON. Output will be valid JSON.
Args :
json ( Optional [ str ] ) : A string containing JSON .
data ( Any ) : If json is not supplied , then encode this data .
indent ( Union [ None , int , str ] , optional ) : Number of spaces to indent . Defaults to 2.
highlight ( bool , optional ) : Enable highlighting of output : Defaults to True .
skip_keys ( bool , optional ) : Skip keys not of a basic type . Defaults to False .
ensure_ascii ( bool , optional ) : Escape all non - ascii characters . Defaults to False .
check_circular ( bool , optional ) : Check for circular references . Defaults to True .
allow_nan ( bool , optional ) : Allow NaN and Infinity values . Defaults to True .
default ( Callable , optional ) : A callable that converts values that can not be encoded
in to something that can be JSON encoded . Defaults to None .
sort_keys ( bool , optional ) : Sort dictionary keys . Defaults to False .
"""
from rich . json import JSON
if json is None :
json_renderable = JSON . from_data (
data ,
indent = indent ,
highlight = highlight ,
skip_keys = skip_keys ,
ensure_ascii = ensure_ascii ,
check_circular = check_circular ,
allow_nan = allow_nan ,
default = default ,
sort_keys = sort_keys ,
)
else :
if not isinstance ( json , str ) :
raise TypeError (
f " json must be str. Did you mean print_json(data= { json !r} ) ? "
)
json_renderable = JSON (
json ,
indent = indent ,
highlight = highlight ,
skip_keys = skip_keys ,
ensure_ascii = ensure_ascii ,
check_circular = check_circular ,
allow_nan = allow_nan ,
default = default ,
sort_keys = sort_keys ,
)
self . print ( json_renderable , soft_wrap = True )
2021-04-13 04:02:29 +00:00
def update_screen (
self ,
renderable : RenderableType ,
* ,
2022-01-24 04:07:52 +00:00
region : Optional [ Region ] = None ,
options : Optional [ ConsoleOptions ] = None ,
2021-04-13 04:02:29 +00:00
) - > None :
""" Update the screen at a given offset.
Args :
renderable ( RenderableType ) : A Rich renderable .
region ( Region , optional ) : Region of screen to update , or None for entire screen . Defaults to None .
x ( int , optional ) : x offset . Defaults to 0.
y ( int , optional ) : y offset . Defaults to 0.
Raises :
errors . NoAltScreen : If the Console isn ' t in alt screen mode.
"""
if not self . is_alt_screen :
raise errors . NoAltScreen ( " Alt screen must be enabled to call update_screen " )
render_options = options or self . options
if region is None :
x = y = 0
render_options = render_options . update_dimensions (
render_options . max_width , render_options . height or self . height
)
else :
x , y , width , height = region
render_options = render_options . update_dimensions ( width , height )
lines = self . render_lines ( renderable , options = render_options )
self . update_screen_lines ( lines , x , y )
2022-01-24 04:07:52 +00:00
def update_screen_lines (
self , lines : List [ List [ Segment ] ] , x : int = 0 , y : int = 0
) - > None :
2021-04-13 04:02:29 +00:00
""" Update lines of the screen at a given offset.
Args :
lines ( List [ List [ Segment ] ] ) : Rendered lines ( as produced by : meth : ` ~ rich . Console . render_lines ` ) .
x ( int , optional ) : x offset ( column no ) . Defaults to 0.
y ( int , optional ) : y offset ( column no ) . Defaults to 0.
Raises :
errors . NoAltScreen : If the Console isn ' t in alt screen mode.
"""
if not self . is_alt_screen :
raise errors . NoAltScreen ( " Alt screen must be enabled to call update_screen " )
screen_update = ScreenUpdate ( lines , x , y )
segments = self . render ( screen_update )
self . _buffer . extend ( segments )
self . _check_buffer ( )
def print_exception (
self ,
* ,
width : Optional [ int ] = 100 ,
extra_lines : int = 3 ,
theme : Optional [ str ] = None ,
word_wrap : bool = False ,
show_locals : bool = False ,
2022-01-24 04:07:52 +00:00
suppress : Iterable [ Union [ str , ModuleType ] ] = ( ) ,
max_frames : int = 100 ,
2021-04-13 04:02:29 +00:00
) - > None :
""" Prints a rich render of the last exception and traceback.
Args :
2022-11-07 18:06:49 +00:00
width ( Optional [ int ] , optional ) : Number of characters used to render code . Defaults to 100.
2021-04-13 04:02:29 +00:00
extra_lines ( int , optional ) : Additional lines of code to render . Defaults to 3.
theme ( str , optional ) : Override pygments theme used in traceback
word_wrap ( bool , optional ) : Enable word wrapping of long lines . Defaults to False .
show_locals ( bool , optional ) : Enable display of local variables . Defaults to False .
2022-01-24 04:07:52 +00:00
suppress ( Iterable [ Union [ str , ModuleType ] ] ) : Optional sequence of modules or paths to exclude from traceback .
max_frames ( int ) : Maximum number of frames to show in a traceback , 0 for no maximum . Defaults to 100.
2021-04-13 04:02:29 +00:00
"""
from . traceback import Traceback
traceback = Traceback (
width = width ,
extra_lines = extra_lines ,
theme = theme ,
word_wrap = word_wrap ,
show_locals = show_locals ,
2022-01-24 04:07:52 +00:00
suppress = suppress ,
max_frames = max_frames ,
2021-04-13 04:02:29 +00:00
)
self . print ( traceback )
2022-01-24 04:07:52 +00:00
@staticmethod
def _caller_frame_info (
offset : int ,
currentframe : Callable [ [ ] , Optional [ FrameType ] ] = inspect . currentframe ,
) - > Tuple [ str , int , Dict [ str , Any ] ] :
""" Get caller frame information.
Args :
offset ( int ) : the caller offset within the current frame stack .
currentframe ( Callable [ [ ] , Optional [ FrameType ] ] , optional ) : the callable to use to
retrieve the current frame . Defaults to ` ` inspect . currentframe ` ` .
Returns :
Tuple [ str , int , Dict [ str , Any ] ] : A tuple containing the filename , the line number and
the dictionary of local variables associated with the caller frame .
Raises :
RuntimeError : If the stack offset is invalid .
"""
# Ignore the frame of this local helper
offset + = 1
frame = currentframe ( )
if frame is not None :
# Use the faster currentframe where implemented
2022-11-07 18:06:49 +00:00
while offset and frame is not None :
2022-01-24 04:07:52 +00:00
frame = frame . f_back
offset - = 1
assert frame is not None
return frame . f_code . co_filename , frame . f_lineno , frame . f_locals
else :
# Fallback to the slower stack
frame_info = inspect . stack ( ) [ offset ]
return frame_info . filename , frame_info . lineno , frame_info . frame . f_locals
2021-04-13 04:02:29 +00:00
def log (
self ,
* objects : Any ,
2022-01-24 04:07:52 +00:00
sep : str = " " ,
end : str = " \n " ,
style : Optional [ Union [ str , Style ] ] = None ,
justify : Optional [ JustifyMethod ] = None ,
emoji : Optional [ bool ] = None ,
markup : Optional [ bool ] = None ,
highlight : Optional [ bool ] = None ,
2021-04-13 04:02:29 +00:00
log_locals : bool = False ,
2022-01-24 04:07:52 +00:00
_stack_offset : int = 1 ,
2021-04-13 04:02:29 +00:00
) - > None :
""" Log rich content to the terminal.
Args :
objects ( positional args ) : Objects to log to the terminal .
sep ( str , optional ) : String to write between print data . Defaults to " " .
end ( str , optional ) : String to write at end of print data . Defaults to " \\ \\ n " .
style ( Union [ str , Style ] , optional ) : A style to apply to output . Defaults to None .
justify ( str , optional ) : One of " left " , " right " , " center " , or " full " . Defaults to ` ` None ` ` .
overflow ( str , optional ) : Overflow method : " crop " , " fold " , or " ellipsis " . Defaults to None .
emoji ( Optional [ bool ] , optional ) : Enable emoji code , or ` ` None ` ` to use console default . Defaults to None .
markup ( Optional [ bool ] , optional ) : Enable markup , or ` ` None ` ` to use console default . Defaults to None .
highlight ( Optional [ bool ] , optional ) : Enable automatic highlighting , or ` ` None ` ` to use console default . Defaults to None .
log_locals ( bool , optional ) : Boolean to enable logging of locals where ` ` log ( ) ` `
was called . Defaults to False .
_stack_offset ( int , optional ) : Offset of caller from end of call stack . Defaults to 1.
"""
if not objects :
objects = ( NewLine ( ) , )
2022-01-24 04:07:52 +00:00
render_hooks = self . _render_hooks [ : ]
2021-04-13 04:02:29 +00:00
with self :
renderables = self . _collect_renderables (
objects ,
sep ,
end ,
justify = justify ,
emoji = emoji ,
markup = markup ,
highlight = highlight ,
)
if style is not None :
renderables = [ Styled ( renderable , style ) for renderable in renderables ]
2022-01-24 04:07:52 +00:00
filename , line_no , locals = self . _caller_frame_info ( _stack_offset )
link_path = None if filename . startswith ( " < " ) else os . path . abspath ( filename )
path = filename . rpartition ( os . sep ) [ - 1 ]
2021-04-13 04:02:29 +00:00
if log_locals :
locals_map = {
key : value
2022-01-24 04:07:52 +00:00
for key , value in locals . items ( )
2021-04-13 04:02:29 +00:00
if not key . startswith ( " __ " )
}
renderables . append ( render_scope ( locals_map , title = " [i]locals " ) )
renderables = [
self . _log_render (
self ,
renderables ,
log_time = self . get_datetime ( ) ,
path = path ,
line_no = line_no ,
link_path = link_path ,
)
]
2022-01-24 04:07:52 +00:00
for hook in render_hooks :
2021-04-13 04:02:29 +00:00
renderables = hook . process_renderables ( renderables )
new_segments : List [ Segment ] = [ ]
extend = new_segments . extend
render = self . render
render_options = self . options
for renderable in renderables :
extend ( render ( renderable , render_options ) )
buffer_extend = self . _buffer . extend
for line in Segment . split_and_crop_lines (
new_segments , self . width , pad = False
) :
buffer_extend ( line )
def _check_buffer ( self ) - > None :
2022-11-07 18:06:49 +00:00
""" Check if the buffer may be rendered. Render it if it can (e.g. Console.quiet is False)
Rendering is supported on Windows , Unix and Jupyter environments . For
legacy Windows consoles , the win32 API is called directly .
This method will also record what it renders if recording is enabled via Console . record .
"""
2021-04-13 04:02:29 +00:00
if self . quiet :
del self . _buffer [ : ]
return
with self . _lock :
2022-11-07 18:06:49 +00:00
if self . record :
with self . _record_buffer_lock :
self . _record_buffer . extend ( self . _buffer [ : ] )
2021-04-13 04:02:29 +00:00
if self . _buffer_index == 0 :
2022-11-07 18:06:49 +00:00
2021-04-13 04:02:29 +00:00
if self . is_jupyter : # pragma: no cover
from . jupyter import display
display ( self . _buffer , self . _render_buffer ( self . _buffer [ : ] ) )
del self . _buffer [ : ]
else :
2022-11-07 18:06:49 +00:00
if WINDOWS :
use_legacy_windows_render = False
if self . legacy_windows :
try :
use_legacy_windows_render = (
self . file . fileno ( ) in _STD_STREAMS_OUTPUT
)
except ( ValueError , io . UnsupportedOperation ) :
pass
if use_legacy_windows_render :
from rich . _win32_console import LegacyWindowsTerm
from rich . _windows_renderer import legacy_windows_render
buffer = self . _buffer [ : ]
if self . no_color and self . _color_system :
buffer = list ( Segment . remove_color ( buffer ) )
legacy_windows_render ( buffer , LegacyWindowsTerm ( self . file ) )
else :
# Either a non-std stream on legacy Windows, or modern Windows.
text = self . _render_buffer ( self . _buffer [ : ] )
# https://bugs.python.org/issue37871
write = self . file . write
for line in text . splitlines ( True ) :
try :
2021-04-13 04:02:29 +00:00
write ( line )
2022-11-07 18:06:49 +00:00
except UnicodeEncodeError as error :
error . reason = f " { error . reason } \n *** You may need to add PYTHONIOENCODING=utf-8 to your environment *** "
raise
else :
text = self . _render_buffer ( self . _buffer [ : ] )
try :
self . file . write ( text )
2021-04-13 04:02:29 +00:00
except UnicodeEncodeError as error :
error . reason = f " { error . reason } \n *** You may need to add PYTHONIOENCODING=utf-8 to your environment *** "
raise
2022-11-07 18:06:49 +00:00
self . file . flush ( )
del self . _buffer [ : ]
2021-04-13 04:02:29 +00:00
def _render_buffer ( self , buffer : Iterable [ Segment ] ) - > str :
""" Render buffered output, and clear buffer. """
output : List [ str ] = [ ]
append = output . append
color_system = self . _color_system
legacy_windows = self . legacy_windows
not_terminal = not self . is_terminal
if self . no_color and color_system :
buffer = Segment . remove_color ( buffer )
for text , style , control in buffer :
if style :
append (
style . render (
text ,
color_system = color_system ,
legacy_windows = legacy_windows ,
)
)
elif not ( not_terminal and control ) :
append ( text )
rendered = " " . join ( output )
return rendered
def input (
self ,
prompt : TextType = " " ,
* ,
markup : bool = True ,
emoji : bool = True ,
password : bool = False ,
2022-01-24 04:07:52 +00:00
stream : Optional [ TextIO ] = None ,
2021-04-13 04:02:29 +00:00
) - > str :
""" Displays a prompt and waits for input from the user. The prompt may contain color / style.
2022-01-24 04:07:52 +00:00
It works in the same way as Python ' s builtin :func:`input` function and provides elaborate line editing and history features if Python ' s builtin : mod : ` readline ` module is previously loaded .
2021-04-13 04:02:29 +00:00
Args :
prompt ( Union [ str , Text ] ) : Text to render in the prompt .
markup ( bool , optional ) : Enable console markup ( requires a str prompt ) . Defaults to True .
emoji ( bool , optional ) : Enable emoji ( requires a str prompt ) . Defaults to True .
password : ( bool , optional ) : Hide typed text . Defaults to False .
stream : ( TextIO , optional ) : Optional file to read input from ( rather than stdin ) . Defaults to None .
Returns :
str : Text read from stdin .
"""
if prompt :
2022-11-07 18:06:49 +00:00
self . print ( prompt , markup = markup , emoji = emoji , end = " " )
2021-04-13 04:02:29 +00:00
if password :
2022-11-07 18:06:49 +00:00
result = getpass ( " " , stream = stream )
2021-04-13 04:02:29 +00:00
else :
if stream :
result = stream . readline ( )
else :
2022-11-07 18:06:49 +00:00
result = input ( )
2021-04-13 04:02:29 +00:00
return result
def export_text ( self , * , clear : bool = True , styles : bool = False ) - > str :
""" Generate text from console contents (requires record=True argument in constructor).
Args :
clear ( bool , optional ) : Clear record buffer after exporting . Defaults to ` ` True ` ` .
styles ( bool , optional ) : If ` ` True ` ` , ansi escape codes will be included . ` ` False ` ` for plain text .
Defaults to ` ` False ` ` .
Returns :
str : String containing console contents .
"""
assert (
self . record
) , " To export console contents set record=True in the constructor or instance "
with self . _record_buffer_lock :
if styles :
text = " " . join (
( style . render ( text ) if style else text )
for text , style , _ in self . _record_buffer
)
else :
text = " " . join (
segment . text
for segment in self . _record_buffer
if not segment . control
)
if clear :
del self . _record_buffer [ : ]
return text
def save_text ( self , path : str , * , clear : bool = True , styles : bool = False ) - > None :
""" Generate text from console and save to a given location (requires record=True argument in constructor).
Args :
path ( str ) : Path to write text files .
clear ( bool , optional ) : Clear record buffer after exporting . Defaults to ` ` True ` ` .
styles ( bool , optional ) : If ` ` True ` ` , ansi style codes will be included . ` ` False ` ` for plain text .
Defaults to ` ` False ` ` .
"""
text = self . export_text ( clear = clear , styles = styles )
with open ( path , " wt " , encoding = " utf-8 " ) as write_file :
write_file . write ( text )
def export_html (
self ,
* ,
2022-01-24 04:07:52 +00:00
theme : Optional [ TerminalTheme ] = None ,
2021-04-13 04:02:29 +00:00
clear : bool = True ,
2022-01-24 04:07:52 +00:00
code_format : Optional [ str ] = None ,
2021-04-13 04:02:29 +00:00
inline_styles : bool = False ,
) - > str :
""" Generate HTML from console contents (requires record=True argument in constructor).
Args :
theme ( TerminalTheme , optional ) : TerminalTheme object containing console colors .
clear ( bool , optional ) : Clear record buffer after exporting . Defaults to ` ` True ` ` .
2022-11-07 18:06:49 +00:00
code_format ( str , optional ) : Format string to render HTML . In addition to ' {foreground} ' ,
' {background} ' , and ' {code} ' , should contain ' {stylesheet} ' if inline_styles is ` ` False ` ` .
2021-04-13 04:02:29 +00:00
inline_styles ( bool , optional ) : If ` ` True ` ` styles will be inlined in to spans , which makes files
larger but easier to cut and paste markup . If ` ` False ` ` , styles will be embedded in a style tag .
Defaults to False .
Returns :
str : String containing console contents as HTML .
"""
assert (
self . record
) , " To export console contents set record=True in the constructor or instance "
fragments : List [ str ] = [ ]
append = fragments . append
_theme = theme or DEFAULT_TERMINAL_THEME
stylesheet = " "
render_code_format = CONSOLE_HTML_FORMAT if code_format is None else code_format
with self . _record_buffer_lock :
if inline_styles :
for text , style , _ in Segment . filter_control (
Segment . simplify ( self . _record_buffer )
) :
text = escape ( text )
if style :
rule = style . get_html_style ( _theme )
if style . link :
text = f ' <a href= " { style . link } " > { text } </a> '
text = f ' <span style= " { rule } " > { text } </span> ' if rule else text
append ( text )
else :
styles : Dict [ str , int ] = { }
for text , style , _ in Segment . filter_control (
Segment . simplify ( self . _record_buffer )
) :
text = escape ( text )
if style :
rule = style . get_html_style ( _theme )
style_number = styles . setdefault ( rule , len ( styles ) + 1 )
if style . link :
text = f ' <a class= " r { style_number } " href= " { style . link } " > { text } </a> '
else :
text = f ' <span class= " r { style_number } " > { text } </span> '
append ( text )
stylesheet_rules : List [ str ] = [ ]
stylesheet_append = stylesheet_rules . append
for style_rule , style_number in styles . items ( ) :
if style_rule :
stylesheet_append ( f " .r { style_number } {{ { style_rule } }} " )
stylesheet = " \n " . join ( stylesheet_rules )
rendered_code = render_code_format . format (
code = " " . join ( fragments ) ,
stylesheet = stylesheet ,
foreground = _theme . foreground_color . hex ,
background = _theme . background_color . hex ,
)
if clear :
del self . _record_buffer [ : ]
return rendered_code
def save_html (
self ,
path : str ,
* ,
2022-01-24 04:07:52 +00:00
theme : Optional [ TerminalTheme ] = None ,
2021-04-13 04:02:29 +00:00
clear : bool = True ,
2022-01-24 04:07:52 +00:00
code_format : str = CONSOLE_HTML_FORMAT ,
2021-04-13 04:02:29 +00:00
inline_styles : bool = False ,
) - > None :
""" Generate HTML from console contents and write to a file (requires record=True argument in constructor).
Args :
path ( str ) : Path to write html file .
theme ( TerminalTheme , optional ) : TerminalTheme object containing console colors .
clear ( bool , optional ) : Clear record buffer after exporting . Defaults to ` ` True ` ` .
2022-11-07 18:06:49 +00:00
code_format ( str , optional ) : Format string to render HTML . In addition to ' {foreground} ' ,
' {background} ' , and ' {code} ' , should contain ' {stylesheet} ' if inline_styles is ` ` False ` ` .
2021-04-13 04:02:29 +00:00
inline_styles ( bool , optional ) : If ` ` True ` ` styles will be inlined in to spans , which makes files
larger but easier to cut and paste markup . If ` ` False ` ` , styles will be embedded in a style tag .
Defaults to False .
"""
html = self . export_html (
theme = theme ,
clear = clear ,
code_format = code_format ,
inline_styles = inline_styles ,
)
with open ( path , " wt " , encoding = " utf-8 " ) as write_file :
write_file . write ( html )
2022-11-07 18:06:49 +00:00
def export_svg (
self ,
* ,
title : str = " Rich " ,
theme : Optional [ TerminalTheme ] = None ,
clear : bool = True ,
code_format : str = CONSOLE_SVG_FORMAT ,
font_aspect_ratio : float = 0.61 ,
unique_id : Optional [ str ] = None ,
) - > str :
"""
Generate an SVG from the console contents ( requires record = True in Console constructor ) .
Args :
title ( str , optional ) : The title of the tab in the output image
theme ( TerminalTheme , optional ) : The ` ` TerminalTheme ` ` object to use to style the terminal
clear ( bool , optional ) : Clear record buffer after exporting . Defaults to ` ` True ` `
code_format ( str , optional ) : Format string used to generate the SVG . Rich will inject a number of variables
into the string in order to form the final SVG output . The default template used and the variables
injected by Rich can be found by inspecting the ` ` console . CONSOLE_SVG_FORMAT ` ` variable .
font_aspect_ratio ( float , optional ) : The width to height ratio of the font used in the ` ` code_format ` `
string . Defaults to 0.61 , which is the width to height ratio of Fira Code ( the default font ) .
If you aren ' t specifying a different font inside ``code_format``, you probably don ' t need this .
unique_id ( str , optional ) : unique id that is used as the prefix for various elements ( CSS styles , node
ids ) . If not set , this defaults to a computed value based on the recorded content .
"""
from rich . cells import cell_len
style_cache : Dict [ Style , str ] = { }
def get_svg_style ( style : Style ) - > str :
""" Convert a Style to CSS rules for SVG. """
if style in style_cache :
return style_cache [ style ]
css_rules = [ ]
color = (
_theme . foreground_color
if ( style . color is None or style . color . is_default )
else style . color . get_truecolor ( _theme )
)
bgcolor = (
_theme . background_color
if ( style . bgcolor is None or style . bgcolor . is_default )
else style . bgcolor . get_truecolor ( _theme )
)
if style . reverse :
color , bgcolor = bgcolor , color
if style . dim :
color = blend_rgb ( color , bgcolor , 0.4 )
css_rules . append ( f " fill: { color . hex } " )
if style . bold :
css_rules . append ( " font-weight: bold " )
if style . italic :
css_rules . append ( " font-style: italic; " )
if style . underline :
css_rules . append ( " text-decoration: underline; " )
if style . strike :
css_rules . append ( " text-decoration: line-through; " )
css = " ; " . join ( css_rules )
style_cache [ style ] = css
return css
_theme = theme or SVG_EXPORT_THEME
width = self . width
char_height = 20
char_width = char_height * font_aspect_ratio
line_height = char_height * 1.22
margin_top = 1
margin_right = 1
margin_bottom = 1
margin_left = 1
padding_top = 40
padding_right = 8
padding_bottom = 8
padding_left = 8
padding_width = padding_left + padding_right
padding_height = padding_top + padding_bottom
margin_width = margin_left + margin_right
margin_height = margin_top + margin_bottom
text_backgrounds : List [ str ] = [ ]
text_group : List [ str ] = [ ]
classes : Dict [ str , int ] = { }
style_no = 1
def escape_text ( text : str ) - > str :
""" HTML escape text and replace spaces with nbsp. """
return escape ( text ) . replace ( " " , "   " )
def make_tag (
name : str , content : Optional [ str ] = None , * * attribs : object
) - > str :
""" Make a tag from name, content, and attributes. """
def stringify ( value : object ) - > str :
if isinstance ( value , ( float ) ) :
return format ( value , " g " )
return str ( value )
tag_attribs = " " . join (
f ' { k . lstrip ( " _ " ) . replace ( " _ " , " - " ) } = " { stringify ( v ) } " '
for k , v in attribs . items ( )
)
return (
f " < { name } { tag_attribs } > { content } </ { name } > "
if content
else f " < { name } { tag_attribs } /> "
)
with self . _record_buffer_lock :
segments = list ( Segment . filter_control ( self . _record_buffer ) )
if clear :
self . _record_buffer . clear ( )
if unique_id is None :
unique_id = " terminal- " + str (
zlib . adler32 (
( " " . join ( repr ( segment ) for segment in segments ) ) . encode (
" utf-8 " ,
" ignore " ,
)
+ title . encode ( " utf-8 " , " ignore " )
)
)
y = 0
for y , line in enumerate ( Segment . split_and_crop_lines ( segments , length = width ) ) :
x = 0
for text , style , _control in line :
style = style or Style ( )
rules = get_svg_style ( style )
if rules not in classes :
classes [ rules ] = style_no
style_no + = 1
class_name = f " r { classes [ rules ] } "
if style . reverse :
has_background = True
background = (
_theme . foreground_color . hex
if style . color is None
else style . color . get_truecolor ( _theme ) . hex
)
else :
bgcolor = style . bgcolor
has_background = bgcolor is not None and not bgcolor . is_default
background = (
_theme . background_color . hex
if style . bgcolor is None
else style . bgcolor . get_truecolor ( _theme ) . hex
)
text_length = cell_len ( text )
if has_background :
text_backgrounds . append (
make_tag (
" rect " ,
fill = background ,
x = x * char_width ,
y = y * line_height + 1.5 ,
width = char_width * text_length ,
height = line_height + 0.25 ,
shape_rendering = " crispEdges " ,
)
)
if text != " " * len ( text ) :
text_group . append (
make_tag (
" text " ,
escape_text ( text ) ,
_class = f " { unique_id } - { class_name } " ,
x = x * char_width ,
y = y * line_height + char_height ,
textLength = char_width * len ( text ) ,
clip_path = f " url(# { unique_id } -line- { y } ) " ,
)
)
x + = cell_len ( text )
line_offsets = [ line_no * line_height + 1.5 for line_no in range ( y ) ]
lines = " \n " . join (
f """ <clipPath id= " { unique_id } -line- { line_no } " >
{ make_tag ( " rect " , x = 0 , y = offset , width = char_width * width , height = line_height + 0.25 ) }
< / clipPath > """
for line_no , offset in enumerate ( line_offsets )
)
styles = " \n " . join (
f " . { unique_id } -r { rule_no } {{ { css } }} " for css , rule_no in classes . items ( )
)
backgrounds = " " . join ( text_backgrounds )
matrix = " " . join ( text_group )
terminal_width = ceil ( width * char_width + padding_width )
terminal_height = ( y + 1 ) * line_height + padding_height
chrome = make_tag (
" rect " ,
fill = _theme . background_color . hex ,
stroke = " rgba(255,255,255,0.35) " ,
stroke_width = " 1 " ,
x = margin_left ,
y = margin_top ,
width = terminal_width ,
height = terminal_height ,
rx = 8 ,
)
title_color = _theme . foreground_color . hex
if title :
chrome + = make_tag (
" text " ,
escape_text ( title ) ,
_class = f " { unique_id } -title " ,
fill = title_color ,
text_anchor = " middle " ,
x = terminal_width / / 2 ,
y = margin_top + char_height + 6 ,
)
chrome + = f """
< g transform = " translate(26,22) " >
< circle cx = " 0 " cy = " 0 " r = " 7 " fill = " #ff5f57 " / >
< circle cx = " 22 " cy = " 0 " r = " 7 " fill = " #febc2e " / >
< circle cx = " 44 " cy = " 0 " r = " 7 " fill = " #28c840 " / >
< / g >
"""
svg = code_format . format (
unique_id = unique_id ,
char_width = char_width ,
char_height = char_height ,
line_height = line_height ,
terminal_width = char_width * width - 1 ,
terminal_height = ( y + 1 ) * line_height - 1 ,
width = terminal_width + margin_width ,
height = terminal_height + margin_height ,
terminal_x = margin_left + padding_left ,
terminal_y = margin_top + padding_top ,
styles = styles ,
chrome = chrome ,
backgrounds = backgrounds ,
matrix = matrix ,
lines = lines ,
)
return svg
def save_svg (
self ,
path : str ,
* ,
title : str = " Rich " ,
theme : Optional [ TerminalTheme ] = None ,
clear : bool = True ,
code_format : str = CONSOLE_SVG_FORMAT ,
font_aspect_ratio : float = 0.61 ,
unique_id : Optional [ str ] = None ,
) - > None :
""" Generate an SVG file from the console contents (requires record=True in Console constructor).
Args :
path ( str ) : The path to write the SVG to .
title ( str , optional ) : The title of the tab in the output image
theme ( TerminalTheme , optional ) : The ` ` TerminalTheme ` ` object to use to style the terminal
clear ( bool , optional ) : Clear record buffer after exporting . Defaults to ` ` True ` `
code_format ( str , optional ) : Format string used to generate the SVG . Rich will inject a number of variables
into the string in order to form the final SVG output . The default template used and the variables
injected by Rich can be found by inspecting the ` ` console . CONSOLE_SVG_FORMAT ` ` variable .
font_aspect_ratio ( float , optional ) : The width to height ratio of the font used in the ` ` code_format ` `
string . Defaults to 0.61 , which is the width to height ratio of Fira Code ( the default font ) .
If you aren ' t specifying a different font inside ``code_format``, you probably don ' t need this .
unique_id ( str , optional ) : unique id that is used as the prefix for various elements ( CSS styles , node
ids ) . If not set , this defaults to a computed value based on the recorded content .
"""
svg = self . export_svg (
title = title ,
theme = theme ,
clear = clear ,
code_format = code_format ,
font_aspect_ratio = font_aspect_ratio ,
unique_id = unique_id ,
)
with open ( path , " wt " , encoding = " utf-8 " ) as write_file :
write_file . write ( svg )
def _svg_hash ( svg_main_code : str ) - > str :
""" Returns a unique hash for the given SVG main code.
Args :
svg_main_code ( str ) : The content we ' re going to inject in the SVG envelope.
Returns :
str : a hash of the given content
"""
return str ( zlib . adler32 ( svg_main_code . encode ( ) ) )
2021-04-13 04:02:29 +00:00
if __name__ == " __main__ " : # pragma: no cover
2022-11-07 18:06:49 +00:00
console = Console ( record = True )
2021-04-13 04:02:29 +00:00
console . log (
" JSONRPC [i]request[/i] " ,
5 ,
1.3 ,
True ,
False ,
None ,
{
" jsonrpc " : " 2.0 " ,
" method " : " subtract " ,
" params " : { " minuend " : 42 , " subtrahend " : 23 } ,
" id " : 3 ,
} ,
)
console . log ( " Hello, World! " , " { ' a ' : 1} " , repr ( console ) )
console . print (
{
" name " : None ,
" empty " : [ ] ,
" quiz " : {
" sport " : {
" answered " : True ,
" q1 " : {
" question " : " Which one is correct team name in NBA? " ,
" options " : [
" New York Bulls " ,
" Los Angeles Kings " ,
" Golden State Warriors " ,
" Huston Rocket " ,
] ,
" answer " : " Huston Rocket " ,
} ,
} ,
" maths " : {
" answered " : False ,
" q1 " : {
" question " : " 5 + 7 = ? " ,
" options " : [ 10 , 11 , 12 , 13 ] ,
" answer " : 12 ,
} ,
" q2 " : {
" question " : " 12 - 8 = ? " ,
" options " : [ 1 , 2 , 3 , 4 ] ,
" answer " : 4 ,
} ,
} ,
} ,
}
)