123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211 |
- import inspect
- import os
- import platform
- import sys
- import threading
- 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 threading import RLock
- 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,
- )
- if sys.version_info >= (3, 8):
- from typing import Literal, Protocol, runtime_checkable
- else:
- from pip._vendor.typing_extensions import (
- Literal,
- Protocol,
- runtime_checkable,
- ) # pragma: no cover
- from . import errors, themes
- from ._emoji_replace import _emoji_replace
- from ._log_render import FormatTimeCallable, LogRender
- from .align import Align, AlignMethod
- from .color import ColorSystem
- 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, 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
- 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()
- CONSOLE_HTML_FORMAT = """\
- <!DOCTYPE html>
- <head>
- <meta charset="UTF-8">
- <style>
- {stylesheet}
- body {{
- color: {foreground};
- background-color: {background};
- }}
- </style>
- </head>
- <html>
- <body>
- <code>
- <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
- </code>
- </body>
- </html>
- """
- _TERM_COLORS = {"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 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", 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]
- # 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
- except NameError:
- return False
- ipython = get_ipython() # type: ignore
- shell = ipython.__class__.__name__
- if "google.colab" in str(ipython.__class__) 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
- if detect_legacy_windows(): # pragma: no cover
- from pip._vendor.colorama import init
- init(strip=False)
- 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` 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:
- width = width or 93
- height = height or 100
- self.soft_wrap = soft_wrap
- self._width = width
- self._height = height
- 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 = 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} {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)
- 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
- 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 OSError: # Probably not a terminal
- pass
- else:
- try:
- width, height = os.get_terminal_size(sys.__stdin__.fileno())
- except (AttributeError, ValueError, OSError):
- try:
- width, height = os.get_terminal_size(sys.__stdout__.fileno())
- except (AttributeError, ValueError, OSError):
- pass
- 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: 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.
- """
- if self.is_terminal and not self.legacy_windows:
- 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 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
- 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
- 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)
- lines = list(
- islice(
- Segment.split_and_crop_lines(
- _rendered,
- render_options.max_width,
- include_new_lines=new_lines,
- pad=pad,
- ),
- None,
- render_options.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 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 it's 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))
- del text[:]
- 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 = True,
- 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 pip._vendor.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 88.
- 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:
- 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``.
- 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(),)
- 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."""
- if self.quiet:
- del self._buffer[:]
- return
- with self._lock:
- 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:
- text = self._render_buffer(self._buffer[:])
- del self._buffer[:]
- if text:
- try:
- if WINDOWS: # pragma: no cover
- # https://bugs.python.org/issue37871
- write = self.file.write
- for line in text.splitlines(True):
- write(line)
- else:
- self.file.write(text)
- self.file.flush()
- except UnicodeEncodeError as error:
- error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
- raise
- 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
- if self.record:
- with self._record_buffer_lock:
- self._record_buffer.extend(buffer)
- 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.
- """
- prompt_str = ""
- if prompt:
- with self.capture() as capture:
- self.print(prompt, markup=markup, emoji=emoji, end="")
- prompt_str = capture.get()
- if self.legacy_windows:
- # Legacy windows doesn't like ANSI codes in getpass or input (colorama bug)?
- self.file.write(prompt_str)
- prompt_str = ""
- if password:
- result = getpass(prompt_str, stream=stream)
- else:
- if stream:
- self.file.write(prompt_str)
- result = stream.readline()
- else:
- result = input(prompt_str)
- 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, should contain {foreground}
- {background} and {code}.
- 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, should contain {foreground}
- {background} and {code}.
- 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)
- if __name__ == "__main__": # pragma: no cover
- console = Console()
- 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,
- },
- },
- },
- }
- )
- console.log("foo")
|