mirror of
https://github.com/Textualize/rich.git
synced 2025-08-27 05:34:09 +00:00
Merge pull request #994 from nathanrpage97/feat/ipython-traceback
Add traceback feature for IPython
This commit is contained in:
commit
f81b92842a
3 changed files with 57 additions and 9 deletions
|
@ -1,11 +1,13 @@
|
||||||
from typing import Literal, Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
from typing_extensions import Literal
|
||||||
|
|
||||||
|
from ._loop import loop_last
|
||||||
from .console import Console, ConsoleOptions, RenderableType, RenderResult
|
from .console import Console, ConsoleOptions, RenderableType, RenderResult
|
||||||
from .control import Control
|
from .control import Control
|
||||||
from .segment import Segment
|
from .segment import Segment
|
||||||
from .text import Text
|
|
||||||
from .style import StyleType
|
from .style import StyleType
|
||||||
from ._loop import loop_last
|
from .text import Text
|
||||||
|
|
||||||
VerticalOverflowMethod = Literal["crop", "ellipsis", "visible"]
|
VerticalOverflowMethod = Literal["crop", "ellipsis", "visible"]
|
||||||
|
|
||||||
|
@ -88,4 +90,4 @@ class LiveRender:
|
||||||
for last, line in loop_last(lines):
|
for last, line in loop_last(lines):
|
||||||
yield from line
|
yield from line
|
||||||
if not last:
|
if not last:
|
||||||
yield _Segment.line()
|
yield _Segment.line()
|
||||||
|
|
|
@ -72,7 +72,7 @@ def install(
|
||||||
def excepthook(
|
def excepthook(
|
||||||
type_: Type[BaseException],
|
type_: Type[BaseException],
|
||||||
value: BaseException,
|
value: BaseException,
|
||||||
traceback: TracebackType,
|
traceback: Optional[TracebackType],
|
||||||
) -> None:
|
) -> None:
|
||||||
traceback_console.print(
|
traceback_console.print(
|
||||||
Traceback.from_exception(
|
Traceback.from_exception(
|
||||||
|
@ -88,9 +88,55 @@ def install(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
old_excepthook = sys.excepthook
|
def ipy_excepthook_closure(ip) -> None: # pragma: no cover
|
||||||
sys.excepthook = excepthook
|
tb_data = {} # store information about showtraceback call
|
||||||
return old_excepthook
|
default_showtraceback = ip.showtraceback # keep reference of default traceback
|
||||||
|
|
||||||
|
def ipy_show_traceback(*args, **kwargs) -> None:
|
||||||
|
"""wrap the default ip.showtraceback to store info for ip._showtraceback"""
|
||||||
|
nonlocal tb_data
|
||||||
|
tb_data = kwargs
|
||||||
|
default_showtraceback(*args, **kwargs)
|
||||||
|
|
||||||
|
def ipy_display_traceback(*args, is_syntax: bool = False, **kwargs) -> None:
|
||||||
|
"""Internally called traceback from ip._showtraceback"""
|
||||||
|
nonlocal tb_data
|
||||||
|
exc_tuple = ip._get_exc_info()
|
||||||
|
|
||||||
|
# do not display trace on syntax error
|
||||||
|
tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]
|
||||||
|
|
||||||
|
# determine correct tb_offset
|
||||||
|
compiled = tb_data.get("running_compiled_code", False)
|
||||||
|
tb_offset = tb_data.get("tb_offset", 1 if compiled else 0)
|
||||||
|
# remove ipython internal frames from trace with tb_offset
|
||||||
|
for _ in range(tb_offset):
|
||||||
|
if tb is None:
|
||||||
|
break
|
||||||
|
tb = tb.tb_next
|
||||||
|
|
||||||
|
excepthook(exc_tuple[0], exc_tuple[1], tb)
|
||||||
|
tb_data = {} # clear data upon usage
|
||||||
|
|
||||||
|
# replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work
|
||||||
|
# this is also what the ipython docs recommends to modify when subclassing InteractiveShell
|
||||||
|
ip._showtraceback = ipy_display_traceback
|
||||||
|
# add wrapper to capture tb_data
|
||||||
|
ip.showtraceback = ipy_show_traceback
|
||||||
|
ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(
|
||||||
|
*args, is_syntax=True, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
try: # pragma: no cover
|
||||||
|
# if wihin ipython, use customized traceback
|
||||||
|
ip = get_ipython() # type: ignore
|
||||||
|
ipy_excepthook_closure(ip)
|
||||||
|
return sys.excepthook
|
||||||
|
except Exception:
|
||||||
|
# otherwise use default system hook
|
||||||
|
old_excepthook = sys.excepthook
|
||||||
|
sys.excepthook = excepthook
|
||||||
|
return old_excepthook
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
@ -491,4 +491,4 @@ def test_no_nested_live():
|
||||||
with pytest.raises(errors.LiveError):
|
with pytest.raises(errors.LiveError):
|
||||||
with console.status("foo"):
|
with console.status("foo"):
|
||||||
with console.status("bar"):
|
with console.status("bar"):
|
||||||
pass
|
pass
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue