← pod_r5_v1i

rich_4070

failed WRONG_FIX UNSUBMITTED wrong_fix_unsubmitted(timeout) · 19 tool calls · 0 s · Textualize/rich

Task input

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 calls (0)

#ToolArgumentsResult
No trace captured.

Patch

diff --git a/rich/console.py b/rich/console.py
index ad92d529..4d85ffc9 100644
--- a/rich/console.py
+++ b/rich/console.py
@@ -1,15 +1,15 @@
-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
@@ -50,10 +50,10 @@ from .highlighter import NullHighlighter, ReprHighlighter
 from .markup import render as render_markup
 from .measure import Measurement, measure_renderables
 from .pager import Pager, SystemPager
-from .pretty import Pretty, is_expandable
+
 from .protocol import rich_cast
 from .region import Region
-from .scope import render_scope
+
 from .screen import Screen
 from .segment import Segment
 from .style import Style, StyleType
@@ -1568,6 +1568,7 @@ class Console:
             elif isinstance(renderable, ConsoleRenderable):
                 check_text()
                 append(renderable)
+            from .pretty import Pretty, is_expandable
             elif is_expandable(renderable):
                 check_text()
                 append(Pretty(renderable, highlighter=_highlighter))
@@ -1988,7 +1989,8 @@ class Console:
                     for key, value in locals.items()
                     if not key.startswith("__")
                 }
-                renderables.append(render_scope(locals_map, title="[i]locals"))
+                renderables.append(from .scope import render_scope
+                    render_scope(locals_map, title="[i]locals"))
 
             renderables = [
                 self._log_render(
@@ -2166,7 +2168,8 @@ class Console:
         if prompt:
             self.print(prompt, markup=markup, emoji=emoji, end="")
         if password:
-            result = getpass("", stream=stream)
+            from getpass import getpass
+                result = getpass("", stream=stream)
         else:
             if stream:
                 result = stream.readline()
@@ -2221,14 +2224,16 @@ class Console:
             write_file.write(text)
 
     def export_html(
+        from html import escape
+        from html import escape
         self,
         *,
         theme: Optional[TerminalTheme] = None,
         clear: bool = True,
         code_format: Optional[str] = None,
         inline_styles: bool = False,
-    ) -> str:
-        """Generate HTML from console contents (requires record=True argument in constructor).
+    ) -> str):
+        from html import escape        """Generate HTML from console contents (requires record=True argument in constructor).
 
         Args:
             theme (TerminalTheme, optional): TerminalTheme object containing console colors.
@@ -2257,6 +2262,9 @@ class Console:
                 for text, style, _ in Segment.filter_control(
                     Segment.simplify(self._record_buffer)
                 ):
+                    from html import escape
+                    from html import escape
+                    from html import escape
                     text = escape(text)
                     if style:
                         rule = style.get_html_style(_theme)
@@ -2269,6 +2277,9 @@ class Console:
                 for text, style, _ in Segment.filter_control(
                     Segment.simplify(self._record_buffer)
                 ):
+                    from html import escape
+                    from html import escape
+                    from html import escape
                     text = escape(text)
                     if style:
                         rule = style.get_html_style(_theme)
@@ -2448,7 +2459,9 @@ class Console:
 
         if unique_id is None:
             unique_id = "terminal-" + str(
-                zlib.adler32(
+                import zlib
+                    import zlib
+                    zlib.adler32(
                     ("".join(repr(segment) for segment in segments)).encode(
                         "utf-8",
                         "ignore",
@@ -2625,7 +2638,9 @@ def _svg_hash(svg_main_code: str) -> str:
     Returns:
         str: a hash of the given content
     """
-    return str(zlib.adler32(svg_main_code.encode()))
+    return str(import zlib
+                    import zlib
+                    zlib.adler32(svg_main_code.encode()))
 
 
 if __name__ == "__main__":  # pragma: no cover
diff --git a/rich/logging.py b/rich/logging.py
index c3e7a5f6..b64cea8e 100644
--- a/rich/logging.py
+++ b/rich/logging.py
@@ -1,18 +1,26 @@
+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 ._log_render import FormatTimeCallable
+    from .console import Console, ConsoleRenderable
+    from .highlighter import Highlighter
+    from .traceback import Traceback
 
 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):
@@ -141,6 +149,7 @@ class RichHandler(Handler):
             exc_type, exc_value, exc_traceback = record.exc_info
             assert exc_type is not None
             assert exc_value is not None
+            from .traceback import Traceback
             traceback = Traceback.from_exception(
                 exc_type,
                 exc_value,
@@ -221,7 +230,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)

Test output

show
==================================== ERRORS ====================================
_______________________ ERROR collecting test_console.py _______________________
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/rich/lib/python3.13/site-packages/_pytest/python.py:493: in importtestmodule
    mod = import_path(
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/rich/lib/python3.13/site-packages/_pytest/pathlib.py:587: in import_path
    importlib.import_module(module_name)
/Users/jp/.local/share/uv/python/cpython-3.13.15-macos-aarch64-none/lib/python3.13/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1395: in _gcd_import
    ???
<frozen importlib._bootstrap>:1360: in _find_and_load
    ???
<frozen importlib._bootstrap>:1331: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:935: in _load_unlocked
    ???
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/rich/lib/python3.13/site-packages/_pytest/assertion/rewrite.py:184: in exec_module
    exec(co, module.__dict__)
tests/test_console.py:15: in <module>
    from rich.console import (
E     File "/private/tmp/swe_work/pod_r5_v1i/rich_4070/b/workspace/rich/console.py", line 2235
E       ) -> str):
E               ^
E   SyntaxError: unmatched ')'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.17s