← eval8_submission_v1a

rich_4070

failed WRONG_FIX UNSUBMITTED agent_error · 18 tool calls · 197 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 (18)

#ToolArgumentsResult
1run_command
{
 "command": "ls -R rich | grep -E \"logging.py|console.py|segment.py|theme.py|syntax.py|protocol.py|repr.py\""
}
{"status": "ok", "stdout": "_win32_console.py\nconsole.py\nlogging.py\nprotocol.py\nrepr.py\nsegment.py\nsyntax.py\nterminal_theme.py\ntheme.py\n", "stderr": ""
full
{"status": "ok", "stdout": "_win32_console.py\nconsole.py\nlogging.py\nprotocol.py\nrepr.py\nsegment.py\nsyntax.py\nterminal_theme.py\ntheme.py\n", "stderr": "", "exit_code": 0}
2run_command
{
 "command": "grep -n \"import\" rich/logging.py rich/console.py rich/segment.py rich/theme.py rich/syntax.py rich/protocol.py rich/repr.py"
}
{"status": "ok", "stdout": "rich/logging.py:1:import logging\nrich/logging.py:2:from datetime import datetime\nrich/logging.py:3:from logging import Handler, Lo
full
{"status": "ok", "stdout": "rich/logging.py:1:import logging\nrich/logging.py:2:from datetime import datetime\nrich/logging.py:3:from logging import Handler, LogRecord\nrich/logging.py:4:from pathlib import Path\nrich/logging.py:5:from types import ModuleType\nrich/logging.py:6:from typing import ClassVar, Iterable, List, Optional, Type, Union\nrich/logging.py:8:from rich._null_file import NullFile\nrich/logging.py:10:from . import get_console\nrich/logging.py:11:from ._log_render import FormatTimeCallable, LogRender\nrich/logging.py:12:from .console import Console, ConsoleRenderable\nrich/logging.py:13:from .highlighter import Highlighter, ReprHighlighter\nrich/logging.py:14:from .text import Text\nrich/logging.py:15:from .traceback import Traceback\nrich/logging.py:243:    from time import sleep\nrich/console.py:1:import inspect\nrich/console.py:2:import os\nrich/console.py:3:import sys\nrich/console.py:4:import threading\nrich/console.py:5:import zlib\nrich/console.py:6:from abc import ABC, abstractmethod\nrich/console.py:7:from dataclasses import dataclass, field\nrich/console.py:8:from datetime import datetime\nrich/console.py:9:from functools import wraps\nrich/console.py:10:from getpass import getpass\nrich/console.py:11:from html import escape\nrich/console.py:12:from inspect import isclass\nrich/console.py:13:from itertools import islice\nrich/console.py:14:from math import ceil\nrich/console.py:15:from time import monotonic\nrich/console.py:16:from types import FrameType, ModuleType, TracebackType\nrich/console.py:17:from typing import (\nrich/console.py:38:from rich._null_file import NULL_FILE\nrich/console.py:40:from . import errors, themes\nrich/console.py:41:from ._emoji_replace import _emoji_replace\nrich/console.py:42:from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT\nrich/console.py:43:from ._fileno import get_fileno\nrich/console.py:44:from ._log_render import FormatTimeCallable, LogRender\nrich/console.py:45:from .align import Al
3run_command
{
 "command": "sed -n '1,30p' rich/logging.py"
}
{"status": "ok", "stdout": "import logging\nfrom datetime import datetime\nfrom logging import Handler, LogRecord\nfrom pathlib import Path\nfrom types import M
full
{"status": "ok", "stdout": "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", "stderr": "", "exit_code": 0}
4run_command
{
 "command": "grep -n \"emit\" rich/logging.py"
}
{"status": "ok", "stdout": "132: def emit(self, record: LogRecord) -> None:\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "132:    def emit(self, record: LogRecord) -> None:\n", "stderr": "", "exit_code": 0}
5run_command
{
 "command": "sed -n '120,150p' rich/logging.py"
}
{"status": "ok", "stdout": " Args:\n record (LogRecord): LogRecord instance.\n\n Returns:\n Text: A tuple of the style and level name.\n \"\"\"\n level_name = r
full
{"status": "ok", "stdout": "        Args:\n            record (LogRecord): LogRecord instance.\n\n        Returns:\n            Text: A tuple of the style and level name.\n        \"\"\"\n        level_name = record.levelname\n        level_text = Text.styled(\n            level_name.ljust(8), f\"logging.level.{level_name.lower()}\"\n        )\n        return level_text\n\n    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,\n", "stderr": "", "exit_code": 0}
6edit_file
{
 "filepath": "rich/logging.py",
 "new_string": "from __future__ import annotations\n\nimport logging\nfrom datetime import datetime\nfrom logging import Handler, LogRecord\nimport os\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\n",
 "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\n"
}
{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -1,16 +1,22 @@\
full
{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -1,16 +1,22 @@\n+from __future__ import annotations\n+\n import logging\n from datetime import datetime\n from logging import Handler, LogRecord\n-from pathlib import Path\n+import os\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", "is_truncated": false}
7run_command
{
 "command": "grep -n \"Path(\" rich/logging.py"
}
{"status": "ok", "stdout": "230: path = Path(record.pathname).name\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "230:        path = Path(record.pathname).name\n", "stderr": "", "exit_code": 0}
8edit_file
{
 "filepath": "rich/logging.py",
 "new_string": "        path = os.path.basename(record.pathname)",
 "old_string": "        path = Path(record.pathname).name"
}
{"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}
9edit_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,\n",
 "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,\n"
}
{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -144,6 +144,7 @
full
{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -144,6 +144,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}
10edit_file
{
 "filepath": "rich/logging.py",
 "new_string": "from .text import Text\n",
 "old_string": "from .text import Text\nfrom .traceback import Traceback\n"
}
{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -18,7 +18,6 @@\
full
{"status": "ok", "filepath": "rich/logging.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/logging.py\n+++ b/rich/logging.py\n@@ -18,7 +18,6 @@\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}
11run_command
{
 "command": "sed -n '1,30p' rich/console.py"
}
{"status": "ok", "stdout": "import inspect\nimport os\nimport sys\nimport threading\nimport zlib\nfrom abc import ABC, abstractmethod\nfrom dataclasses import d
full
{"status": "ok", "stdout": "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", "stderr": "", "exit_code": 0}
12run_command
{
 "command": "grep -n \"isclass\" rich/console.py"
}
{"status": "ok", "stdout": "12:from inspect import isclass\n1324: if hasattr(renderable, \"__rich_console__\") and not isclass(renderable):\n", "stderr": "", "e
full
{"status": "ok", "stdout": "12:from inspect import isclass\n1324:        if hasattr(renderable, \"__rich_console__\") and not isclass(renderable):\n", "stderr": "", "exit_code": 0}
13run_command
{
 "command": "grep -n \"currentframe\" rich/console.py"
}
{"status": "ok", "stdout": "1904: currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n1910: currentframe (Callable[[], Optional[FrameType]]
full
{"status": "ok", "stdout": "1904:        currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,\n1910:            currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to\n1911:                retrieve the current frame. Defaults to ``inspect.currentframe``.\n1923:        frame = currentframe()\n1925:            # Use the faster currentframe where implemented\n", "stderr": "", "exit_code": 0}
14edit_file
{
 "filepath": "rich/console.py",
 "new_string": "import 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 itertools import islice\nfrom math import ceil\nfrom time import monotonic\nfrom types import FrameType, ModuleType, TracebackType\nfrom typing import (\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"
}
{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1,4 +1,3 @@\n-
full
{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1,4 +1,3 @@\n-import inspect\n import os\n import sys\n import threading\n@@ -9,7 +8,6 @@\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}
15edit_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@@ -1319,7 +1319,7
full
{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1319,7 +1319,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}
16edit_file
{
 "filepath": "rich/console.py",
 "new_string": "        currentframe: Callable[[], Optional[FrameType]] = sys._getframe,\n",
 "old_string": "        currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe,"
}
{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1899,7 +1899,8
full
{"status": "ok", "filepath": "rich/console.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/rich/console.py\n+++ b/rich/console.py\n@@ -1899,7 +1899,8 @@\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+\n     ) -> Tuple[str, int, Dict[str, Any]]:\n         \"\"\"Get caller frame information.\n \n", "is_truncated": false}
17run_command
{
 "command": "grep -n \"from .pretty import Pretty\" rich/console.py"
}
{"status": "ok", "stdout": "51:from .pretty import Pretty, is_expandable\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "51:from .pretty import Pretty, is_expandable\n", "stderr": "", "exit_code": 0}
18run_command
{
 "command": "grep -n \"def print(\" rich/console.py"
}
{"status": "ok", "stdout": "1646: def print(\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "1646:    def print(\n", "stderr": "", "exit_code": 0}

Patch

diff --git a/rich/console.py b/rich/console.py
index ad92d529..7681136f 100644
--- a/rich/console.py
+++ b/rich/console.py
@@ -1,4 +1,3 @@
-import inspect
 import os
 import sys
 import threading
@@ -9,7 +8,6 @@ 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 +1319,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 +1899,8 @@ 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.
 
diff --git a/rich/logging.py b/rich/logging.py
index c3e7a5f6..9a13b8b7 100644
--- a/rich/logging.py
+++ b/rich/logging.py
@@ -1,18 +1,23 @@
+from __future__ import annotations
+
 import logging
 from datetime import datetime
 from logging import Handler, LogRecord
-from pathlib import Path
+import os
 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)

Test output

show
..........................................F
=================================== FAILURES ===================================
_____________________________ test_input_password ______________________________

prompt = '', stream = None

    def unix_getpass(prompt='Password: ', stream=None):
        """Prompt for a password, with echo turned off.
    
        Args:
          prompt: Written on stream to ask for the input.  Default: 'Password: '
          stream: A writable file object to display the prompt.  Defaults to
                  the tty.  If no tty is available defaults to sys.stderr.
        Returns:
          The seKr3t input.
        Raises:
          EOFError: If our input tty or stdin was closed.
          GetPassWarning: When we were unable to turn echo off on the input.
    
        Always restores terminal settings before returning.
        """
        passwd = None
        with contextlib.ExitStack() as stack:
            try:
                # Always try reading and writing directly on the tty first.
>               fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)
E               OSError: [Errno 6] No such device or address: '/dev/tty'

/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/getpass.py:47: OSError

During handling of the above exception, another exception occurred:

prompt = '', stream = None

    def unix_getpass(prompt='Password: ', stream=None):
        """Prompt for a password, with echo turned off.
    
        Args:
          prompt: Written on stream to ask for the input.  Default: 'Password: '
          stream: A writable file object to display the prompt.  Defaults to
                  the tty.  If no tty is available defaults to sys.stderr.
        Returns:
          The seKr3t input.
        Raises:
          EOFError: If our input tty or stdin was closed.
          GetPassWarning: When we were unable to turn echo off on the input.
    
        Always restores terminal settings before returning.
        """
        passwd = None
        with contextlib.ExitStack() as stack:
            try:
                # Always try reading and writing directly on the tty first.
                fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY)
                tty = io.FileIO(fd, 'w+')
                stack.enter_context(tty)
                input = io.TextIOWrapper(tty)
                stack.enter_context(input)
                if not stream:
                    stream = input
            except OSError:
                # If that fails, see if stdin can be controlled.
                stack.close()
                try:
>                   fd = sys.stdin.fileno()
E                   io.UnsupportedOperation: redirected stdin is pseudofile, has no fileno()

/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/getpass.py:58: UnsupportedOperation

During handling of the above exception, another exception occurred:

monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7c2fc3376740>
capsys = <_pytest.capture.CaptureFixture object at 0x7c2fc3558e10>

    def test_input_password(monkeypatch, capsys) -> None:
        def fake_input(prompt, stream=None):
            console.file.write(prompt)
            return "bar"
    
        import getpass
    
        monkeypatch.setattr(getpass, "getpass", fake_input)
        console = Console()
>       user_input = console.input(prompt="foo:", password=True)

tests/test_console.py:407: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
rich/console.py:2168: in input
    result = getpass("", stream=stream)
/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/getpass.py:61: in unix_getpass
    passwd = fallback_getpass(prompt, stream)
/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/getpass.py:126: in fallback_getpass
    return _raw_input(prompt, stream)
/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/getpass.py:146: in _raw_input
    line = input.readline()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <_pytest.capture.DontReadFromInput object at 0x7c2fc35b5fd0>, size = -1

    def read(self, size: int = -1) -> str:
>       raise OSError(
            "pytest: reading from stdin while output is captured!  Consider using `-s`."
        )
E       OSError: pytest: reading from stdin while output is captured!  Consider using `-s`.

/kaggle/tmp/envs/rich/lib/python3.13/site-packages/_pytest/capture.py:209: OSError
----------------------------- Captured stdout call -----------------------------
foo:
----------------------------- Captured stderr call -----------------------------
Warning: Password input may be echoed.
=============================== warnings summary ===============================
test_console.py::test_input_password
  /root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/getpass.py:61: GetPassWarning: Can not control echo on the terminal.
    passwd = fallback_getpass(prompt, stream)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 42 passed, 1 warning in 0.47s