1import inspect
2import os
3import platform
4import sys
5import threading
6from abc import ABC, abstractmethod
7from dataclasses import dataclass, field
8from datetime import datetime
9from functools import wraps
10from getpass import getpass
11from html import escape
12from inspect import isclass
13from itertools import islice
14from time import monotonic
15from threading import RLock
16from types import FrameType, TracebackType, ModuleType
17from typing import (
18    IO,
19    TYPE_CHECKING,
20    Any,
21    Callable,
22    Dict,
23    Iterable,
24    List,
25    Mapping,
26    NamedTuple,
27    Optional,
28    TextIO,
29    Tuple,
30    Type,
31    Union,
32    cast,
33)
34
35if sys.version_info >= (3, 8):
36    from typing import Literal, Protocol, runtime_checkable
37else:
38    from typing_extensions import (
39        Literal,
40        Protocol,
41        runtime_checkable,
42    )  # pragma: no cover
43
44from . import errors, themes
45from ._emoji_replace import _emoji_replace
46from ._log_render import FormatTimeCallable, LogRender
47from .align import Align, AlignMethod
48from .color import ColorSystem
49from .control import Control
50from .emoji import EmojiVariant
51from .highlighter import NullHighlighter, ReprHighlighter
52from .markup import render as render_markup
53from .measure import Measurement, measure_renderables
54from .pager import Pager, SystemPager
55from .pretty import Pretty, is_expandable
56from .protocol import rich_cast
57from .region import Region
58from .scope import render_scope
59from .screen import Screen
60from .segment import Segment
61from .style import Style, StyleType
62from .styled import Styled
63from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme
64from .text import Text, TextType
65from .theme import Theme, ThemeStack
66
67if TYPE_CHECKING:
68    from ._windows import WindowsConsoleFeatures
69    from .live import Live
70    from .status import Status
71
72WINDOWS = platform.system() == "Windows"
73
74HighlighterType = Callable[[Union[str, "Text"]], "Text"]
75JustifyMethod = Literal["default", "left", "center", "right", "full"]
76OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"]
77
78
79class NoChange:
80    pass
81
82
83NO_CHANGE = NoChange()
84
85
86CONSOLE_HTML_FORMAT = """\
87<!DOCTYPE html>
88<head>
89<meta charset="UTF-8">
90<style>
91{stylesheet}
92body {{
93    color: {foreground};
94    background-color: {background};
95}}
96</style>
97</head>
98<html>
99<body>
100    <code>
101        <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">{code}</pre>
102    </code>
103</body>
104</html>
105"""
106
107_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD}
108
109
110class ConsoleDimensions(NamedTuple):
111    """Size of the terminal."""
112
113    width: int
114    """The width of the console in 'cells'."""
115    height: int
116    """The height of the console in lines."""
117
118
119@dataclass
120class ConsoleOptions:
121    """Options for __rich_console__ method."""
122
123    size: ConsoleDimensions
124    """Size of console."""
125    legacy_windows: bool
126    """legacy_windows: flag for legacy windows."""
127    min_width: int
128    """Minimum width of renderable."""
129    max_width: int
130    """Maximum width of renderable."""
131    is_terminal: bool
132    """True if the target is a terminal, otherwise False."""
133    encoding: str
134    """Encoding of terminal."""
135    max_height: int
136    """Height of container (starts as terminal)"""
137    justify: Optional[JustifyMethod] = None
138    """Justify value override for renderable."""
139    overflow: Optional[OverflowMethod] = None
140    """Overflow value override for renderable."""
141    no_wrap: Optional[bool] = False
142    """Disable wrapping for text."""
143    highlight: Optional[bool] = None
144    """Highlight override for render_str."""
145    markup: Optional[bool] = None
146    """Enable markup when rendering strings."""
147    height: Optional[int] = None
148
149    @property
150    def ascii_only(self) -> bool:
151        """Check if renderables should use ascii only."""
152        return not self.encoding.startswith("utf")
153
154    def copy(self) -> "ConsoleOptions":
155        """Return a copy of the options.
156
157        Returns:
158            ConsoleOptions: a copy of self.
159        """
160        options: ConsoleOptions = ConsoleOptions.__new__(ConsoleOptions)
161        options.__dict__ = self.__dict__.copy()
162        return options
163
164    def update(
165        self,
166        *,
167        width: Union[int, NoChange] = NO_CHANGE,
168        min_width: Union[int, NoChange] = NO_CHANGE,
169        max_width: Union[int, NoChange] = NO_CHANGE,
170        justify: Union[Optional[JustifyMethod], NoChange] = NO_CHANGE,
171        overflow: Union[Optional[OverflowMethod], NoChange] = NO_CHANGE,
172        no_wrap: Union[Optional[bool], NoChange] = NO_CHANGE,
173        highlight: Union[Optional[bool], NoChange] = NO_CHANGE,
174        markup: Union[Optional[bool], NoChange] = NO_CHANGE,
175        height: Union[Optional[int], NoChange] = NO_CHANGE,
176    ) -> "ConsoleOptions":
177        """Update values, return a copy."""
178        options = self.copy()
179        if not isinstance(width, NoChange):
180            options.min_width = options.max_width = max(0, width)
181        if not isinstance(min_width, NoChange):
182            options.min_width = min_width
183        if not isinstance(max_width, NoChange):
184            options.max_width = max_width
185        if not isinstance(justify, NoChange):
186            options.justify = justify
187        if not isinstance(overflow, NoChange):
188            options.overflow = overflow
189        if not isinstance(no_wrap, NoChange):
190            options.no_wrap = no_wrap
191        if not isinstance(highlight, NoChange):
192            options.highlight = highlight
193        if not isinstance(markup, NoChange):
194            options.markup = markup
195        if not isinstance(height, NoChange):
196            if height is not None:
197                options.max_height = height
198            options.height = None if height is None else max(0, height)
199        return options
200
201    def update_width(self, width: int) -> "ConsoleOptions":
202        """Update just the width, return a copy.
203
204        Args:
205            width (int): New width (sets both min_width and max_width)
206
207        Returns:
208            ~ConsoleOptions: New console options instance.
209        """
210        options = self.copy()
211        options.min_width = options.max_width = max(0, width)
212        return options
213
214    def update_height(self, height: int) -> "ConsoleOptions":
215        """Update the height, and return a copy.
216
217        Args:
218            height (int): New height
219
220        Returns:
221            ~ConsoleOptions: New Console options instance.
222        """
223        options = self.copy()
224        options.max_height = options.height = height
225        return options
226
227    def update_dimensions(self, width: int, height: int) -> "ConsoleOptions":
228        """Update the width and height, and return a copy.
229
230        Args:
231            width (int): New width (sets both min_width and max_width).
232            height (int): New height.
233
234        Returns:
235            ~ConsoleOptions: New console options instance.
236        """
237        options = self.copy()
238        options.min_width = options.max_width = max(0, width)
239        options.height = options.max_height = height
240        return options
241
242
243@runtime_checkable
244class RichCast(Protocol):
245    """An object that may be 'cast' to a console renderable."""
246
247    def __rich__(self) -> Union["ConsoleRenderable", str]:  # pragma: no cover
248        ...
249
250
251@runtime_checkable
252class ConsoleRenderable(Protocol):
253    """An object that supports the console protocol."""
254
255    def __rich_console__(
256        self, console: "Console", options: "ConsoleOptions"
257    ) -> "RenderResult":  # pragma: no cover
258        ...
259
260
261RenderableType = Union[ConsoleRenderable, RichCast, str]
262"""A type that may be rendered by Console."""
263
264RenderResult = Iterable[Union[RenderableType, Segment]]
265"""The result of calling a __rich_console__ method."""
266
267
268_null_highlighter = NullHighlighter()
269
270
271class CaptureError(Exception):
272    """An error in the Capture context manager."""
273
274
275class NewLine:
276    """A renderable to generate new line(s)"""
277
278    def __init__(self, count: int = 1) -> None:
279        self.count = count
280
281    def __rich_console__(
282        self, console: "Console", options: "ConsoleOptions"
283    ) -> Iterable[Segment]:
284        yield Segment("\n" * self.count)
285
286
287class ScreenUpdate:
288    """Render a list of lines at a given offset."""
289
290    def __init__(self, lines: List[List[Segment]], x: int, y: int) -> None:
291        self._lines = lines
292        self.x = x
293        self.y = y
294
295    def __rich_console__(
296        self, console: "Console", options: ConsoleOptions
297    ) -> RenderResult:
298        x = self.x
299        move_to = Control.move_to
300        for offset, line in enumerate(self._lines, self.y):
301            yield move_to(x, offset)
302            yield from line
303
304
305class Capture:
306    """Context manager to capture the result of printing to the console.
307    See :meth:`~rich.console.Console.capture` for how to use.
308
309    Args:
310        console (Console): A console instance to capture output.
311    """
312
313    def __init__(self, console: "Console") -> None:
314        self._console = console
315        self._result: Optional[str] = None
316
317    def __enter__(self) -> "Capture":
318        self._console.begin_capture()
319        return self
320
321    def __exit__(
322        self,
323        exc_type: Optional[Type[BaseException]],
324        exc_val: Optional[BaseException],
325        exc_tb: Optional[TracebackType],
326    ) -> None:
327        self._result = self._console.end_capture()
328
329    def get(self) -> str:
330        """Get the result of the capture."""
331        if self._result is None:
332            raise CaptureError(
333                "Capture result is not available until context manager exits."
334            )
335        return self._result
336
337
338class ThemeContext:
339    """A context manager to use a temporary theme. See :meth:`~rich.console.Console.use_theme` for usage."""
340
341    def __init__(self, console: "Console", theme: Theme, inherit: bool = True) -> None:
342        self.console = console
343        self.theme = theme
344        self.inherit = inherit
345
346    def __enter__(self) -> "ThemeContext":
347        self.console.push_theme(self.theme)
348        return self
349
350    def __exit__(
351        self,
352        exc_type: Optional[Type[BaseException]],
353        exc_val: Optional[BaseException],
354        exc_tb: Optional[TracebackType],
355    ) -> None:
356        self.console.pop_theme()
357
358
359class PagerContext:
360    """A context manager that 'pages' content. See :meth:`~rich.console.Console.pager` for usage."""
361
362    def __init__(
363        self,
364        console: "Console",
365        pager: Optional[Pager] = None,
366        styles: bool = False,
367        links: bool = False,
368    ) -> None:
369        self._console = console
370        self.pager = SystemPager() if pager is None else pager
371        self.styles = styles
372        self.links = links
373
374    def __enter__(self) -> "PagerContext":
375        self._console._enter_buffer()
376        return self
377
378    def __exit__(
379        self,
380        exc_type: Optional[Type[BaseException]],
381        exc_val: Optional[BaseException],
382        exc_tb: Optional[TracebackType],
383    ) -> None:
384        if exc_type is None:
385            with self._console._lock:
386                buffer: List[Segment] = self._console._buffer[:]
387                del self._console._buffer[:]
388                segments: Iterable[Segment] = buffer
389                if not self.styles:
390                    segments = Segment.strip_styles(segments)
391                elif not self.links:
392                    segments = Segment.strip_links(segments)
393                content = self._console._render_buffer(segments)
394            self.pager.show(content)
395        self._console._exit_buffer()
396
397
398class ScreenContext:
399    """A context manager that enables an alternative screen. See :meth:`~rich.console.Console.screen` for usage."""
400
401    def __init__(
402        self, console: "Console", hide_cursor: bool, style: StyleType = ""
403    ) -> None:
404        self.console = console
405        self.hide_cursor = hide_cursor
406        self.screen = Screen(style=style)
407        self._changed = False
408
409    def update(
410        self, *renderables: RenderableType, style: Optional[StyleType] = None
411    ) -> None:
412        """Update the screen.
413
414        Args:
415            renderable (RenderableType, optional): Optional renderable to replace current renderable,
416                or None for no change. Defaults to None.
417            style: (Style, optional): Replacement style, or None for no change. Defaults to None.
418        """
419        if renderables:
420            self.screen.renderable = (
421                Group(*renderables) if len(renderables) > 1 else renderables[0]
422            )
423        if style is not None:
424            self.screen.style = style
425        self.console.print(self.screen, end="")
426
427    def __enter__(self) -> "ScreenContext":
428        self._changed = self.console.set_alt_screen(True)
429        if self._changed and self.hide_cursor:
430            self.console.show_cursor(False)
431        return self
432
433    def __exit__(
434        self,
435        exc_type: Optional[Type[BaseException]],
436        exc_val: Optional[BaseException],
437        exc_tb: Optional[TracebackType],
438    ) -> None:
439        if self._changed:
440            self.console.set_alt_screen(False)
441            if self.hide_cursor:
442                self.console.show_cursor(True)
443
444
445class Group:
446    """Takes a group of renderables and returns a renderable object that renders the group.
447
448    Args:
449        renderables (Iterable[RenderableType]): An iterable of renderable objects.
450        fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True.
451    """
452
453    def __init__(self, *renderables: "RenderableType", fit: bool = True) -> None:
454        self._renderables = renderables
455        self.fit = fit
456        self._render: Optional[List[RenderableType]] = None
457
458    @property
459    def renderables(self) -> List["RenderableType"]:
460        if self._render is None:
461            self._render = list(self._renderables)
462        return self._render
463
464    def __rich_measure__(
465        self, console: "Console", options: "ConsoleOptions"
466    ) -> "Measurement":
467        if self.fit:
468            return measure_renderables(console, options, self.renderables)
469        else:
470            return Measurement(options.max_width, options.max_width)
471
472    def __rich_console__(
473        self, console: "Console", options: "ConsoleOptions"
474    ) -> RenderResult:
475        yield from self.renderables
476
477
478RenderGroup = Group  # TODO: deprecate at some point
479
480
481def group(fit: bool = True) -> Callable[..., Callable[..., Group]]:
482    """A decorator that turns an iterable of renderables in to a group.
483
484    Args:
485        fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True.
486    """
487
488    def decorator(
489        method: Callable[..., Iterable[RenderableType]]
490    ) -> Callable[..., Group]:
491        """Convert a method that returns an iterable of renderables in to a RenderGroup."""
492
493        @wraps(method)
494        def _replace(*args: Any, **kwargs: Any) -> Group:
495            renderables = method(*args, **kwargs)
496            return Group(*renderables, fit=fit)
497
498        return _replace
499
500    return decorator
501
502
503render_group = group
504
505
506def _is_jupyter() -> bool:  # pragma: no cover
507    """Check if we're running in a Jupyter notebook."""
508    try:
509        get_ipython  # type: ignore
510    except NameError:
511        return False
512    ipython = get_ipython()  # type: ignore
513    shell = ipython.__class__.__name__
514    if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell":
515        return True  # Jupyter notebook or qtconsole
516    elif shell == "TerminalInteractiveShell":
517        return False  # Terminal running IPython
518    else:
519        return False  # Other type (?)
520
521
522COLOR_SYSTEMS = {
523    "standard": ColorSystem.STANDARD,
524    "256": ColorSystem.EIGHT_BIT,
525    "truecolor": ColorSystem.TRUECOLOR,
526    "windows": ColorSystem.WINDOWS,
527}
528
529
530_COLOR_SYSTEMS_NAMES = {system: name for name, system in COLOR_SYSTEMS.items()}
531
532
533@dataclass
534class ConsoleThreadLocals(threading.local):
535    """Thread local values for Console context."""
536
537    theme_stack: ThemeStack
538    buffer: List[Segment] = field(default_factory=list)
539    buffer_index: int = 0
540
541
542class RenderHook(ABC):
543    """Provides hooks in to the render process."""
544
545    @abstractmethod
546    def process_renderables(
547        self, renderables: List[ConsoleRenderable]
548    ) -> List[ConsoleRenderable]:
549        """Called with a list of objects to render.
550
551        This method can return a new list of renderables, or modify and return the same list.
552
553        Args:
554            renderables (List[ConsoleRenderable]): A number of renderable objects.
555
556        Returns:
557            List[ConsoleRenderable]: A replacement list of renderables.
558        """
559
560
561_windows_console_features: Optional["WindowsConsoleFeatures"] = None
562
563
564def get_windows_console_features() -> "WindowsConsoleFeatures":  # pragma: no cover
565    global _windows_console_features
566    if _windows_console_features is not None:
567        return _windows_console_features
568    from ._windows import get_windows_console_features
569
570    _windows_console_features = get_windows_console_features()
571    return _windows_console_features
572
573
574def detect_legacy_windows() -> bool:
575    """Detect legacy Windows."""
576    return WINDOWS and not get_windows_console_features().vt
577
578
579if detect_legacy_windows():  # pragma: no cover
580    from colorama import init
581
582    init(strip=False)
583
584
585class Console:
586    """A high level console interface.
587
588    Args:
589        color_system (str, optional): The color system supported by your terminal,
590            either ``"standard"``, ``"256"`` or ``"truecolor"``. Leave as ``"auto"`` to autodetect.
591        force_terminal (Optional[bool], optional): Enable/disable terminal control codes, or None to auto-detect terminal. Defaults to None.
592        force_jupyter (Optional[bool], optional): Enable/disable Jupyter rendering, or None to auto-detect Jupyter. Defaults to None.
593        force_interactive (Optional[bool], optional): Enable/disable interactive mode, or None to auto detect. Defaults to None.
594        soft_wrap (Optional[bool], optional): Set soft wrap default on print method. Defaults to False.
595        theme (Theme, optional): An optional style theme object, or ``None`` for default theme.
596        stderr (bool, optional): Use stderr rather than stdout if ``file`` is not specified. Defaults to False.
597        file (IO, optional): A file object where the console should write to. Defaults to stdout.
598        quiet (bool, Optional): Boolean to suppress all output. Defaults to False.
599        width (int, optional): The width of the terminal. Leave as default to auto-detect width.
600        height (int, optional): The height of the terminal. Leave as default to auto-detect height.
601        style (StyleType, optional): Style to apply to all output, or None for no style. Defaults to None.
602        no_color (Optional[bool], optional): Enabled no color mode, or None to auto detect. Defaults to None.
603        tab_size (int, optional): Number of spaces used to replace a tab character. Defaults to 8.
604        record (bool, optional): Boolean to enable recording of terminal output,
605            required to call :meth:`export_html` and :meth:`export_text`. Defaults to False.
606        markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True.
607        emoji (bool, optional): Enable emoji code. Defaults to True.
608        emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None.
609        highlight (bool, optional): Enable automatic highlighting. Defaults to True.
610        log_time (bool, optional): Boolean to enable logging of time by :meth:`log` methods. Defaults to True.
611        log_path (bool, optional): Boolean to enable the logging of the caller by :meth:`log`. Defaults to True.
612        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] ".
613        highlighter (HighlighterType, optional): Default highlighter.
614        legacy_windows (bool, optional): Enable legacy Windows mode, or ``None`` to auto detect. Defaults to ``None``.
615        safe_box (bool, optional): Restrict box options that don't render on legacy Windows.
616        get_datetime (Callable[[], datetime], optional): Callable that gets the current time as a datetime.datetime object (used by Console.log),
617            or None for datetime.now.
618        get_time (Callable[[], time], optional): Callable that gets the current time in seconds, default uses time.monotonic.
619    """
620
621    _environ: Mapping[str, str] = os.environ
622
623    def __init__(
624        self,
625        *,
626        color_system: Optional[
627            Literal["auto", "standard", "256", "truecolor", "windows"]
628        ] = "auto",
629        force_terminal: Optional[bool] = None,
630        force_jupyter: Optional[bool] = None,
631        force_interactive: Optional[bool] = None,
632        soft_wrap: bool = False,
633        theme: Optional[Theme] = None,
634        stderr: bool = False,
635        file: Optional[IO[str]] = None,
636        quiet: bool = False,
637        width: Optional[int] = None,
638        height: Optional[int] = None,
639        style: Optional[StyleType] = None,
640        no_color: Optional[bool] = None,
641        tab_size: int = 8,
642        record: bool = False,
643        markup: bool = True,
644        emoji: bool = True,
645        emoji_variant: Optional[EmojiVariant] = None,
646        highlight: bool = True,
647        log_time: bool = True,
648        log_path: bool = True,
649        log_time_format: Union[str, FormatTimeCallable] = "[%X]",
650        highlighter: Optional["HighlighterType"] = ReprHighlighter(),
651        legacy_windows: Optional[bool] = None,
652        safe_box: bool = True,
653        get_datetime: Optional[Callable[[], datetime]] = None,
654        get_time: Optional[Callable[[], float]] = None,
655        _environ: Optional[Mapping[str, str]] = None,
656    ):
657        # Copy of os.environ allows us to replace it for testing
658        if _environ is not None:
659            self._environ = _environ
660
661        self.is_jupyter = _is_jupyter() if force_jupyter is None else force_jupyter
662        if self.is_jupyter:
663            width = width or 93
664            height = height or 100
665
666        self.soft_wrap = soft_wrap
667        self._width = width
668        self._height = height
669        self.tab_size = tab_size
670        self.record = record
671        self._markup = markup
672        self._emoji = emoji
673        self._emoji_variant: Optional[EmojiVariant] = emoji_variant
674        self._highlight = highlight
675        self.legacy_windows: bool = (
676            (detect_legacy_windows() and not self.is_jupyter)
677            if legacy_windows is None
678            else legacy_windows
679        )
680        if width is None:
681            columns = self._environ.get("COLUMNS")
682            if columns is not None and columns.isdigit():
683                width = int(columns) - self.legacy_windows
684        if height is None:
685            lines = self._environ.get("LINES")
686            if lines is not None and lines.isdigit():
687                height = int(lines)
688
689        self.soft_wrap = soft_wrap
690        self._width = width
691        self._height = height
692
693        self._color_system: Optional[ColorSystem]
694        self._force_terminal = force_terminal
695        self._file = file
696        self.quiet = quiet
697        self.stderr = stderr
698
699        if color_system is None:
700            self._color_system = None
701        elif color_system == "auto":
702            self._color_system = self._detect_color_system()
703        else:
704            self._color_system = COLOR_SYSTEMS[color_system]
705
706        self._lock = threading.RLock()
707        self._log_render = LogRender(
708            show_time=log_time,
709            show_path=log_path,
710            time_format=log_time_format,
711        )
712        self.highlighter: HighlighterType = highlighter or _null_highlighter
713        self.safe_box = safe_box
714        self.get_datetime = get_datetime or datetime.now
715        self.get_time = get_time or monotonic
716        self.style = style
717        self.no_color = (
718            no_color if no_color is not None else "NO_COLOR" in self._environ
719        )
720        self.is_interactive = (
721            (self.is_terminal and not self.is_dumb_terminal)
722            if force_interactive is None
723            else force_interactive
724        )
725
726        self._record_buffer_lock = threading.RLock()
727        self._thread_locals = ConsoleThreadLocals(
728            theme_stack=ThemeStack(themes.DEFAULT if theme is None else theme)
729        )
730        self._record_buffer: List[Segment] = []
731        self._render_hooks: List[RenderHook] = []
732        self._live: Optional["Live"] = None
733        self._is_alt_screen = False
734
735    def __repr__(self) -> str:
736        return f"<console width={self.width} {str(self._color_system)}>"
737
738    @property
739    def file(self) -> IO[str]:
740        """Get the file object to write to."""
741        file = self._file or (sys.stderr if self.stderr else sys.stdout)
742        file = getattr(file, "rich_proxied_file", file)
743        return file
744
745    @file.setter
746    def file(self, new_file: IO[str]) -> None:
747        """Set a new file object."""
748        self._file = new_file
749
750    @property
751    def _buffer(self) -> List[Segment]:
752        """Get a thread local buffer."""
753        return self._thread_locals.buffer
754
755    @property
756    def _buffer_index(self) -> int:
757        """Get a thread local buffer."""
758        return self._thread_locals.buffer_index
759
760    @_buffer_index.setter
761    def _buffer_index(self, value: int) -> None:
762        self._thread_locals.buffer_index = value
763
764    @property
765    def _theme_stack(self) -> ThemeStack:
766        """Get the thread local theme stack."""
767        return self._thread_locals.theme_stack
768
769    def _detect_color_system(self) -> Optional[ColorSystem]:
770        """Detect color system from env vars."""
771        if self.is_jupyter:
772            return ColorSystem.TRUECOLOR
773        if not self.is_terminal or self.is_dumb_terminal:
774            return None
775        if WINDOWS:  # pragma: no cover
776            if self.legacy_windows:  # pragma: no cover
777                return ColorSystem.WINDOWS
778            windows_console_features = get_windows_console_features()
779            return (
780                ColorSystem.TRUECOLOR
781                if windows_console_features.truecolor
782                else ColorSystem.EIGHT_BIT
783            )
784        else:
785            color_term = self._environ.get("COLORTERM", "").strip().lower()
786            if color_term in ("truecolor", "24bit"):
787                return ColorSystem.TRUECOLOR
788            term = self._environ.get("TERM", "").strip().lower()
789            _term_name, _hyphen, colors = term.rpartition("-")
790            color_system = _TERM_COLORS.get(colors, ColorSystem.STANDARD)
791            return color_system
792
793    def _enter_buffer(self) -> None:
794        """Enter in to a buffer context, and buffer all output."""
795        self._buffer_index += 1
796
797    def _exit_buffer(self) -> None:
798        """Leave buffer context, and render content if required."""
799        self._buffer_index -= 1
800        self._check_buffer()
801
802    def set_live(self, live: "Live") -> None:
803        """Set Live instance. Used by Live context manager.
804
805        Args:
806            live (Live): Live instance using this Console.
807
808        Raises:
809            errors.LiveError: If this Console has a Live context currently active.
810        """
811        with self._lock:
812            if self._live is not None:
813                raise errors.LiveError("Only one live display may be active at once")
814            self._live = live
815
816    def clear_live(self) -> None:
817        """Clear the Live instance."""
818        with self._lock:
819            self._live = None
820
821    def push_render_hook(self, hook: RenderHook) -> None:
822        """Add a new render hook to the stack.
823
824        Args:
825            hook (RenderHook): Render hook instance.
826        """
827        with self._lock:
828            self._render_hooks.append(hook)
829
830    def pop_render_hook(self) -> None:
831        """Pop the last renderhook from the stack."""
832        with self._lock:
833            self._render_hooks.pop()
834
835    def __enter__(self) -> "Console":
836        """Own context manager to enter buffer context."""
837        self._enter_buffer()
838        return self
839
840    def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
841        """Exit buffer context."""
842        self._exit_buffer()
843
844    def begin_capture(self) -> None:
845        """Begin capturing console output. Call :meth:`end_capture` to exit capture mode and return output."""
846        self._enter_buffer()
847
848    def end_capture(self) -> str:
849        """End capture mode and return captured string.
850
851        Returns:
852            str: Console output.
853        """
854        render_result = self._render_buffer(self._buffer)
855        del self._buffer[:]
856        self._exit_buffer()
857        return render_result
858
859    def push_theme(self, theme: Theme, *, inherit: bool = True) -> None:
860        """Push a new theme on to the top of the stack, replacing the styles from the previous theme.
861        Generally speaking, you should call :meth:`~rich.console.Console.use_theme` to get a context manager, rather
862        than calling this method directly.
863
864        Args:
865            theme (Theme): A theme instance.
866            inherit (bool, optional): Inherit existing styles. Defaults to True.
867        """
868        self._theme_stack.push_theme(theme, inherit=inherit)
869
870    def pop_theme(self) -> None:
871        """Remove theme from top of stack, restoring previous theme."""
872        self._theme_stack.pop_theme()
873
874    def use_theme(self, theme: Theme, *, inherit: bool = True) -> ThemeContext:
875        """Use a different theme for the duration of the context manager.
876
877        Args:
878            theme (Theme): Theme instance to user.
879            inherit (bool, optional): Inherit existing console styles. Defaults to True.
880
881        Returns:
882            ThemeContext: [description]
883        """
884        return ThemeContext(self, theme, inherit)
885
886    @property
887    def color_system(self) -> Optional[str]:
888        """Get color system string.
889
890        Returns:
891            Optional[str]: "standard", "256" or "truecolor".
892        """
893
894        if self._color_system is not None:
895            return _COLOR_SYSTEMS_NAMES[self._color_system]
896        else:
897            return None
898
899    @property
900    def encoding(self) -> str:
901        """Get the encoding of the console file, e.g. ``"utf-8"``.
902
903        Returns:
904            str: A standard encoding string.
905        """
906        return (getattr(self.file, "encoding", "utf-8") or "utf-8").lower()
907
908    @property
909    def is_terminal(self) -> bool:
910        """Check if the console is writing to a terminal.
911
912        Returns:
913            bool: True if the console writing to a device capable of
914            understanding terminal codes, otherwise False.
915        """
916        if self._force_terminal is not None:
917            return self._force_terminal
918        isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
919        try:
920            return False if isatty is None else isatty()
921        except ValueError:
922            # in some situation (at the end of a pytest run for example) isatty() can raise
923            # ValueError: I/O operation on closed file
924            # return False because we aren't in a terminal anymore
925            return False
926
927    @property
928    def is_dumb_terminal(self) -> bool:
929        """Detect dumb terminal.
930
931        Returns:
932            bool: True if writing to a dumb terminal, otherwise False.
933
934        """
935        _term = self._environ.get("TERM", "")
936        is_dumb = _term.lower() in ("dumb", "unknown")
937        return self.is_terminal and is_dumb
938
939    @property
940    def options(self) -> ConsoleOptions:
941        """Get default console options."""
942        return ConsoleOptions(
943            max_height=self.size.height,
944            size=self.size,
945            legacy_windows=self.legacy_windows,
946            min_width=1,
947            max_width=self.width,
948            encoding=self.encoding,
949            is_terminal=self.is_terminal,
950        )
951
952    @property
953    def size(self) -> ConsoleDimensions:
954        """Get the size of the console.
955
956        Returns:
957            ConsoleDimensions: A named tuple containing the dimensions.
958        """
959
960        if self._width is not None and self._height is not None:
961            return ConsoleDimensions(self._width - self.legacy_windows, self._height)
962
963        if self.is_dumb_terminal:
964            return ConsoleDimensions(80, 25)
965
966        width: Optional[int] = None
967        height: Optional[int] = None
968
969        if WINDOWS:  # pragma: no cover
970            try:
971                width, height = os.get_terminal_size()
972            except OSError:  # Probably not a terminal
973                pass
974        else:
975            try:
976                width, height = os.get_terminal_size(sys.__stdin__.fileno())
977            except (AttributeError, ValueError, OSError):
978                try:
979                    width, height = os.get_terminal_size(sys.__stdout__.fileno())
980                except (AttributeError, ValueError, OSError):
981                    pass
982
983        columns = self._environ.get("COLUMNS")
984        if columns is not None and columns.isdigit():
985            width = int(columns)
986        lines = self._environ.get("LINES")
987        if lines is not None and lines.isdigit():
988            height = int(lines)
989
990        # get_terminal_size can report 0, 0 if run from pseudo-terminal
991        width = width or 80
992        height = height or 25
993        return ConsoleDimensions(
994            width - self.legacy_windows if self._width is None else self._width,
995            height if self._height is None else self._height,
996        )
997
998    @size.setter
999    def size(self, new_size: Tuple[int, int]) -> None:
1000        """Set a new size for the terminal.
1001
1002        Args:
1003            new_size (Tuple[int, int]): New width and height.
1004        """
1005        width, height = new_size
1006        self._width = width
1007        self._height = height
1008
1009    @property
1010    def width(self) -> int:
1011        """Get the width of the console.
1012
1013        Returns:
1014            int: The width (in characters) of the console.
1015        """
1016        return self.size.width
1017
1018    @width.setter
1019    def width(self, width: int) -> None:
1020        """Set width.
1021
1022        Args:
1023            width (int): New width.
1024        """
1025        self._width = width
1026
1027    @property
1028    def height(self) -> int:
1029        """Get the height of the console.
1030
1031        Returns:
1032            int: The height (in lines) of the console.
1033        """
1034        return self.size.height
1035
1036    @height.setter
1037    def height(self, height: int) -> None:
1038        """Set height.
1039
1040        Args:
1041            height (int): new height.
1042        """
1043        self._height = height
1044
1045    def bell(self) -> None:
1046        """Play a 'bell' sound (if supported by the terminal)."""
1047        self.control(Control.bell())
1048
1049    def capture(self) -> Capture:
1050        """A context manager to *capture* the result of print() or log() in a string,
1051        rather than writing it to the console.
1052
1053        Example:
1054            >>> from rich.console import Console
1055            >>> console = Console()
1056            >>> with console.capture() as capture:
1057            ...     console.print("[bold magenta]Hello World[/]")
1058            >>> print(capture.get())
1059
1060        Returns:
1061            Capture: Context manager with disables writing to the terminal.
1062        """
1063        capture = Capture(self)
1064        return capture
1065
1066    def pager(
1067        self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False
1068    ) -> PagerContext:
1069        """A context manager to display anything printed within a "pager". The pager application
1070        is defined by the system and will typically support at least pressing a key to scroll.
1071
1072        Args:
1073            pager (Pager, optional): A pager object, or None to use :class:`~rich.pager.SystemPager`. Defaults to None.
1074            styles (bool, optional): Show styles in pager. Defaults to False.
1075            links (bool, optional): Show links in pager. Defaults to False.
1076
1077        Example:
1078            >>> from rich.console import Console
1079            >>> from rich.__main__ import make_test_card
1080            >>> console = Console()
1081            >>> with console.pager():
1082                    console.print(make_test_card())
1083
1084        Returns:
1085            PagerContext: A context manager.
1086        """
1087        return PagerContext(self, pager=pager, styles=styles, links=links)
1088
1089    def line(self, count: int = 1) -> None:
1090        """Write new line(s).
1091
1092        Args:
1093            count (int, optional): Number of new lines. Defaults to 1.
1094        """
1095
1096        assert count >= 0, "count must be >= 0"
1097        self.print(NewLine(count))
1098
1099    def clear(self, home: bool = True) -> None:
1100        """Clear the screen.
1101
1102        Args:
1103            home (bool, optional): Also move the cursor to 'home' position. Defaults to True.
1104        """
1105        if home:
1106            self.control(Control.clear(), Control.home())
1107        else:
1108            self.control(Control.clear())
1109
1110    def status(
1111        self,
1112        status: RenderableType,
1113        *,
1114        spinner: str = "dots",
1115        spinner_style: str = "status.spinner",
1116        speed: float = 1.0,
1117        refresh_per_second: float = 12.5,
1118    ) -> "Status":
1119        """Display a status and spinner.
1120
1121        Args:
1122            status (RenderableType): A status renderable (str or Text typically).
1123            spinner (str, optional): Name of spinner animation (see python -m rich.spinner). Defaults to "dots".
1124            spinner_style (StyleType, optional): Style of spinner. Defaults to "status.spinner".
1125            speed (float, optional): Speed factor for spinner animation. Defaults to 1.0.
1126            refresh_per_second (float, optional): Number of refreshes per second. Defaults to 12.5.
1127
1128        Returns:
1129            Status: A Status object that may be used as a context manager.
1130        """
1131        from .status import Status
1132
1133        status_renderable = Status(
1134            status,
1135            console=self,
1136            spinner=spinner,
1137            spinner_style=spinner_style,
1138            speed=speed,
1139            refresh_per_second=refresh_per_second,
1140        )
1141        return status_renderable
1142
1143    def show_cursor(self, show: bool = True) -> bool:
1144        """Show or hide the cursor.
1145
1146        Args:
1147            show (bool, optional): Set visibility of the cursor.
1148        """
1149        if self.is_terminal and not self.legacy_windows:
1150            self.control(Control.show_cursor(show))
1151            return True
1152        return False
1153
1154    def set_alt_screen(self, enable: bool = True) -> bool:
1155        """Enables alternative screen mode.
1156
1157        Note, if you enable this mode, you should ensure that is disabled before
1158        the application exits. See :meth:`~rich.Console.screen` for a context manager
1159        that handles this for you.
1160
1161        Args:
1162            enable (bool, optional): Enable (True) or disable (False) alternate screen. Defaults to True.
1163
1164        Returns:
1165            bool: True if the control codes were written.
1166
1167        """
1168        changed = False
1169        if self.is_terminal and not self.legacy_windows:
1170            self.control(Control.alt_screen(enable))
1171            changed = True
1172            self._is_alt_screen = enable
1173        return changed
1174
1175    @property
1176    def is_alt_screen(self) -> bool:
1177        """Check if the alt screen was enabled.
1178
1179        Returns:
1180            bool: True if the alt screen was enabled, otherwise False.
1181        """
1182        return self._is_alt_screen
1183
1184    def screen(
1185        self, hide_cursor: bool = True, style: Optional[StyleType] = None
1186    ) -> "ScreenContext":
1187        """Context manager to enable and disable 'alternative screen' mode.
1188
1189        Args:
1190            hide_cursor (bool, optional): Also hide the cursor. Defaults to False.
1191            style (Style, optional): Optional style for screen. Defaults to None.
1192
1193        Returns:
1194            ~ScreenContext: Context which enables alternate screen on enter, and disables it on exit.
1195        """
1196        return ScreenContext(self, hide_cursor=hide_cursor, style=style or "")
1197
1198    def measure(
1199        self, renderable: RenderableType, *, options: Optional[ConsoleOptions] = None
1200    ) -> Measurement:
1201        """Measure a renderable. Returns a :class:`~rich.measure.Measurement` object which contains
1202        information regarding the number of characters required to print the renderable.
1203
1204        Args:
1205            renderable (RenderableType): Any renderable or string.
1206            options (Optional[ConsoleOptions], optional): Options to use when measuring, or None
1207                to use default options. Defaults to None.
1208
1209        Returns:
1210            Measurement: A measurement of the renderable.
1211        """
1212        measurement = Measurement.get(self, options or self.options, renderable)
1213        return measurement
1214
1215    def render(
1216        self, renderable: RenderableType, options: Optional[ConsoleOptions] = None
1217    ) -> Iterable[Segment]:
1218        """Render an object in to an iterable of `Segment` instances.
1219
1220        This method contains the logic for rendering objects with the console protocol.
1221        You are unlikely to need to use it directly, unless you are extending the library.
1222
1223        Args:
1224            renderable (RenderableType): An object supporting the console protocol, or
1225                an object that may be converted to a string.
1226            options (ConsoleOptions, optional): An options object, or None to use self.options. Defaults to None.
1227
1228        Returns:
1229            Iterable[Segment]: An iterable of segments that may be rendered.
1230        """
1231
1232        _options = options or self.options
1233        if _options.max_width < 1:
1234            # No space to render anything. This prevents potential recursion errors.
1235            return
1236        render_iterable: RenderResult
1237
1238        renderable = rich_cast(renderable)
1239        if hasattr(renderable, "__rich_console__") and not isclass(renderable):
1240            render_iterable = renderable.__rich_console__(self, _options)  # type: ignore
1241        elif isinstance(renderable, str):
1242            text_renderable = self.render_str(
1243                renderable, highlight=_options.highlight, markup=_options.markup
1244            )
1245            render_iterable = text_renderable.__rich_console__(self, _options)
1246        else:
1247            raise errors.NotRenderableError(
1248                f"Unable to render {renderable!r}; "
1249                "A str, Segment or object with __rich_console__ method is required"
1250            )
1251
1252        try:
1253            iter_render = iter(render_iterable)
1254        except TypeError:
1255            raise errors.NotRenderableError(
1256                f"object {render_iterable!r} is not renderable"
1257            )
1258        _Segment = Segment
1259        for render_output in iter_render:
1260            if isinstance(render_output, _Segment):
1261                yield render_output
1262            else:
1263                yield from self.render(render_output, _options)
1264
1265    def render_lines(
1266        self,
1267        renderable: RenderableType,
1268        options: Optional[ConsoleOptions] = None,
1269        *,
1270        style: Optional[Style] = None,
1271        pad: bool = True,
1272        new_lines: bool = False,
1273    ) -> List[List[Segment]]:
1274        """Render objects in to a list of lines.
1275
1276        The output of render_lines is useful when further formatting of rendered console text
1277        is required, such as the Panel class which draws a border around any renderable object.
1278
1279        Args:
1280            renderable (RenderableType): Any object renderable in the console.
1281            options (Optional[ConsoleOptions], optional): Console options, or None to use self.options. Default to ``None``.
1282            style (Style, optional): Optional style to apply to renderables. Defaults to ``None``.
1283            pad (bool, optional): Pad lines shorter than render width. Defaults to ``True``.
1284            new_lines (bool, optional): Include "\n" characters at end of lines.
1285
1286        Returns:
1287            List[List[Segment]]: A list of lines, where a line is a list of Segment objects.
1288        """
1289        with self._lock:
1290            render_options = options or self.options
1291            _rendered = self.render(renderable, render_options)
1292            if style:
1293                _rendered = Segment.apply_style(_rendered, style)
1294            lines = list(
1295                islice(
1296                    Segment.split_and_crop_lines(
1297                        _rendered,
1298                        render_options.max_width,
1299                        include_new_lines=new_lines,
1300                        pad=pad,
1301                    ),
1302                    None,
1303                    render_options.height,
1304                )
1305            )
1306            if render_options.height is not None:
1307                extra_lines = render_options.height - len(lines)
1308                if extra_lines > 0:
1309                    pad_line = [
1310                        [Segment(" " * render_options.max_width, style), Segment("\n")]
1311                        if new_lines
1312                        else [Segment(" " * render_options.max_width, style)]
1313                    ]
1314                    lines.extend(pad_line * extra_lines)
1315
1316            return lines
1317
1318    def render_str(
1319        self,
1320        text: str,
1321        *,
1322        style: Union[str, Style] = "",
1323        justify: Optional[JustifyMethod] = None,
1324        overflow: Optional[OverflowMethod] = None,
1325        emoji: Optional[bool] = None,
1326        markup: Optional[bool] = None,
1327        highlight: Optional[bool] = None,
1328        highlighter: Optional[HighlighterType] = None,
1329    ) -> "Text":
1330        """Convert a string to a Text instance. This is is called automatically if
1331        you print or log a string.
1332
1333        Args:
1334            text (str): Text to render.
1335            style (Union[str, Style], optional): Style to apply to rendered text.
1336            justify (str, optional): Justify method: "default", "left", "center", "full", or "right". Defaults to ``None``.
1337            overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to ``None``.
1338            emoji (Optional[bool], optional): Enable emoji, or ``None`` to use Console default.
1339            markup (Optional[bool], optional): Enable markup, or ``None`` to use Console default.
1340            highlight (Optional[bool], optional): Enable highlighting, or ``None`` to use Console default.
1341            highlighter (HighlighterType, optional): Optional highlighter to apply.
1342        Returns:
1343            ConsoleRenderable: Renderable object.
1344
1345        """
1346        emoji_enabled = emoji or (emoji is None and self._emoji)
1347        markup_enabled = markup or (markup is None and self._markup)
1348        highlight_enabled = highlight or (highlight is None and self._highlight)
1349
1350        if markup_enabled:
1351            rich_text = render_markup(
1352                text,
1353                style=style,
1354                emoji=emoji_enabled,
1355                emoji_variant=self._emoji_variant,
1356            )
1357            rich_text.justify = justify
1358            rich_text.overflow = overflow
1359        else:
1360            rich_text = Text(
1361                _emoji_replace(text, default_variant=self._emoji_variant)
1362                if emoji_enabled
1363                else text,
1364                justify=justify,
1365                overflow=overflow,
1366                style=style,
1367            )
1368
1369        _highlighter = (highlighter or self.highlighter) if highlight_enabled else None
1370        if _highlighter is not None:
1371            highlight_text = _highlighter(str(rich_text))
1372            highlight_text.copy_styles(rich_text)
1373            return highlight_text
1374
1375        return rich_text
1376
1377    def get_style(
1378        self, name: Union[str, Style], *, default: Optional[Union[Style, str]] = None
1379    ) -> Style:
1380        """Get a Style instance by it's theme name or parse a definition.
1381
1382        Args:
1383            name (str): The name of a style or a style definition.
1384
1385        Returns:
1386            Style: A Style object.
1387
1388        Raises:
1389            MissingStyle: If no style could be parsed from name.
1390
1391        """
1392        if isinstance(name, Style):
1393            return name
1394
1395        try:
1396            style = self._theme_stack.get(name)
1397            if style is None:
1398                style = Style.parse(name)
1399            return style.copy() if style.link else style
1400        except errors.StyleSyntaxError as error:
1401            if default is not None:
1402                return self.get_style(default)
1403            raise errors.MissingStyle(
1404                f"Failed to get style {name!r}; {error}"
1405            ) from None
1406
1407    def _collect_renderables(
1408        self,
1409        objects: Iterable[Any],
1410        sep: str,
1411        end: str,
1412        *,
1413        justify: Optional[JustifyMethod] = None,
1414        emoji: Optional[bool] = None,
1415        markup: Optional[bool] = None,
1416        highlight: Optional[bool] = None,
1417    ) -> List[ConsoleRenderable]:
1418        """Combine a number of renderables and text into one renderable.
1419
1420        Args:
1421            objects (Iterable[Any]): Anything that Rich can render.
1422            sep (str): String to write between print data.
1423            end (str): String to write at end of print data.
1424            justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
1425            emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default.
1426            markup (Optional[bool], optional): Enable markup, or ``None`` to use console default.
1427            highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default.
1428
1429        Returns:
1430            List[ConsoleRenderable]: A list of things to render.
1431        """
1432        renderables: List[ConsoleRenderable] = []
1433        _append = renderables.append
1434        text: List[Text] = []
1435        append_text = text.append
1436
1437        append = _append
1438        if justify in ("left", "center", "right"):
1439
1440            def align_append(renderable: RenderableType) -> None:
1441                _append(Align(renderable, cast(AlignMethod, justify)))
1442
1443            append = align_append
1444
1445        _highlighter: HighlighterType = _null_highlighter
1446        if highlight or (highlight is None and self._highlight):
1447            _highlighter = self.highlighter
1448
1449        def check_text() -> None:
1450            if text:
1451                sep_text = Text(sep, justify=justify, end=end)
1452                append(sep_text.join(text))
1453                del text[:]
1454
1455        for renderable in objects:
1456            renderable = rich_cast(renderable)
1457            if isinstance(renderable, str):
1458                append_text(
1459                    self.render_str(
1460                        renderable, emoji=emoji, markup=markup, highlighter=_highlighter
1461                    )
1462                )
1463            elif isinstance(renderable, Text):
1464                append_text(renderable)
1465            elif isinstance(renderable, ConsoleRenderable):
1466                check_text()
1467                append(renderable)
1468            elif is_expandable(renderable):
1469                check_text()
1470                append(Pretty(renderable, highlighter=_highlighter))
1471            else:
1472                append_text(_highlighter(str(renderable)))
1473
1474        check_text()
1475
1476        if self.style is not None:
1477            style = self.get_style(self.style)
1478            renderables = [Styled(renderable, style) for renderable in renderables]
1479
1480        return renderables
1481
1482    def rule(
1483        self,
1484        title: TextType = "",
1485        *,
1486        characters: str = "─",
1487        style: Union[str, Style] = "rule.line",
1488        align: AlignMethod = "center",
1489    ) -> None:
1490        """Draw a line with optional centered title.
1491
1492        Args:
1493            title (str, optional): Text to render over the rule. Defaults to "".
1494            characters (str, optional): Character(s) to form the line. Defaults to "─".
1495            style (str, optional): Style of line. Defaults to "rule.line".
1496            align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center".
1497        """
1498        from .rule import Rule
1499
1500        rule = Rule(title=title, characters=characters, style=style, align=align)
1501        self.print(rule)
1502
1503    def control(self, *control: Control) -> None:
1504        """Insert non-printing control codes.
1505
1506        Args:
1507            control_codes (str): Control codes, such as those that may move the cursor.
1508        """
1509        if not self.is_dumb_terminal:
1510            with self:
1511                self._buffer.extend(_control.segment for _control in control)
1512
1513    def out(
1514        self,
1515        *objects: Any,
1516        sep: str = " ",
1517        end: str = "\n",
1518        style: Optional[Union[str, Style]] = None,
1519        highlight: Optional[bool] = None,
1520    ) -> None:
1521        """Output to the terminal. This is a low-level way of writing to the terminal which unlike
1522        :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markup, but will
1523        optionally apply highlighting and a basic style.
1524
1525        Args:
1526            sep (str, optional): String to write between print data. Defaults to " ".
1527            end (str, optional): String to write at end of print data. Defaults to "\\\\n".
1528            style (Union[str, Style], optional): A style to apply to output. Defaults to None.
1529            highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use
1530                console default. Defaults to ``None``.
1531        """
1532        raw_output: str = sep.join(str(_object) for _object in objects)
1533        self.print(
1534            raw_output,
1535            style=style,
1536            highlight=highlight,
1537            emoji=False,
1538            markup=False,
1539            no_wrap=True,
1540            overflow="ignore",
1541            crop=False,
1542            end=end,
1543        )
1544
1545    def print(
1546        self,
1547        *objects: Any,
1548        sep: str = " ",
1549        end: str = "\n",
1550        style: Optional[Union[str, Style]] = None,
1551        justify: Optional[JustifyMethod] = None,
1552        overflow: Optional[OverflowMethod] = None,
1553        no_wrap: Optional[bool] = None,
1554        emoji: Optional[bool] = None,
1555        markup: Optional[bool] = None,
1556        highlight: Optional[bool] = None,
1557        width: Optional[int] = None,
1558        height: Optional[int] = None,
1559        crop: bool = True,
1560        soft_wrap: Optional[bool] = None,
1561        new_line_start: bool = False,
1562    ) -> None:
1563        """Print to the console.
1564
1565        Args:
1566            objects (positional args): Objects to log to the terminal.
1567            sep (str, optional): String to write between print data. Defaults to " ".
1568            end (str, optional): String to write at end of print data. Defaults to "\\\\n".
1569            style (Union[str, Style], optional): A style to apply to output. Defaults to None.
1570            justify (str, optional): Justify method: "default", "left", "right", "center", or "full". Defaults to ``None``.
1571            overflow (str, optional): Overflow method: "ignore", "crop", "fold", or "ellipsis". Defaults to None.
1572            no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to None.
1573            emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to ``None``.
1574            markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to ``None``.
1575            highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to ``None``.
1576            width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``.
1577            crop (Optional[bool], optional): Crop output to width of terminal. Defaults to True.
1578            soft_wrap (bool, optional): Enable soft wrap mode which disables word wrapping and cropping of text or ``None`` for
1579                Console default. Defaults to ``None``.
1580            new_line_start (bool, False): Insert a new line at the start if the output contains more than one line. Defaults to ``False``.
1581        """
1582        if not objects:
1583            objects = (NewLine(),)
1584
1585        if soft_wrap is None:
1586            soft_wrap = self.soft_wrap
1587        if soft_wrap:
1588            if no_wrap is None:
1589                no_wrap = True
1590            if overflow is None:
1591                overflow = "ignore"
1592            crop = False
1593        render_hooks = self._render_hooks[:]
1594        with self:
1595            renderables = self._collect_renderables(
1596                objects,
1597                sep,
1598                end,
1599                justify=justify,
1600                emoji=emoji,
1601                markup=markup,
1602                highlight=highlight,
1603            )
1604            for hook in render_hooks:
1605                renderables = hook.process_renderables(renderables)
1606            render_options = self.options.update(
1607                justify=justify,
1608                overflow=overflow,
1609                width=min(width, self.width) if width is not None else NO_CHANGE,
1610                height=height,
1611                no_wrap=no_wrap,
1612                markup=markup,
1613                highlight=highlight,
1614            )
1615
1616            new_segments: List[Segment] = []
1617            extend = new_segments.extend
1618            render = self.render
1619            if style is None:
1620                for renderable in renderables:
1621                    extend(render(renderable, render_options))
1622            else:
1623                for renderable in renderables:
1624                    extend(
1625                        Segment.apply_style(
1626                            render(renderable, render_options), self.get_style(style)
1627                        )
1628                    )
1629            if new_line_start:
1630                if (
1631                    len("".join(segment.text for segment in new_segments).splitlines())
1632                    > 1
1633                ):
1634                    new_segments.insert(0, Segment.line())
1635            if crop:
1636                buffer_extend = self._buffer.extend
1637                for line in Segment.split_and_crop_lines(
1638                    new_segments, self.width, pad=False
1639                ):
1640                    buffer_extend(line)
1641            else:
1642                self._buffer.extend(new_segments)
1643
1644    def print_json(
1645        self,
1646        json: Optional[str] = None,
1647        *,
1648        data: Any = None,
1649        indent: Union[None, int, str] = 2,
1650        highlight: bool = True,
1651        skip_keys: bool = False,
1652        ensure_ascii: bool = True,
1653        check_circular: bool = True,
1654        allow_nan: bool = True,
1655        default: Optional[Callable[[Any], Any]] = None,
1656        sort_keys: bool = False,
1657    ) -> None:
1658        """Pretty prints JSON. Output will be valid JSON.
1659
1660        Args:
1661            json (Optional[str]): A string containing JSON.
1662            data (Any): If json is not supplied, then encode this data.
1663            indent (Union[None, int, str], optional): Number of spaces to indent. Defaults to 2.
1664            highlight (bool, optional): Enable highlighting of output: Defaults to True.
1665            skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
1666            ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
1667            check_circular (bool, optional): Check for circular references. Defaults to True.
1668            allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
1669            default (Callable, optional): A callable that converts values that can not be encoded
1670                in to something that can be JSON encoded. Defaults to None.
1671            sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
1672        """
1673        from rich.json import JSON
1674
1675        if json is None:
1676            json_renderable = JSON.from_data(
1677                data,
1678                indent=indent,
1679                highlight=highlight,
1680                skip_keys=skip_keys,
1681                ensure_ascii=ensure_ascii,
1682                check_circular=check_circular,
1683                allow_nan=allow_nan,
1684                default=default,
1685                sort_keys=sort_keys,
1686            )
1687        else:
1688            if not isinstance(json, str):
1689                raise TypeError(
1690                    f"json must be str. Did you mean print_json(data={json!r}) ?"
1691                )
1692            json_renderable = JSON(
1693                json,
1694                indent=indent,
1695                highlight=highlight,
1696                skip_keys=skip_keys,
1697                ensure_ascii=ensure_ascii,
1698                check_circular=check_circular,
1699                allow_nan=allow_nan,
1700                default=default,
1701                sort_keys=sort_keys,
1702            )
1703        self.print(json_renderable, soft_wrap=True)
1704
1705    def update_screen(
1706        self,
1707        renderable: RenderableType,
1708        *,
1709        region: Optional[Region] = None,
1710        options: Optional[ConsoleOptions] = None,
1711    ) -> None:
1712        """Update the screen at a given offset.
1713
1714        Args:
1715            renderable (RenderableType): A Rich renderable.
1716            region (Region, optional): Region of screen to update, or None for entire screen. Defaults to None.
1717            x (int, optional): x offset. Defaults to 0.
1718            y (int, optional): y offset. Defaults to 0.
1719
1720        Raises:
1721            errors.NoAltScreen: If the Console isn't in alt screen mode.
1722
1723        """
1724        if not self.is_alt_screen:
1725            raise errors.NoAltScreen("Alt screen must be enabled to call update_screen")
1726        render_options = options or self.options
1727        if region is None:
1728            x = y = 0
1729            render_options = render_options.update_dimensions(
1730                render_options.max_width, render_options.height or self.height
1731            )
1732        else:
1733            x, y, width, height = region
1734            render_options = render_options.update_dimensions(width, height)
1735
1736        lines = self.render_lines(renderable, options=render_options)
1737        self.update_screen_lines(lines, x, y)
1738
1739    def update_screen_lines(
1740        self, lines: List[List[Segment]], x: int = 0, y: int = 0
1741    ) -> None:
1742        """Update lines of the screen at a given offset.
1743
1744        Args:
1745            lines (List[List[Segment]]): Rendered lines (as produced by :meth:`~rich.Console.render_lines`).
1746            x (int, optional): x offset (column no). Defaults to 0.
1747            y (int, optional): y offset (column no). Defaults to 0.
1748
1749        Raises:
1750            errors.NoAltScreen: If the Console isn't in alt screen mode.
1751        """
1752        if not self.is_alt_screen:
1753            raise errors.NoAltScreen("Alt screen must be enabled to call update_screen")
1754        screen_update = ScreenUpdate(lines, x, y)
1755        segments = self.render(screen_update)
1756        self._buffer.extend(segments)
1757        self._check_buffer()
1758
1759    def print_exception(
1760        self,
1761        *,
1762        width: Optional[int] = 100,
1763        extra_lines: int = 3,
1764        theme: Optional[str] = None,
1765        word_wrap: bool = False,
1766        show_locals: bool = False,
1767        suppress: Iterable[Union[str, ModuleType]] = (),
1768        max_frames: int = 100,
1769    ) -> None:
1770        """Prints a rich render of the last exception and traceback.
1771
1772        Args:
1773            width (Optional[int], optional): Number of characters used to render code. Defaults to 88.
1774            extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
1775            theme (str, optional): Override pygments theme used in traceback
1776            word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
1777            show_locals (bool, optional): Enable display of local variables. Defaults to False.
1778            suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
1779            max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
1780        """
1781        from .traceback import Traceback
1782
1783        traceback = Traceback(
1784            width=width,
1785            extra_lines=extra_lines,
1786            theme=theme,
1787            word_wrap=word_wrap,
1788            show_locals=show_locals,
1789            suppress=suppress,
1790            max_frames=max_frames,
1791        )
1792        self.print(traceback)
1793
1794    @staticmethod
1795    def _caller_frame_info(
1796        offset: int,
1797        currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,
1798    ) -> Tuple[str, int, Dict[str, Any]]:
1799        """Get caller frame information.
1800
1801        Args:
1802            offset (int): the caller offset within the current frame stack.
1803            currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to
1804                retrieve the current frame. Defaults to ``inspect.currentframe``.
1805
1806        Returns:
1807            Tuple[str, int, Dict[str, Any]]: A tuple containing the filename, the line number and
1808                the dictionary of local variables associated with the caller frame.
1809
1810        Raises:
1811            RuntimeError: If the stack offset is invalid.
1812        """
1813        # Ignore the frame of this local helper
1814        offset += 1
1815
1816        frame = currentframe()
1817        if frame is not None:
1818            # Use the faster currentframe where implemented
1819            while offset and frame:
1820                frame = frame.f_back
1821                offset -= 1
1822            assert frame is not None
1823            return frame.f_code.co_filename, frame.f_lineno, frame.f_locals
1824        else:
1825            # Fallback to the slower stack
1826            frame_info = inspect.stack()[offset]
1827            return frame_info.filename, frame_info.lineno, frame_info.frame.f_locals
1828
1829    def log(
1830        self,
1831        *objects: Any,
1832        sep: str = " ",
1833        end: str = "\n",
1834        style: Optional[Union[str, Style]] = None,
1835        justify: Optional[JustifyMethod] = None,
1836        emoji: Optional[bool] = None,
1837        markup: Optional[bool] = None,
1838        highlight: Optional[bool] = None,
1839        log_locals: bool = False,
1840        _stack_offset: int = 1,
1841    ) -> None:
1842        """Log rich content to the terminal.
1843
1844        Args:
1845            objects (positional args): Objects to log to the terminal.
1846            sep (str, optional): String to write between print data. Defaults to " ".
1847            end (str, optional): String to write at end of print data. Defaults to "\\\\n".
1848            style (Union[str, Style], optional): A style to apply to output. Defaults to None.
1849            justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``.
1850            overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None.
1851            emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.
1852            markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None.
1853            highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
1854            log_locals (bool, optional): Boolean to enable logging of locals where ``log()``
1855                was called. Defaults to False.
1856            _stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1.
1857        """
1858        if not objects:
1859            objects = (NewLine(),)
1860
1861        render_hooks = self._render_hooks[:]
1862
1863        with self:
1864            renderables = self._collect_renderables(
1865                objects,
1866                sep,
1867                end,
1868                justify=justify,
1869                emoji=emoji,
1870                markup=markup,
1871                highlight=highlight,
1872            )
1873            if style is not None:
1874                renderables = [Styled(renderable, style) for renderable in renderables]
1875
1876            filename, line_no, locals = self._caller_frame_info(_stack_offset)
1877            link_path = None if filename.startswith("<") else os.path.abspath(filename)
1878            path = filename.rpartition(os.sep)[-1]
1879            if log_locals:
1880                locals_map = {
1881                    key: value
1882                    for key, value in locals.items()
1883                    if not key.startswith("__")
1884                }
1885                renderables.append(render_scope(locals_map, title="[i]locals"))
1886
1887            renderables = [
1888                self._log_render(
1889                    self,
1890                    renderables,
1891                    log_time=self.get_datetime(),
1892                    path=path,
1893                    line_no=line_no,
1894                    link_path=link_path,
1895                )
1896            ]
1897            for hook in render_hooks:
1898                renderables = hook.process_renderables(renderables)
1899            new_segments: List[Segment] = []
1900            extend = new_segments.extend
1901            render = self.render
1902            render_options = self.options
1903            for renderable in renderables:
1904                extend(render(renderable, render_options))
1905            buffer_extend = self._buffer.extend
1906            for line in Segment.split_and_crop_lines(
1907                new_segments, self.width, pad=False
1908            ):
1909                buffer_extend(line)
1910
1911    def _check_buffer(self) -> None:
1912        """Check if the buffer may be rendered."""
1913        if self.quiet:
1914            del self._buffer[:]
1915            return
1916        with self._lock:
1917            if self._buffer_index == 0:
1918                if self.is_jupyter:  # pragma: no cover
1919                    from .jupyter import display
1920
1921                    display(self._buffer, self._render_buffer(self._buffer[:]))
1922                    del self._buffer[:]
1923                else:
1924                    text = self._render_buffer(self._buffer[:])
1925                    del self._buffer[:]
1926                    if text:
1927                        try:
1928                            if WINDOWS:  # pragma: no cover
1929                                # https://bugs.python.org/issue37871
1930                                write = self.file.write
1931                                for line in text.splitlines(True):
1932                                    write(line)
1933                            else:
1934                                self.file.write(text)
1935                            self.file.flush()
1936                        except UnicodeEncodeError as error:
1937                            error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***"
1938                            raise
1939
1940    def _render_buffer(self, buffer: Iterable[Segment]) -> str:
1941        """Render buffered output, and clear buffer."""
1942        output: List[str] = []
1943        append = output.append
1944        color_system = self._color_system
1945        legacy_windows = self.legacy_windows
1946        if self.record:
1947            with self._record_buffer_lock:
1948                self._record_buffer.extend(buffer)
1949        not_terminal = not self.is_terminal
1950        if self.no_color and color_system:
1951            buffer = Segment.remove_color(buffer)
1952        for text, style, control in buffer:
1953            if style:
1954                append(
1955                    style.render(
1956                        text,
1957                        color_system=color_system,
1958                        legacy_windows=legacy_windows,
1959                    )
1960                )
1961            elif not (not_terminal and control):
1962                append(text)
1963
1964        rendered = "".join(output)
1965        return rendered
1966
1967    def input(
1968        self,
1969        prompt: TextType = "",
1970        *,
1971        markup: bool = True,
1972        emoji: bool = True,
1973        password: bool = False,
1974        stream: Optional[TextIO] = None,
1975    ) -> str:
1976        """Displays a prompt and waits for input from the user. The prompt may contain color / style.
1977
1978        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.
1979
1980        Args:
1981            prompt (Union[str, Text]): Text to render in the prompt.
1982            markup (bool, optional): Enable console markup (requires a str prompt). Defaults to True.
1983            emoji (bool, optional): Enable emoji (requires a str prompt). Defaults to True.
1984            password: (bool, optional): Hide typed text. Defaults to False.
1985            stream: (TextIO, optional): Optional file to read input from (rather than stdin). Defaults to None.
1986
1987        Returns:
1988            str: Text read from stdin.
1989        """
1990        prompt_str = ""
1991        if prompt:
1992            with self.capture() as capture:
1993                self.print(prompt, markup=markup, emoji=emoji, end="")
1994            prompt_str = capture.get()
1995        if self.legacy_windows:
1996            # Legacy windows doesn't like ANSI codes in getpass or input (colorama bug)?
1997            self.file.write(prompt_str)
1998            prompt_str = ""
1999        if password:
2000            result = getpass(prompt_str, stream=stream)
2001        else:
2002            if stream:
2003                self.file.write(prompt_str)
2004                result = stream.readline()
2005            else:
2006                result = input(prompt_str)
2007        return result
2008
2009    def export_text(self, *, clear: bool = True, styles: bool = False) -> str:
2010        """Generate text from console contents (requires record=True argument in constructor).
2011
2012        Args:
2013            clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
2014            styles (bool, optional): If ``True``, ansi escape codes will be included. ``False`` for plain text.
2015                Defaults to ``False``.
2016
2017        Returns:
2018            str: String containing console contents.
2019
2020        """
2021        assert (
2022            self.record
2023        ), "To export console contents set record=True in the constructor or instance"
2024
2025        with self._record_buffer_lock:
2026            if styles:
2027                text = "".join(
2028                    (style.render(text) if style else text)
2029                    for text, style, _ in self._record_buffer
2030                )
2031            else:
2032                text = "".join(
2033                    segment.text
2034                    for segment in self._record_buffer
2035                    if not segment.control
2036                )
2037            if clear:
2038                del self._record_buffer[:]
2039        return text
2040
2041    def save_text(self, path: str, *, clear: bool = True, styles: bool = False) -> None:
2042        """Generate text from console and save to a given location (requires record=True argument in constructor).
2043
2044        Args:
2045            path (str): Path to write text files.
2046            clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
2047            styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text.
2048                Defaults to ``False``.
2049
2050        """
2051        text = self.export_text(clear=clear, styles=styles)
2052        with open(path, "wt", encoding="utf-8") as write_file:
2053            write_file.write(text)
2054
2055    def export_html(
2056        self,
2057        *,
2058        theme: Optional[TerminalTheme] = None,
2059        clear: bool = True,
2060        code_format: Optional[str] = None,
2061        inline_styles: bool = False,
2062    ) -> str:
2063        """Generate HTML from console contents (requires record=True argument in constructor).
2064
2065        Args:
2066            theme (TerminalTheme, optional): TerminalTheme object containing console colors.
2067            clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
2068            code_format (str, optional): Format string to render HTML, should contain {foreground}
2069                {background} and {code}.
2070            inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files
2071                larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag.
2072                Defaults to False.
2073
2074        Returns:
2075            str: String containing console contents as HTML.
2076        """
2077        assert (
2078            self.record
2079        ), "To export console contents set record=True in the constructor or instance"
2080        fragments: List[str] = []
2081        append = fragments.append
2082        _theme = theme or DEFAULT_TERMINAL_THEME
2083        stylesheet = ""
2084
2085        render_code_format = CONSOLE_HTML_FORMAT if code_format is None else code_format
2086
2087        with self._record_buffer_lock:
2088            if inline_styles:
2089                for text, style, _ in Segment.filter_control(
2090                    Segment.simplify(self._record_buffer)
2091                ):
2092                    text = escape(text)
2093                    if style:
2094                        rule = style.get_html_style(_theme)
2095                        if style.link:
2096                            text = f'<a href="{style.link}">{text}</a>'
2097                        text = f'<span style="{rule}">{text}</span>' if rule else text
2098                    append(text)
2099            else:
2100                styles: Dict[str, int] = {}
2101                for text, style, _ in Segment.filter_control(
2102                    Segment.simplify(self._record_buffer)
2103                ):
2104                    text = escape(text)
2105                    if style:
2106                        rule = style.get_html_style(_theme)
2107                        style_number = styles.setdefault(rule, len(styles) + 1)
2108                        if style.link:
2109                            text = f'<a class="r{style_number}" href="{style.link}">{text}</a>'
2110                        else:
2111                            text = f'<span class="r{style_number}">{text}</span>'
2112                    append(text)
2113                stylesheet_rules: List[str] = []
2114                stylesheet_append = stylesheet_rules.append
2115                for style_rule, style_number in styles.items():
2116                    if style_rule:
2117                        stylesheet_append(f".r{style_number} {{{style_rule}}}")
2118                stylesheet = "\n".join(stylesheet_rules)
2119
2120            rendered_code = render_code_format.format(
2121                code="".join(fragments),
2122                stylesheet=stylesheet,
2123                foreground=_theme.foreground_color.hex,
2124                background=_theme.background_color.hex,
2125            )
2126            if clear:
2127                del self._record_buffer[:]
2128        return rendered_code
2129
2130    def save_html(
2131        self,
2132        path: str,
2133        *,
2134        theme: Optional[TerminalTheme] = None,
2135        clear: bool = True,
2136        code_format: str = CONSOLE_HTML_FORMAT,
2137        inline_styles: bool = False,
2138    ) -> None:
2139        """Generate HTML from console contents and write to a file (requires record=True argument in constructor).
2140
2141        Args:
2142            path (str): Path to write html file.
2143            theme (TerminalTheme, optional): TerminalTheme object containing console colors.
2144            clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.
2145            code_format (str, optional): Format string to render HTML, should contain {foreground}
2146                {background} and {code}.
2147            inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files
2148                larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag.
2149                Defaults to False.
2150
2151        """
2152        html = self.export_html(
2153            theme=theme,
2154            clear=clear,
2155            code_format=code_format,
2156            inline_styles=inline_styles,
2157        )
2158        with open(path, "wt", encoding="utf-8") as write_file:
2159            write_file.write(html)
2160
2161
2162if __name__ == "__main__":  # pragma: no cover
2163    console = Console()
2164
2165    console.log(
2166        "JSONRPC [i]request[/i]",
2167        5,
2168        1.3,
2169        True,
2170        False,
2171        None,
2172        {
2173            "jsonrpc": "2.0",
2174            "method": "subtract",
2175            "params": {"minuend": 42, "subtrahend": 23},
2176            "id": 3,
2177        },
2178    )
2179
2180    console.log("Hello, World!", "{'a': 1}", repr(console))
2181
2182    console.print(
2183        {
2184            "name": None,
2185            "empty": [],
2186            "quiz": {
2187                "sport": {
2188                    "answered": True,
2189                    "q1": {
2190                        "question": "Which one is correct team name in NBA?",
2191                        "options": [
2192                            "New York Bulls",
2193                            "Los Angeles Kings",
2194                            "Golden State Warriors",
2195                            "Huston Rocket",
2196                        ],
2197                        "answer": "Huston Rocket",
2198                    },
2199                },
2200                "maths": {
2201                    "answered": False,
2202                    "q1": {
2203                        "question": "5 + 7 = ?",
2204                        "options": [10, 11, 12, 13],
2205                        "answer": 12,
2206                    },
2207                    "q2": {
2208                        "question": "12 - 8 = ?",
2209                        "options": [1, 2, 3, 4],
2210                        "answer": 4,
2211                    },
2212                },
2213            },
2214        }
2215    )
2216    console.log("foo")
2217