summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/rich/console.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/rich/console.py')
-rw-r--r--venv/lib/python3.11/site-packages/rich/console.py2633
1 files changed, 0 insertions, 2633 deletions
diff --git a/venv/lib/python3.11/site-packages/rich/console.py b/venv/lib/python3.11/site-packages/rich/console.py
deleted file mode 100644
index 1232cd5..0000000
--- a/venv/lib/python3.11/site-packages/rich/console.py
+++ /dev/null
@@ -1,2633 +0,0 @@
-import inspect
-import os
-import platform
-import sys
-import threading
-import zlib
-from abc import ABC, abstractmethod
-from dataclasses import dataclass, field
-from datetime import datetime
-from functools import wraps
-from getpass import getpass
-from html import escape
-from inspect import isclass
-from itertools import islice
-from math import ceil
-from time import monotonic
-from types import FrameType, ModuleType, TracebackType
-from typing import (
- IO,
- TYPE_CHECKING,
- Any,
- Callable,
- Dict,
- Iterable,
- List,
- Mapping,
- NamedTuple,
- Optional,
- TextIO,
- Tuple,
- Type,
- Union,
- cast,
-)
-
-from rich._null_file import NULL_FILE
-
-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
-
-from . import errors, themes
-from ._emoji_replace import _emoji_replace
-from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT
-from ._fileno import get_fileno
-from ._log_render import FormatTimeCallable, LogRender
-from .align import Align, AlignMethod
-from .color import ColorSystem, blend_rgb
-from .control import Control
-from .emoji import EmojiVariant
-from .highlighter import NullHighlighter, ReprHighlighter
-from .markup import render as render_markup
-from .measure import Measurement, measure_renderables
-from .pager import Pager, SystemPager
-from .pretty import Pretty, is_expandable
-from .protocol import rich_cast
-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
-from .terminal_theme import DEFAULT_TERMINAL_THEME, SVG_EXPORT_THEME, TerminalTheme
-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
-
-JUPYTER_DEFAULT_COLUMNS = 115
-JUPYTER_DEFAULT_LINES = 100
-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()
-
-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,
-}
-
-
-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."""
- max_height: int
- """Height of container (starts as terminal)"""
- 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.
- """
- options: ConsoleOptions = ConsoleOptions.__new__(ConsoleOptions)
- 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):
- if height is not None:
- options.max_height = height
- 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
-
- 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
-
- 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
-
- 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)
- options.height = options.max_height = height
- return options
-
-
-@runtime_checkable
-class RichCast(Protocol):
- """An object that may be 'cast' to a console renderable."""
-
- def __rich__(
- self,
- ) -> Union["ConsoleRenderable", "RichCast", str]: # pragma: no cover
- ...
-
-
-@runtime_checkable
-class ConsoleRenderable(Protocol):
- """An object that supports the console protocol."""
-
- def __rich_console__(
- self, console: "Console", options: "ConsoleOptions"
- ) -> "RenderResult": # pragma: no cover
- ...
-
-
-# A type that may be rendered by Console.
-RenderableType = Union[ConsoleRenderable, RichCast, str]
-"""A string or any object that may be rendered by Rich."""
-
-# The result of calling a __rich_console__ method.
-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
-
- def __exit__(
- self,
- exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType],
- ) -> None:
- 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
-
- def __exit__(
- self,
- exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType],
- ) -> None:
- 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",
- pager: Optional[Pager] = None,
- 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
-
- def __exit__(
- self,
- exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType],
- ) -> None:
- 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
-
- def update(
- self, *renderables: RenderableType, style: Optional[StyleType] = None
- ) -> None:
- """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 = (
- Group(*renderables) if len(renderables) > 1 else renderables[0]
- )
- 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
-
- def __exit__(
- self,
- exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType],
- ) -> None:
- if self._changed:
- self.console.set_alt_screen(False)
- if self.hide_cursor:
- self.console.show_cursor(True)
-
-
-class Group:
- """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
-
-
-def group(fit: bool = True) -> Callable[..., Callable[..., Group]]:
- """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.
- """
-
- def decorator(
- method: Callable[..., Iterable[RenderableType]]
- ) -> Callable[..., Group]:
- """Convert a method that returns an iterable of renderables in to a Group."""
-
- @wraps(method)
- def _replace(*args: Any, **kwargs: Any) -> Group:
- renderables = method(*args, **kwargs)
- return Group(*renderables, fit=fit)
-
- return _replace
-
- return decorator
-
-
-def _is_jupyter() -> bool: # pragma: no cover
- """Check if we're running in a Jupyter notebook."""
- try:
- get_ipython # type: ignore[name-defined]
- except NameError:
- return False
- ipython = get_ipython() # type: ignore[name-defined]
- shell = ipython.__class__.__name__
- if (
- "google.colab" in str(ipython.__class__)
- or os.getenv("DATABRICKS_RUNTIME_VERSION")
- or shell == "ZMQInteractiveShell"
- ):
- 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,
- required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.
- markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True.
- emoji (bool, optional): Enable emoji code. Defaults to True.
- emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None.
- 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",
- force_terminal: Optional[bool] = None,
- force_jupyter: Optional[bool] = None,
- force_interactive: Optional[bool] = None,
- soft_wrap: bool = False,
- theme: Optional[Theme] = None,
- stderr: bool = False,
- file: Optional[IO[str]] = None,
- quiet: bool = False,
- width: Optional[int] = None,
- height: Optional[int] = None,
- style: Optional[StyleType] = None,
- no_color: Optional[bool] = None,
- tab_size: int = 8,
- record: bool = False,
- markup: bool = True,
- emoji: bool = True,
- emoji_variant: Optional[EmojiVariant] = None,
- highlight: bool = True,
- log_time: bool = True,
- log_path: bool = True,
- log_time_format: Union[str, FormatTimeCallable] = "[%X]",
- highlighter: Optional["HighlighterType"] = ReprHighlighter(),
- legacy_windows: Optional[bool] = None,
- safe_box: bool = True,
- get_datetime: Optional[Callable[[], datetime]] = None,
- get_time: Optional[Callable[[], float]] = None,
- _environ: Optional[Mapping[str, str]] = None,
- ):
- # 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:
- 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
-
- self.tab_size = tab_size
- self.record = record
- self._markup = markup
- self._emoji = emoji
- self._emoji_variant: Optional[EmojiVariant] = emoji_variant
- self._highlight = highlight
- self.legacy_windows: bool = (
- (detect_legacy_windows() and not self.is_jupyter)
- if legacy_windows is None
- else legacy_windows
- )
-
- 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
-
- self._color_system: Optional[ColorSystem]
-
- self._force_terminal = None
- if force_terminal is not None:
- self._force_terminal = force_terminal
-
- 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} {self._color_system!s}>"
-
- @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)
- if file is None:
- file = NULL_FILE
- 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()
- _term_name, _hyphen, colors = term.rpartition("-")
- 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.
- """
- with self._lock:
- self._render_hooks.append(hook)
-
- def pop_render_hook(self) -> None:
- """Pop the last renderhook from the stack."""
- with self._lock:
- self._render_hooks.pop()
-
- def __enter__(self) -> "Console":
- """Own context manager to enter buffer context."""
- self._enter_buffer()
- return self
-
- def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
- """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
-
- 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
-
- if self.is_jupyter:
- # return False for Jupyter, which may have FORCE_COLOR set
- return False
-
- # If FORCE_COLOR env var has any value at all, we assume a terminal.
- force_color = self._environ.get("FORCE_COLOR")
- if force_color is not None:
- self._force_terminal = True
- return True
-
- 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
-
- @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(
- max_height=self.size.height,
- 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:
- return ConsoleDimensions(self._width - self.legacy_windows, self._height)
-
- if self.is_dumb_terminal:
- return ConsoleDimensions(80, 25)
-
- width: Optional[int] = None
- height: Optional[int] = None
-
- if WINDOWS: # pragma: no cover
- try:
- width, height = os.get_terminal_size()
- except (AttributeError, ValueError, OSError): # Probably not a terminal
- pass
- else:
- for file_descriptor in _STD_STREAMS:
- try:
- width, height = os.get_terminal_size(file_descriptor)
- except (AttributeError, ValueError, OSError):
- pass
- else:
- break
-
- 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)
-
- # get_terminal_size can report 0, 0 if run from pseudo-terminal
- width = width or 80
- height = height or 25
- return ConsoleDimensions(
- width - self.legacy_windows if self._width is None else self._width,
- height if self._height is None else self._height,
- )
-
- @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
-
- @property
- def width(self) -> int:
- """Get the width of the console.
-
- Returns:
- int: The width (in characters) of the console.
- """
- return self.size.width
-
- @width.setter
- def width(self, width: int) -> None:
- """Set width.
-
- Args:
- width (int): New width.
- """
- self._width = width
-
- @property
- def height(self) -> int:
- """Get the height of the console.
-
- Returns:
- int: The height (in lines) of the console.
- """
- return self.size.height
-
- @height.setter
- def height(self, height: int) -> None:
- """Set height.
-
- Args:
- height (int): new height.
- """
- self._height = height
-
- 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(
- self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False
- ) -> 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:
- pager (Pager, optional): A pager object, or None to use :class:`~rich.pager.SystemPager`. Defaults to None.
- 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: StyleType = "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.
- """
- if self.is_terminal:
- 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
-
- 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
-
- def screen(
- self, hide_cursor: bool = True, style: Optional[StyleType] = None
- ) -> "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 "")
-
- 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
-
- def render(
- self, renderable: RenderableType, options: Optional[ConsoleOptions] = None
- ) -> 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
-
- renderable = rich_cast(renderable)
- if hasattr(renderable, "__rich_console__") and not isclass(renderable):
- render_iterable = renderable.__rich_console__(self, _options) # type: ignore[union-attr]
- elif isinstance(renderable, str):
- text_renderable = self.render_str(
- renderable, highlight=_options.highlight, markup=_options.markup
- )
- render_iterable = text_renderable.__rich_console__(self, _options)
- 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
- _options = _options.reset_height()
- 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)
-
- render_height = render_options.height
- if render_height is not None:
- render_height = max(0, render_height)
-
- lines = list(
- islice(
- Segment.split_and_crop_lines(
- _rendered,
- render_options.max_width,
- include_new_lines=new_lines,
- pad=pad,
- style=style,
- ),
- None,
- render_height,
- )
- )
- 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] = "",
- justify: Optional[JustifyMethod] = None,
- overflow: Optional[OverflowMethod] = None,
- emoji: Optional[bool] = None,
- markup: Optional[bool] = None,
- highlight: Optional[bool] = None,
- highlighter: Optional[HighlighterType] = None,
- ) -> "Text":
- """Convert a string to a Text instance. This is called automatically if
- 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:
- rich_text = render_markup(
- text,
- style=style,
- emoji=emoji_enabled,
- emoji_variant=self._emoji_variant,
- )
- rich_text.justify = justify
- rich_text.overflow = overflow
- else:
- rich_text = Text(
- _emoji_replace(text, default_variant=self._emoji_variant)
- if emoji_enabled
- else text,
- 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(
- self, name: Union[str, Style], *, default: Optional[Union[Style, str]] = None
- ) -> Style:
- """Get a Style instance by its theme name or parse a definition.
-
- 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,
- *,
- justify: Optional[JustifyMethod] = None,
- emoji: Optional[bool] = None,
- markup: Optional[bool] = None,
- highlight: Optional[bool] = None,
- ) -> 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))
- text.clear()
-
- for renderable in objects:
- renderable = rich_cast(renderable)
- if isinstance(renderable, str):
- append_text(
- self.render_str(
- renderable, emoji=emoji, markup=markup, highlighter=_highlighter
- )
- )
- elif isinstance(renderable, Text):
- append_text(renderable)
- 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:
- with self:
- self._buffer.extend(_control.segment for _control in control)
-
- def out(
- self,
- *objects: Any,
- sep: str = " ",
- end: str = "\n",
- style: Optional[Union[str, Style]] = None,
- highlight: Optional[bool] = None,
- ) -> 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,
- 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,
- crop: bool = True,
- soft_wrap: Optional[bool] = None,
- new_line_start: bool = False,
- ) -> 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.
- soft_wrap (bool, optional): Enable soft wrap mode which disables word wrapping and cropping of text or ``None`` for
- Console default. Defaults to ``None``.
- new_line_start (bool, False): Insert a new line at the start if the output contains more than one line. Defaults to ``False``.
- """
- 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
- render_hooks = self._render_hooks[:]
- with self:
- renderables = self._collect_renderables(
- objects,
- sep,
- end,
- justify=justify,
- emoji=emoji,
- markup=markup,
- highlight=highlight,
- )
- for hook in render_hooks:
- 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,
- highlight=highlight,
- )
-
- 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)
- )
- )
- if new_line_start:
- if (
- len("".join(segment.text for segment in new_segments).splitlines())
- > 1
- ):
- new_segments.insert(0, Segment.line())
- 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)
-
- def print_json(
- self,
- json: Optional[str] = None,
- *,
- data: Any = None,
- indent: Union[None, int, str] = 2,
- highlight: bool = True,
- skip_keys: bool = False,
- ensure_ascii: bool = False,
- 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)
-
- def update_screen(
- self,
- renderable: RenderableType,
- *,
- region: Optional[Region] = None,
- options: Optional[ConsoleOptions] = None,
- ) -> 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)
-
- def update_screen_lines(
- self, lines: List[List[Segment]], x: int = 0, y: int = 0
- ) -> None:
- """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,
- suppress: Iterable[Union[str, ModuleType]] = (),
- max_frames: int = 100,
- ) -> None:
- """Prints a rich render of the last exception and traceback.
-
- Args:
- width (Optional[int], optional): Number of characters used to render code. Defaults to 100.
- 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.
- 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.
- """
- from .traceback import Traceback
-
- traceback = Traceback(
- width=width,
- extra_lines=extra_lines,
- theme=theme,
- word_wrap=word_wrap,
- show_locals=show_locals,
- suppress=suppress,
- max_frames=max_frames,
- )
- self.print(traceback)
-
- @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
- while offset and frame is not None:
- 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
-
- def log(
- self,
- *objects: Any,
- 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,
- log_locals: bool = False,
- _stack_offset: int = 1,
- ) -> 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``.
- 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(),)
-
- render_hooks = self._render_hooks[:]
-
- 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]
-
- 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]
- if log_locals:
- locals_map = {
- key: value
- for key, value in locals.items()
- 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,
- )
- ]
- for hook in render_hooks:
- 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:
- """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.
- """
- if self.quiet:
- del self._buffer[:]
- return
- with self._lock:
- if self.record:
- with self._record_buffer_lock:
- self._record_buffer.extend(self._buffer[:])
-
- if self._buffer_index == 0:
- if self.is_jupyter: # pragma: no cover
- from .jupyter import display
-
- display(self._buffer, self._render_buffer(self._buffer[:]))
- del self._buffer[:]
- else:
- if WINDOWS:
- use_legacy_windows_render = False
- if self.legacy_windows:
- fileno = get_fileno(self.file)
- if fileno is not None:
- use_legacy_windows_render = (
- fileno in _STD_STREAMS_OUTPUT
- )
-
- 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
- # https://github.com/python/cpython/issues/82052
- # We need to avoid writing more than 32Kb in a single write, due to the above bug
- write = self.file.write
- # Worse case scenario, every character is 4 bytes of utf-8
- MAX_WRITE = 32 * 1024 // 4
- try:
- if len(text) <= MAX_WRITE:
- write(text)
- else:
- batch: List[str] = []
- batch_append = batch.append
- size = 0
- for line in text.splitlines(True):
- if size + len(line) > MAX_WRITE and batch:
- write("".join(batch))
- batch.clear()
- size = 0
- batch_append(line)
- size += len(line)
- if batch:
- write("".join(batch))
- batch.clear()
- 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)
- except UnicodeEncodeError as error:
- error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
- raise
-
- self.file.flush()
- del self._buffer[:]
-
- 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,
- stream: Optional[TextIO] = None,
- ) -> str:
- """Displays a prompt and waits for input from the user. The prompt may contain color / style.
-
- 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.
-
- 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:
- self.print(prompt, markup=markup, emoji=emoji, end="")
- if password:
- result = getpass("", stream=stream)
- else:
- if stream:
- result = stream.readline()
- else:
- result = input()
- 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,
- *,
- theme: Optional[TerminalTheme] = None,
- clear: bool = True,
- code_format: Optional[str] = None,
- 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``.
- code_format (str, optional): Format string to render HTML. In addition to '{foreground}',
- '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``.
- 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,
- *,
- theme: Optional[TerminalTheme] = None,
- clear: bool = True,
- code_format: str = CONSOLE_HTML_FORMAT,
- 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``.
- code_format (str, optional): Format string to render HTML. In addition to '{foreground}',
- '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``.
- 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)
-
- 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(" ", "&#160;")
-
- 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()))
-
-
-if __name__ == "__main__": # pragma: no cover
- console = Console(record=True)
-
- 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,
- },
- },
- },
- }
- )