failed CONTEXT UNSUBMITTED agent_error · 32 tool calls · 275 s · Textualize/rich
perf: reduce Console and RichHandler import time by deferring unused imports ## E2E Results | Import | master | This PR | Speedup | |---|---|---|---| | `from rich.console import Console` | 78.1ms | 52.2ms | **1.50x faster** | | `from rich.logging import RichHandler` | 99.4ms | 56.9ms | **1.75x faster** | | `import rich` | 18.2ms | 18.3ms | (already lean) | ## Summary Defer module-level imports that are only needed in specific code paths, move annotation-only imports to `TYPE_CHECKING`, and remove dead code: | File | Change | Savings | |---|---|---| | `logging.py` | Defer `Traceback` to `emit()` (only when `rich_tracebacks=True`) | ~20ms | | `logging.py` | `from __future__ import annotations` + `TYPE_CHECKING` for `Console`, `ConsoleRenderable`, `Highlighter`, `FormatTimeCallable` | ~6ms | | `logging.py` | Replace `pathlib.Path` → `os.path.basename` (also a minor runtime win) | ~4-5ms | | `console.py` | Eliminate `import inspect`; replace `isclass` → `isinstance(x, type)`, `currentframe` → `sys._getframe` | ~10ms | | `console.py` | Defer `pretty` to `Console.print()` | ~3-5ms | | `console.py` | Defer `scope` to `Console.log()` (only when `log_locals=True`) | ~3-5ms | | `console.py` | Defer `getpass` to `Console.input()` (only when `password=True`) | ~2ms | | `console.py` | Defer `html.escape` and `zlib` to export methods | ~2.3ms | | `console.py` | Remove dead `_svg_hash` function (unused since 113997ac, fixes latent NameError) | cleanup | | `segment.py` | Remove dead `logging` import (`getLogger` assigned but never used) | ~2-3ms | | `theme.py` | Defer `configparser` to `Theme.from_file()` | ~1.5ms | | `syntax.py` | Move `Console`, `ConsoleOptions`, `JustifyMethod`, `RenderResult` to `TYPE_CHECKING` | eliminates console.py from syntax import chain | | `protocol.py` | Replace `from inspect import isclass` → `isinstance(x, type)` | prepares for dataclasses removal | | `repr.py` | Defer `import inspect` to `auto_rich_repr()` | prepares for dataclasses removal | All deferred imports are still available at runtime — they're loaded when the code path that needs them is actually hit. The `TYPE_CHECKING` imports are annotation-only and never needed at runtime (files use `from __future__ import annotations`). ## Benchmarks Environment: Standard_D2s_v5 (non-burstable), CPython 3.13.13, hyperfine --min-runs 30 ### Per-module breakdown (this PR) | Module | Time | |---|---| | `import rich` | 18.2ms | | `Console` | 52.5ms | | `RichHandler` | 56.4ms | | `Syntax` | 64.7ms | | `Traceback` | 92.5ms | | `Markdown` | 103.0ms | ## Motivation pip vendors Rich, and `Console`/`RichHandler` are imported on every `pip` invocation. These savings directly improve startup time for pip and every other tool that imports Rich. ## Testing - `pytest tests/` — 952 passed, 25 skipped - `mypy -p rich --no-incremental` — no issues - `black --check` — passes ## Disclosure This PR was developed with AI assistance (codeflash-agent). Benchmarked in a controlled environment (dedicated non-burstable Azure VM). All tests verified across multiple Python implementations.
| # | Tool | Arguments | Result |
|---|---|---|---|
| 1 | run_command | {
"command": "grep -rn \"import inspect\" rich | head -20"
} | {"status": "ok", "stdout": "rich/_inspect.py:1:import inspect\nrich/console.py:1:import inspect\nrich/pretty.py:4:import inspect\nrich/repr.py:1:import inspect\full{"status": "ok", "stdout": "rich/_inspect.py:1:import inspect\nrich/console.py:1:import inspect\nrich/pretty.py:4:import inspect\nrich/repr.py:1:import inspect\nrich/diagnose.py:4:from rich import inspect\nrich/traceback.py:1:import inspect\n", "exit_code": 0} |
| 2-3 ×2 | read_file | {
"filepath": "rich/logging.py"
} | {"status": "ok", "filepath": "rich/logging.py", "content": "import logging\nfrom datetime import datetime\nfrom logging import Handler, LogRecord\nfrom pathlib full{"status": "ok", "filepath": "rich/logging.py", "content": "import logging\nfrom datetime import datetime\nfrom logging import Handler, LogRecord\nfrom pathlib import Path\nfrom types import ModuleType\nfrom typing import ClassVar, Iterable, List, Optional, Type, Union\n\nfrom rich._null_file import NullFile\n\nfrom . import get_console\nfrom ._log_render import FormatTimeCallable, LogRender\nfrom .console import Console, ConsoleRenderable\nfrom .highlighter import Highlighter, ReprHighlighter\nfrom .text import Text\nfrom .traceback import Traceback\n\n\nclass RichHandler(Handler):\n \"\"\"A logging handler that renders output with Rich. The time / level / message and file are displayed in columns.\n The level is color coded, and the message is syntax highlighted.\n\n Note:\n Be careful when enabling console markup in log messages if you have configured logging for libraries not\n under your control. If a dependency writes messages containing square brackets, it may not produce the intended output.\n\n Args:\n level (Union[int, str], optional): Log level. Defaults to logging.NOTSET.\n console (:class:`~rich.console.Console`, optional): Optional console instance to write logs.\n Default will use a global console instance writing to stdout.\n show_time (bool, optional): Show a column for the time. Defaults to True.\n omit_repeated_times (bool, optional): Omit repetition of the same time. Defaults to True.\n show_level (bool, optional): Show a column for the level. Defaults to True.\n show_path (bool, optional): Show the path to the original log call. Defaults to True.\n enable_link_path (bool, optional): Enable terminal link of path column to file. Defaults to True.\n highlighter (Highlighter, optional): Highlighter to style log messages, or None to use ReprHighlighter. Defaults to None.\n markup (bool, optional): Enable console markup in log messages. Defaults to Fals |
| 4 | read_file | {
"filepath": "rich/logging.py",
"start_line": 151,
"end_line": 297
} | {"status": "ok", "filepath": "rich/logging.py", "content": " theme=self.tracebacks_theme,\n word_wrap=self.tracebacks_word_wrap,\n show_locals=self.tracebacks_sfull{"status": "ok", "filepath": "rich/logging.py", "content": " theme=self.tracebacks_theme,\n word_wrap=self.tracebacks_word_wrap,\n show_locals=self.tracebacks_show_locals,\n locals_max_length=self.locals_max_length,\n locals_max_string=self.locals_max_string,\n suppress=self.tracebacks_suppress,\n max_frames=self.tracebacks_max_frames,\n )\n message = record.getMessage()\n if self.formatter:\n record.message = record.getMessage()\n formatter = self.formatter\n if hasattr(formatter, \"usesTime\") and formatter.usesTime():\n record.asctime = formatter.formatTime(record, formatter.datefmt)\n message = formatter.formatMessage(record)\n\n message_renderable = self.render_message(record, message)\n log_renderable = self.render(\n record=record, traceback=traceback, message_renderable=message_renderable\n )\n if isinstance(self.console.file, NullFile):\n # Handles pythonw, where stdout/stderr are null, and we return NullFile\n # instance from Console.file. In this case, we still want to make a log record\n # even though we won't be writing anything to a file.\n self.handleError(record)\n else:\n try:\n self.console.print(log_renderable)\n except Exception:\n self.handleError(record)\n\n def render_message(self, record: LogRecord, message: str) -> \"ConsoleRenderable\":\n \"\"\"Render message text in to Text.\n\n Args:\n record (LogRecord): logging Record.\n message (str): String containing log message.\n\n Returns:\n ConsoleRenderable: Renderable to display log message.\n \"\"\"\n use_markup = getattr(record, \"markup\", self.markup)\n message_ |
| 5 | edit_file | {
"filepath": "rich/logging.py",
"new_string": "from __future__ import annotations\n\nimport logging\nimport os\nfrom datetime import datetime\nfrom logging import Handler, LogRecord\nfrom types import ModuleType\nfrom typing import TYPE_CHECKING, ClassVar, Iterable, List, Optional, Type, Union\n\nif TYPE_CHECKING:\n from .console import Console, ConsoleRenderable\n from .highlighter import Highlighter\n from ._log_render import FormatTimeCallable\n\nfrom rich._null_file import NullFile\n\nfrom . import get_console\nfrom ._log_render import LogRender\nfrom .highlighter import ReprHighlighter\nfrom .text import Text",
"old_string": "import logging\nfrom datetime import datetime\nfrom logging import Handler, LogRecord\nfrom pathlib import Path\nfrom types import ModuleType\nfrom typing import ClassVar, Iterable, List, Optional, Type, Union\n\nfrom rich._null_file import NullFile\n\nfrom . import get_console\nfrom ._log_render import FormatTimeCallable, LogRender\nfrom .console import Console, ConsoleRenderable\nfrom .highlighter import Highlighter, ReprHighlighter\nfrom .text import Text\nfrom .traceback import Traceback"
} | {"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -1,18 +1,23 @@\full{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -1,18 +1,23 @@\n+from __future__ import annotations\n+\n import logging\n+import os\n from datetime import datetime\n from logging import Handler, LogRecord\n-from pathlib import Path\n from types import ModuleType\n-from typing import ClassVar, Iterable, List, Optional, Type, Union\n+from typing import TYPE_CHECKING, ClassVar, Iterable, List, Optional, Type, Union\n+\n+if TYPE_CHECKING:\n+ from .console import Console, ConsoleRenderable\n+ from .highlighter import Highlighter\n+ from ._log_render import FormatTimeCallable\n \n from rich._null_file import NullFile\n \n from . import get_console\n-from ._log_render import FormatTimeCallable, LogRender\n-from .console import Console, ConsoleRenderable\n-from .highlighter import Highlighter, ReprHighlighter\n+from ._log_render import LogRender\n+from .highlighter import ReprHighlighter\n from .text import Text\n-from .traceback import Traceback\n \n \n class RichHandler(Handler):\n", "is_truncated": false} |
| 6 | edit_file | {
"filepath": "rich/logging.py",
"new_string": " def emit(self, record: LogRecord) -> None:\n \"\"\"Invoked by logging.\"\"\"\n message = self.format(record)\n traceback = None\n if (\n self.rich_tracebacks\n and record.exc_info\n and record.exc_info != (None, None, None)\n ):\n from .traceback import Traceback\n exc_type, exc_value, exc_traceback = record.exc_info\n assert exc_type is not None\n assert exc_value is not None\n traceback = Traceback.from_exception(\n exc_type,\n exc_value,\n exc_traceback,\n width=self.tracebacks_width,\n code_width=self.tracebacks_code_width,\n extra_lines=self.tracebacks_extra_lines,",
"old_string": " def emit(self, record: LogRecord) -> None:\n \"\"\"Invoked by logging.\"\"\"\n message = self.format(record)\n traceback = None\n if (\n self.rich_tracebacks\n and record.exc_info\n and record.exc_info != (None, None, None)\n ):\n exc_type, exc_value, exc_traceback = record.exc_info\n assert exc_type is not None\n assert exc_value is not None\n traceback = Traceback.from_exception(\n exc_type,\n exc_value,\n exc_traceback,\n width=self.tracebacks_width,\n code_width=self.tracebacks_code_width,\n extra_lines=self.tracebacks_extra_lines,"
} | {"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -143,6 +143,7 @full{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -143,6 +143,7 @@\n and record.exc_info\n and record.exc_info != (None, None, None)\n ):\n+ from .traceback import Traceback\n exc_type, exc_value, exc_traceback = record.exc_info\n assert exc_type is not None\n assert exc_value is not None\n", "is_truncated": false} |
| 7 | edit_file | {
"filepath": "rich/logging.py",
"new_string": " log_renderable = self._log_render(\n self.console,\n [message_renderable] if not traceback else [message_renderable, traceback],\n log_time=log_time,\n time_format=time_format,\n level=level,\n path=os.path.basename(record.pathname),\n line_no=record.lineno,\n link_path=record.pathname if self.enable_link_path else None,\n )",
"old_string": " log_renderable = self._log_render(\n self.console,\n [message_renderable] if not traceback else [message_renderable, traceback],\n log_time=log_time,\n time_format=time_format,\n level=level,\n path=Path(record.pathname).name,\n line_no=record.lineno,\n link_path=record.pathname if self.enable_link_path else None,\n )"
} | {"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}full{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"} |
| 8 | read_file | {
"end_line": 250,
"filepath": "rich/logging.py",
"start_line": 200
} | {"status": "ok", "filepath": "rich/logging.py", "content": "\n highlighter = getattr(record, \"highlighter\", self.highlighter)\n if highlighter:\n message_textfull{"status": "ok", "filepath": "rich/logging.py", "content": "\n highlighter = getattr(record, \"highlighter\", self.highlighter)\n if highlighter:\n message_text = highlighter(message_text)\n\n if self.keywords is None:\n self.keywords = self.KEYWORDS\n\n if self.keywords:\n message_text.highlight_words(self.keywords, \"logging.keyword\")\n\n return message_text\n\n def render(\n self,\n *,\n record: LogRecord,\n traceback: Optional[Traceback],\n message_renderable: \"ConsoleRenderable\",\n ) -> \"ConsoleRenderable\":\n \"\"\"Render log for display.\n\n Args:\n record (LogRecord): logging Record.\n traceback (Optional[Traceback]): Traceback instance or None for no Traceback.\n message_renderable (ConsoleRenderable): Renderable (typically Text) containing log message contents.\n\n Returns:\n ConsoleRenderable: Renderable to display log.\n \"\"\"\n path = Path(record.pathname).name\n level = self.get_level_text(record)\n time_format = None if self.formatter is None else self.formatter.datefmt\n log_time = datetime.fromtimestamp(record.created)\n\n log_renderable = self._log_render(\n self.console,\n [message_renderable] if not traceback else [message_renderable, traceback],\n log_time=log_time,\n time_format=time_format,\n level=level,\n path=path,\n line_no=record.lineno,\n link_path=record.pathname if self.enable_link_path else None,\n )\n return log_renderable\n\n\nif __name__ == \"__main__\": # pragma: no cover\n from time import sleep\n\n", "start_line": 200, "end_line": 250, "total_lines": 303, "is_truncated": false} |
| 9 | edit_file | {
"filepath": "rich/logging.py",
"new_string": " path = os.path.basename(record.pathname)\n level = self.get_level_text(record)",
"old_string": " path = Path(record.pathname).name\n level = self.get_level_text(record)"
} | {"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -227,7 +227,7 @full{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -227,7 +227,7 @@\n Returns:\n ConsoleRenderable: Renderable to display log.\n \"\"\"\n- path = Path(record.pathname).name\n+ path = os.path.basename(record.pathname)\n level = self.get_level_text(record)\n time_format = None if self.formatter is None else self.formatter.datefmt\n log_time = datetime.fromtimestamp(record.created)\n", "is_truncated": false} |
| 10 | read_file | {
"filepath": "rich/console.py"
} | {"status": "ok", "filepath": "rich/console.py", "content": "import inspect\nimport os\nimport sys\nimport threading\nimport zlib\nfrom abc import ABC, abstractmfull{"status": "ok", "filepath": "rich/console.py", "content": "import inspect\nimport os\nimport sys\nimport threading\nimport zlib\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom functools import wraps\nfrom getpass import getpass\nfrom html import escape\nfrom inspect import isclass\nfrom itertools import islice\nfrom math import ceil\nfrom time import monotonic\nfrom types import FrameType, ModuleType, TracebackType\nfrom typing import (\n IO,\n TYPE_CHECKING,\n Any,\n Callable,\n Dict,\n Iterable,\n List,\n Literal,\n Mapping,\n NamedTuple,\n Optional,\n Protocol,\n TextIO,\n Tuple,\n Type,\n Union,\n cast,\n runtime_checkable,\n)\n\nfrom rich._null_file import NULL_FILE\n\nfrom . import errors, themes\nfrom ._emoji_replace import _emoji_replace\nfrom ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT\nfrom ._fileno import get_fileno\nfrom ._log_render import FormatTimeCallable, LogRender\nfrom .align import Align, AlignMethod\nfrom .color import ColorSystem, blend_rgb\nfrom .control import Control\nfrom .emoji import EmojiVariant\nfrom .highlighter import NullHighlighter, ReprHighlighter\nfrom .markup import render as render_markup\nfrom .measure import Measurement, measure_renderables\nfrom .pager import Pager, SystemPager\nfrom .pretty import Pretty, is_expandable\nfrom .protocol import rich_cast\nfrom .region import Region\nfrom .scope import render_scope\nfrom .screen import Screen\nfrom .segment import Segment\nfrom .style import Style, StyleType\nfrom .styled import Styled\nfrom .terminal_theme import DEFAULT_TERMINAL_THEME, SVG_EXPORT_THEME, TerminalTheme\nfrom .text import Text, TextType\nfrom .theme import Theme, ThemeStack\n\nif TYPE_CHECKING:\n from ._windows import WindowsConsoleFeatures\n from .live import Live\n from .status import Status\n\nJUPYTER_DEFAULT_COLUMNS = 115\nJUPYTER_DEFAULT_LINES = 100\nWINDOWS = |
| 11 | read_file | {
"end_line": 150,
"filepath": "rich/console.py",
"start_line": 1
} | {"status": "ok", "filepath": "rich/console.py", "content": "import inspect\nimport os\nimport sys\nimport threading\nimport zlib\nfrom abc import ABC, abstractmfull{"status": "ok", "filepath": "rich/console.py", "content": "import inspect\nimport os\nimport sys\nimport threading\nimport zlib\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom functools import wraps\nfrom getpass import getpass\nfrom html import escape\nfrom inspect import isclass\nfrom itertools import islice\nfrom math import ceil\nfrom time import monotonic\nfrom types import FrameType, ModuleType, TracebackType\nfrom typing import (\n IO,\n TYPE_CHECKING,\n Any,\n Callable,\n Dict,\n Iterable,\n List,\n Literal,\n Mapping,\n NamedTuple,\n Optional,\n Protocol,\n TextIO,\n Tuple,\n Type,\n Union,\n cast,\n runtime_checkable,\n)\n\nfrom rich._null_file import NULL_FILE\n\nfrom . import errors, themes\nfrom ._emoji_replace import _emoji_replace\nfrom ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT\nfrom ._fileno import get_fileno\nfrom ._log_render import FormatTimeCallable, LogRender\nfrom .align import Align, AlignMethod\nfrom .color import ColorSystem, blend_rgb\nfrom .control import Control\nfrom .emoji import EmojiVariant\nfrom .highlighter import NullHighlighter, ReprHighlighter\nfrom .markup import render as render_markup\nfrom .measure import Measurement, measure_renderables\nfrom .pager import Pager, SystemPager\nfrom .pretty import Pretty, is_expandable\nfrom .protocol import rich_cast\nfrom .region import Region\nfrom .scope import render_scope\nfrom .screen import Screen\nfrom .segment import Segment\nfrom .style import Style, StyleType\nfrom .styled import Styled\nfrom .terminal_theme import DEFAULT_TERMINAL_THEME, SVG_EXPORT_THEME, TerminalTheme\nfrom .text import Text, TextType\nfrom .theme import Theme, ThemeStack\n\nif TYPE_CHECKING:\n from ._windows import WindowsConsoleFeatures\n from .live import Live\n from .status import Status\n\nJUPYTER_DEFAULT_COLUMNS = 115\nJUPYTER_DEFAULT_LINES = 100\nWINDOWS = |
| 12 | edit_file | {
"filepath": "rich/console.py",
"new_string": "import os\nimport sys\nimport threading\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom functools import wraps\nfrom itertools import islice\nfrom math import ceil\nfrom time import monotonic\nfrom types import FrameType, ModuleType, TracebackType\nfrom typing import (\n IO,\n TYPE_CHECKING,\n Any,\n Callable,\n Dict,\n Iterable,\n List,\n Literal,\n Mapping,\n NamedTuple,\n Optional,\n Protocol,\n TextIO,\n Tuple,\n Type,\n Union,\n cast,\n runtime_checkable,\n)",
"old_string": "import inspect\nimport os\nimport sys\nimport threading\nimport zlib\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nfrom functools import wraps\nfrom getpass import getpass\nfrom html import escape\nfrom inspect import isclass\nfrom itertools import islice\nfrom math import ceil\nfrom time import monotonic\nfrom types import FrameType, ModuleType, TracebackType\nfrom typing import (\n IO,\n TYPE_CHECKING,\n Any,\n Callable,\n Dict,\n Iterable,\n List,\n Literal,\n Mapping,\n NamedTuple,\n Optional,\n Protocol,\n TextIO,\n Tuple,\n Type,\n Union,\n cast,\n runtime_checkable,\n)"
} | {"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1,15 +1,10 @@\full{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1,15 +1,10 @@\n-import inspect\n import os\n import sys\n import threading\n-import zlib\n from abc import ABC, abstractmethod\n from dataclasses import dataclass, field\n from datetime import datetime\n from functools import wraps\n-from getpass import getpass\n-from html import escape\n-from inspect import isclass\n from itertools import islice\n from math import ceil\n from time import monotonic\n", "is_truncated": false} |
| 13 | run_command | {
"command": "grep -rn \"isclass\" rich/console.py"
} | {"status": "ok", "stdout": "1319: if hasattr(renderable, \"__rich_console__\") and not isclass(renderable):\n", "exit_code": 0}full{"status": "ok", "stdout": "1319: if hasattr(renderable, \"__rich_console__\") and not isclass(renderable):\n", "exit_code": 0} |
| 14 | edit_file | {
"filepath": "rich/console.py",
"new_string": " if hasattr(renderable, \"__rich_console__\") and not isinstance(renderable, type):",
"old_string": " if hasattr(renderable, \"__rich_console__\") and not isclass(renderable):"
} | {"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1316,7 +1316,7full{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1316,7 +1316,7 @@\n render_iterable: RenderResult\n \n renderable = rich_cast(renderable)\n- if hasattr(renderable, \"__rich_console__\") and not isclass(renderable):\n+ if hasattr(renderable, \"__rich_console__\") and not isinstance(renderable, type):\n render_iterable = renderable.__rich_console__(self, _options)\n elif isinstance(renderable, str):\n text_renderable = self.render_str(\n", "is_truncated": false} |
| 15 | run_command | {
"command": "grep -rn \"currentframe\" rich/console.py"
} | {"status": "ok", "stdout": "1899: currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n1905: currentframe (Callable[[], Optional[FrameType]]full{"status": "ok", "stdout": "1899: currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n1905: currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to\n1906: retrieve the current frame. Defaults to ``inspect.currentframe``.\n1918: frame = currentframe()\n1920: # Use the faster currentframe where implemented\n", "exit_code": 0} |
| 16 | read_file | {
"end_line": 1925,
"filepath": "rich/console.py",
"start_line": 1890
} | {"status": "ok", "filepath": "rich/console.py", "content": " show_locals=show_locals,\n suppress=suppress,\n max_frames=max_frames,\n )\n self.print(traceback)\full{"status": "ok", "filepath": "rich/console.py", "content": " show_locals=show_locals,\n suppress=suppress,\n max_frames=max_frames,\n )\n self.print(traceback)\n\n @staticmethod\n def _caller_frame_info(\n offset: int,\n currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n ) -> Tuple[str, int, Dict[str, Any]]:\n \"\"\"Get caller frame information.\n\n Args:\n offset (int): the caller offset within the current frame stack.\n currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to\n retrieve the current frame. Defaults to ``inspect.currentframe``.\n\n Returns:\n Tuple[str, int, Dict[str, Any]]: A tuple containing the filename, the line number and\n the dictionary of local variables associated with the caller frame.\n\n Raises:\n RuntimeError: If the stack offset is invalid.\n \"\"\"\n # Ignore the frame of this local helper\n offset += 1\n\n frame = currentframe()\n if frame is not None:\n # Use the faster currentframe where implemented\n while offset and frame is not None:\n frame = frame.f_back\n offset -= 1\n assert frame is not None\n return frame.f_code.co_filename, frame.f_lineno, frame.f_locals\n", "start_line": 1890, "end_line": 1925, "total_lines": 2679, "is_truncated": false} |
| 17 | edit_file | {
"filepath": "rich/console.py",
"new_string": " @staticmethod\n def _caller_frame_info(\n offset: int,\n currentframe: Callable[[], Optional[FrameType]] = sys._getframe,\n ) -> Tuple[str, int, Dict[str, Any]]:",
"old_string": " @staticmethod\n def _caller_frame_info(\n offset: int,\n currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n ) -> Tuple[str, int, Dict[str, Any]]:"
} | {"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1896,7 +1896,7full{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1896,7 +1896,7 @@\n @staticmethod\n def _caller_frame_info(\n offset: int,\n- currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n+ currentframe: Callable[[], Optional[FrameType]] = sys._getframe,\n ) -> Tuple[str, int, Dict[str, Any]]:\n \"\"\"Get caller frame information.\n \n", "is_truncated": false} |
| 18 | run_command | {
"command": "grep -rn \"pretty\" rich/console.py"
} | {"status": "ok", "stdout": "48:from .pretty import Pretty, is_expandable\n1620: :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markfull{"status": "ok", "stdout": "48:from .pretty import Pretty, is_expandable\n1620: :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markup, but will\n", "exit_code": 0} |
| 19 | read_file | {
"end_line": 1400,
"filepath": "rich/console.py",
"start_line": 1300
} | {"status": "ok", "filepath": "rich/console.py", "content": " This method contains the logic for rendering objects with the console protocol.\n You are unlikely full{"status": "ok", "filepath": "rich/console.py", "content": " This method contains the logic for rendering objects with the console protocol.\n You are unlikely to need to use it directly, unless you are extending the library.\n\n Args:\n renderable (RenderableType): An object supporting the console protocol, or\n an object that may be converted to a string.\n options (ConsoleOptions, optional): An options object, or None to use self.options. Defaults to None.\n\n Returns:\n Iterable[Segment]: An iterable of segments that may be rendered.\n \"\"\"\n\n _options = options or self.options\n if _options.max_width < 1:\n # No space to render anything. This prevents potential recursion errors.\n return\n render_iterable: RenderResult\n\n renderable = rich_cast(renderable)\n if hasattr(renderable, \"__rich_console__\") and not isinstance(renderable, type):\n render_iterable = renderable.__rich_console__(self, _options)\n elif isinstance(renderable, str):\n text_renderable = self.render_str(\n renderable, highlight=_options.highlight, markup=_options.markup\n )\n render_iterable = text_renderable.__rich_console__(self, _options)\n else:\n raise errors.NotRenderableError(\n f\"Unable to render {renderable!r}; \"\n \"A str, Segment or object with __rich_console__ method is required\"\n )\n\n try:\n iter_render = iter(render_iterable)\n except TypeError:\n raise errors.NotRenderableError(\n f\"object {render_iterable!r} is not renderable\"\n )\n _Segment = Segment\n _options = _options.reset_height()\n for render_output in iter_render:\n if isinstance(render_output, _Segment):\n yield render_output\n |
| 20 | run_command | {
"command": "grep -n \"def print(\" rich/console.py"
} | {"status": "ok", "stdout": "1643: def print(\n", "exit_code": 0}full{"status": "ok", "stdout": "1643: def print(\n", "exit_code": 0} |
| 21 | read_file | {
"end_line": 1700,
"filepath": "rich/console.py",
"start_line": 1640
} | {"status": "ok", "filepath": "rich/console.py", "content": " end=end,\n )\n\n def print(\n self,\n *objects: Any,\n sep: str = \" \",\n end: str = \"\\n\",\n stfull{"status": "ok", "filepath": "rich/console.py", "content": " end=end,\n )\n\n def print(\n self,\n *objects: Any,\n sep: str = \" \",\n end: str = \"\\n\",\n style: Optional[Union[str, Style]] = None,\n justify: Optional[JustifyMethod] = None,\n overflow: Optional[OverflowMethod] = None,\n no_wrap: Optional[bool] = None,\n emoji: Optional[bool] = None,\n markup: Optional[bool] = None,\n highlight: Optional[bool] = None,\n width: Optional[int] = None,\n height: Optional[int] = None,\n crop: bool = True,\n soft_wrap: Optional[bool] = None,\n new_line_start: bool = False,\n ) -> None:\n \"\"\"Print to the console.\n\n Args:\n objects (positional args): Objects to log to the terminal.\n sep (str, optional): String to write between print data. Defaults to \" \".\n end (str, optional): String to write at end of print data. Defaults to \"\\\\\\\\n\".\n style (Union[str, Style], optional): A style to apply to output. Defaults to None.\n justify (str, optional): Justify method: \"default\", \"left\", \"right\", \"center\", or \"full\". Defaults to ``None``.\n overflow (str, optional): Overflow method: \"ignore\", \"crop\", \"fold\", or \"ellipsis\". Defaults to None.\n no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to None.\n emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to ``None``.\n markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to ``None``.\n highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to ``None``.\n width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``.\n crop ( |
| 22 | run_command | {
"command": "grep -n \"def log(\" rich/console.py"
} | {"status": "ok", "stdout": "1931: def log(\n", "exit_code": 0}full{"status": "ok", "stdout": "1931: def log(\n", "exit_code": 0} |
| 23 | read_file | {
"end_line": 2000,
"filepath": "rich/console.py",
"start_line": 1930
} | {"status": "ok", "filepath": "rich/console.py", "content": "\n def log(\n self,\n *objects: Any,\n sep: str = \" \",\n end: str = \"\\n\",\n style: Optional[Unifull{"status": "ok", "filepath": "rich/console.py", "content": "\n def log(\n self,\n *objects: Any,\n sep: str = \" \",\n end: str = \"\\n\",\n style: Optional[Union[str, Style]] = None,\n justify: Optional[JustifyMethod] = None,\n emoji: Optional[bool] = None,\n markup: Optional[bool] = None,\n highlight: Optional[bool] = None,\n log_locals: bool = False,\n _stack_offset: int = 1,\n ) -> None:\n \"\"\"Log rich content to the terminal.\n\n Args:\n objects (positional args): Objects to log to the terminal.\n sep (str, optional): String to write between print data. Defaults to \" \".\n end (str, optional): String to write at end of print data. Defaults to \"\\\\\\\\n\".\n style (Union[str, Style], optional): A style to apply to output. Defaults to None.\n justify (str, optional): One of \"left\", \"right\", \"center\", or \"full\". Defaults to ``None``.\n emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.\n markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None.\n highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.\n log_locals (bool, optional): Boolean to enable logging of locals where ``log()``\n was called. Defaults to False.\n _stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1.\n \"\"\"\n if not objects:\n objects = (NewLine(),)\n\n render_hooks = self._render_hooks[:]\n\n with self:\n renderables = self._collect_renderables(\n objects,\n sep,\n end,\n justify=justify,\n emoji=emoji,\n markup=markup,\n |
| 24 | run_command | {
"command": "grep -n \"def input(\" rich/console.py"
} | {"status": "ok", "stdout": "2138: def input(\n", "exit_code": 0}full{"status": "ok", "stdout": "2138: def input(\n", "exit_code": 0} |
| 25 | read_file | {
"end_line": 2200,
"filepath": "rich/console.py",
"start_line": 2130
} | {"status": "ok", "filepath": "rich/console.py", "content": " )\n )\n elif not (not_terminal and control):\n append(text)\n\n rendered = \"\".join(output)\n retufull{"status": "ok", "filepath": "rich/console.py", "content": " )\n )\n elif not (not_terminal and control):\n append(text)\n\n rendered = \"\".join(output)\n return rendered\n\n def input(\n self,\n prompt: TextType = \"\",\n *,\n markup: bool = True,\n emoji: bool = True,\n password: bool = False,\n stream: Optional[TextIO] = None,\n ) -> str:\n \"\"\"Displays a prompt and waits for input from the user. The prompt may contain color / style.\n\n 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.\n\n Args:\n prompt (Union[str, Text]): Text to render in the prompt.\n markup (bool, optional): Enable console markup (requires a str prompt). Defaults to True.\n emoji (bool, optional): Enable emoji (requires a str prompt). Defaults to True.\n password: (bool, optional): Hide typed text. Defaults to False.\n stream: (TextIO, optional): Optional file to read input from (rather than stdin). Defaults to None.\n\n Returns:\n str: Text read from stdin.\n \"\"\"\n if prompt:\n self.print(prompt, markup=markup, emoji=emoji, end=\"\")\n if password:\n result = getpass(\"\", stream=stream)\n else:\n if stream:\n result = stream.readline()\n else:\n result = input()\n return result\n\n def export_text(self, *, clear: bool = True, styles: bool = False) -> str:\n \"\"\"Generate text from console contents (requires record=True argument in constructor).\n\n Args:\n clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.\n styles (bool, optional): If ``True``, ans |
| 26 | edit_file | {
"filepath": "rich/console.py",
"new_string": " if password:\n from getpass import getpass\n result = getpass(\"\", stream=stream)\n else:",
"old_string": " if password:\n result = getpass(\"\", stream=stream)\n else:"
} | {"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -2161,6 +2161,7full{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -2161,6 +2161,7 @@\n if prompt:\n self.print(prompt, markup=markup, emoji=emoji, end=\"\")\n if password:\n+ from getpass import getpass\n result = getpass(\"\", stream=stream)\n else:\n if stream:\n", "is_truncated": false} |
| 27 | run_command | {
"command": "grep -rn \"export_text\" rich/console.py"
} | {"status": "ok", "stdout": "602: required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.\n2173: def export_text(sefull{"status": "ok", "stdout": "602: required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.\n2173: def export_text(self, *, clear: bool = True, styles: bool = False) -> str:\n2215: text = self.export_text(clear=clear, styles=styles)\n", "exit_code": 0} |
| 28 | run_command | {
"command": "grep -rn \"export_html\" rich/console.py"
} | {"status": "ok", "stdout": "602: required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.\n2219: def export_html(\nfull{"status": "ok", "stdout": "602: required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.\n2219: def export_html(\n2316: html = self.export_html(\n", "exit_code": 0} |
| 29 | read_file | {
"end_line": 2300,
"filepath": "rich/console.py",
"start_line": 2210
} | {"status": "ok", "filepath": "rich/console.py", "content": " clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.\n styles (bool, full{"status": "ok", "filepath": "rich/console.py", "content": " clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.\n styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text.\n Defaults to ``False``.\n\n \"\"\"\n text = self.export_text(clear=clear, styles=styles)\n with open(path, \"w\", encoding=\"utf-8\") as write_file:\n write_file.write(text)\n\n def export_html(\n self,\n *,\n theme: Optional[TerminalTheme] = None,\n clear: bool = True,\n code_format: Optional[str] = None,\n inline_styles: bool = False,\n ) -> str:\n \"\"\"Generate HTML from console contents (requires record=True argument in constructor).\n\n Args:\n theme (TerminalTheme, optional): TerminalTheme object containing console colors.\n clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``.\n code_format (str, optional): Format string to render HTML. In addition to '{foreground}',\n '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``.\n inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files\n larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag.\n Defaults to False.\n\n Returns:\n str: String containing console contents as HTML.\n \"\"\"\n assert (\n self.record\n ), \"To export console contents set record=True in the constructor or instance\"\n fragments: List[str] = []\n append = fragments.append\n _theme = theme or DEFAULT_TERMINAL_THEME\n stylesheet = \"\"\n\n render_code_format = CONSOLE_HTML_FORMAT if code_format is None else code_format\n\n with self._record_buffer_lo |
| 30 | edit_file | {
"filepath": "rich/console.py",
"new_string": " if inline_styles:\n from html import escape\n for text, style, _ in Segment.filter_control(\n Segment.simplify(self._record_buffer)\n ):\n text = escape(text)\n if style:\n rule = style.get_html_style(_theme)\n if style.link:\n text = f'<a href=\"{style.link}\">{text}</a>'\n text = f'<span style=\"{rule}\">{text}</span>' if rule else text\n append(text)\n else:\n from html import escape\n styles: Dict[str, int] = {}\n for text, style, _ in Segment.filter_control(\n Segment.simplify(self._record_buffer)\n ):\n text = escape(text)\n if style:\n rule = style.get_html_style(_theme)\n style_number = styles.setdefault(rule, len(styles) + 1)\n if style.link:\n text = f'<a class=\"r{style_number}\" href=\"{style.link}\">{text}</a>'\n else:\n text = f'<span class=\"r{style_number}\">{text}</span>'\n append(text)",
"old_string": " if inline_styles:\n for text, style, _ in Segment.filter_control(\n Segment.simplify(self._record_buffer)\n ):\n text = escape(text)\n if style:\n rule = style.get_html_style(_theme)\n if style.link:\n text = f'<a href=\"{style.link}\">{text}</a>'\n text = f'<span style=\"{rule}\">{text}</span>' if rule else text\n append(text)\n else:\n styles: Dict[str, int] = {}\n for text, style, _ in Segment.filter_control(\n Segment.simplify(self._record_buffer)\n ):\n text = escape(text)\n if style:\n rule = style.get_html_style(_theme)\n style_number = styles.setdefault(rule, len(styles) + 1)\n if style.link:\n text = f'<a class=\"r{style_number}\" href=\"{style.link}\">{text}</a>'\n else:\n text = f'<span class=\"r{style_number}\">{text}</span>'\n append(text)"
} | {"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "flexible", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -2250,6 +225full{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "flexible", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -2250,6 +2250,7 @@\n \n with self._record_buffer_lock:\n if inline_styles:\n+ from html import escape\n for text, style, _ in Segment.filter_control(\n Segment.simplify(self._record_buffer)\n ):\n@@ -2261,6 +2262,7 @@\n text = f'<span style=\"{rule}\">{text}</span>' if rule else text\n append(text)\n else:\n+ from html import escape\n styles: Dict[str, int] = {}\n for text, style, _ in Segment.filter_control(\n Segment.simplify(self._record_buffer)\n", "is_truncated": false} |
| 31 | run_command | {
"command": "grep -rn \"export_svg\" rich/console.py"
} | {"status": "ok", "stdout": "602: required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.\n2327: def export_svg(\n2full{"status": "ok", "stdout": "602: required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False.\n2327: def export_svg(\n2605: svg = self.export_svg(\n", "exit_code": 0} |
| 32 | read_file | {
"end_line": 2400,
"filepath": "rich/console.py",
"start_line": 2320
} | {"status": "ok", "filepath": "rich/console.py", "content": " clear=clear,\n code_format=code_format,\n inline_styles=inline_styles,\n )\n with open(path, \"w\",full{"status": "ok", "filepath": "rich/console.py", "content": " clear=clear,\n code_format=code_format,\n inline_styles=inline_styles,\n )\n with open(path, \"w\", encoding=\"utf-8\") as write_file:\n write_file.write(html)\n\n def export_svg(\n self,\n *,\n title: str = \"Rich\",\n theme: Optional[TerminalTheme] = None,\n clear: bool = True,\n code_format: str = CONSOLE_SVG_FORMAT,\n font_aspect_ratio: float = 0.61,\n unique_id: Optional[str] = None,\n ) -> str:\n \"\"\"\n Generate an SVG from the console contents (requires record=True in Console constructor).\n\n Args:\n title (str, optional): The title of the tab in the output image\n theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal\n clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``\n code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables\n into the string in order to form the final SVG output. The default template used and the variables\n injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable.\n font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format``\n string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font).\n If you aren't specifying a different font inside ``code_format``, you probably don't need this.\n unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node\n ids). If not set, this defaults to a computed value based on the recorded content.\n \"\"\"\n\n from rich.cells import cell_len\n\n style_cache: Dict[Style, str] = {}\n\n |
diff --git a/rich/console.py b/rich/console.py
index ad92d529..1ced91f9 100644
--- a/rich/console.py
+++ b/rich/console.py
@@ -1,15 +1,10 @@
-import inspect
import os
import sys
import threading
-import zlib
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from functools import wraps
-from getpass import getpass
-from html import escape
-from inspect import isclass
from itertools import islice
from math import ceil
from time import monotonic
@@ -1321,7 +1316,7 @@ class Console:
render_iterable: RenderResult
renderable = rich_cast(renderable)
- if hasattr(renderable, "__rich_console__") and not isclass(renderable):
+ if hasattr(renderable, "__rich_console__") and not isinstance(renderable, type):
render_iterable = renderable.__rich_console__(self, _options)
elif isinstance(renderable, str):
text_renderable = self.render_str(
@@ -1901,7 +1896,7 @@ class Console:
@staticmethod
def _caller_frame_info(
offset: int,
- currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,
+ currentframe: Callable[[], Optional[FrameType]] = sys._getframe,
) -> Tuple[str, int, Dict[str, Any]]:
"""Get caller frame information.
@@ -2166,6 +2161,7 @@ class Console:
if prompt:
self.print(prompt, markup=markup, emoji=emoji, end="")
if password:
+ from getpass import getpass
result = getpass("", stream=stream)
else:
if stream:
@@ -2254,6 +2250,7 @@ class Console:
with self._record_buffer_lock:
if inline_styles:
+ from html import escape
for text, style, _ in Segment.filter_control(
Segment.simplify(self._record_buffer)
):
@@ -2265,6 +2262,7 @@ class Console:
text = f'<span style="{rule}">{text}</span>' if rule else text
append(text)
else:
+ from html import escape
styles: Dict[str, int] = {}
for text, style, _ in Segment.filter_control(
Segment.simplify(self._record_buffer)
diff --git a/rich/logging.py b/rich/logging.py
index c3e7a5f6..1e3272d4 100644
--- a/rich/logging.py
+++ b/rich/logging.py
@@ -1,18 +1,23 @@
+from __future__ import annotations
+
import logging
+import os
from datetime import datetime
from logging import Handler, LogRecord
-from pathlib import Path
from types import ModuleType
-from typing import ClassVar, Iterable, List, Optional, Type, Union
+from typing import TYPE_CHECKING, ClassVar, Iterable, List, Optional, Type, Union
+
+if TYPE_CHECKING:
+ from .console import Console, ConsoleRenderable
+ from .highlighter import Highlighter
+ from ._log_render import FormatTimeCallable
from rich._null_file import NullFile
from . import get_console
-from ._log_render import FormatTimeCallable, LogRender
-from .console import Console, ConsoleRenderable
-from .highlighter import Highlighter, ReprHighlighter
+from ._log_render import LogRender
+from .highlighter import ReprHighlighter
from .text import Text
-from .traceback import Traceback
class RichHandler(Handler):
@@ -138,6 +143,7 @@ class RichHandler(Handler):
and record.exc_info
and record.exc_info != (None, None, None)
):
+ from .traceback import Traceback
exc_type, exc_value, exc_traceback = record.exc_info
assert exc_type is not None
assert exc_value is not None
@@ -221,7 +227,7 @@ class RichHandler(Handler):
Returns:
ConsoleRenderable: Renderable to display log.
"""
- path = Path(record.pathname).name
+ path = os.path.basename(record.pathname)
level = self.get_level_text(record)
time_format = None if self.formatter is None else self.formatter.datefmt
log_time = datetime.fromtimestamp(record.created)
============= FAILURES ===================================
_______________________________ test_export_svg ________________________________
def test_export_svg() -> None:
console = Console(record=True, width=100)
console.print(
"[b red on blue reverse]foo[/] [blink][link=https://example.org]Click[/link]"
)
> svg = console.export_svg()
tests/test_console.py:539:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <console width=100 None>
def export_svg(
self,
*,
title: str = "Rich",
theme: Optional[TerminalTheme] = None,
clear: bool = True,
code_format: str = CONSOLE_SVG_FORMAT,
font_aspect_ratio: float = 0.61,
unique_id: Optional[str] = None,
) -> str:
"""
Generate an SVG from the console contents (requires record=True in Console constructor).
Args:
title (str, optional): The title of the tab in the output image
theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal
clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``
code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables
into the string in order to form the final SVG output. The default template used and the variables
injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable.
font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format``
string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font).
If you aren't specifying a different font inside ``code_format``, you probably don't need this.
unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node
ids). If not set, this defaults to a computed value based on the recorded content.
"""
from rich.cells import cell_len
style_cache: Dict[Style, str] = {}
def get_svg_style(style: Style) -> str:
"""Convert a Style to CSS rules for SVG."""
if style in style_cache:
return style_cache[style]
css_rules = []
color = (
_theme.foreground_color
if (style.color is None or style.color.is_default)
else style.color.get_truecolor(_theme)
)
bgcolor = (
_theme.background_color
if (style.bgcolor is None or style.bgcolor.is_default)
else style.bgcolor.get_truecolor(_theme)
)
if style.reverse:
color, bgcolor = bgcolor, color
if style.dim:
color = blend_rgb(color, bgcolor, 0.4)
css_rules.append(f"fill: {color.hex}")
if style.bold:
css_rules.append("font-weight: bold")
if style.italic:
css_rules.append("font-style: italic;")
if style.underline:
css_rules.append("text-decoration: underline;")
if style.strike:
css_rules.append("text-decoration: line-through;")
css = ";".join(css_rules)
style_cache[style] = css
return css
_theme = theme or SVG_EXPORT_THEME
width = self.width
char_height = 20
char_width = char_height * font_aspect_ratio
line_height = char_height * 1.22
margin_top = 1
margin_right = 1
margin_bottom = 1
margin_left = 1
padding_top = 40
padding_right = 8
padding_bottom = 8
padding_left = 8
padding_width = padding_left + padding_right
padding_height = padding_top + padding_bottom
margin_width = margin_left + margin_right
margin_height = margin_top + margin_bottom
text_backgrounds: List[str] = []
text_group: List[str] = []
classes: Dict[str, int] = {}
style_no = 1
def escape_text(text: str) -> str:
"""HTML escape text and replace spaces with nbsp."""
return escape(text).replace(" ", " ")
def make_tag(
name: str, content: Optional[str] = None, **attribs: object
) -> str:
"""Make a tag from name, content, and attributes."""
def stringify(value: object) -> str:
if isinstance(value, (float)):
return format(value, "g")
return str(value)
tag_attribs = " ".join(
f'{k.lstrip("_").replace("_", "-")}="{stringify(v)}"'
for k, v in attribs.items()
)
return (
f"<{name} {tag_attribs}>{content}</{name}>"
if content
else f"<{name} {tag_attribs}/>"
)
with self._record_buffer_lock:
segments = list(Segment.filter_control(self._record_buffer))
if clear:
self._record_buffer.clear()
if unique_id is None:
unique_id = "terminal-" + str(
> zlib.adler32(
("".join(repr(segment) for segment in segments)).encode(
"utf-8",
"ignore",
)
+ title.encode("utf-8", "ignore")
)
)
E NameError: name 'zlib' is not defined. Did you forget to import 'zlib'?
rich/console.py:2449: NameError
----------------------------- Captured stdout call -----------------------------
foo Click
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 56 passed in 0.46s