Merge pull request #994 from nathanrpage97/feat/ipython-traceback

Add traceback feature for IPython
This commit is contained in:
Will McGugan 2021-02-10 10:29:19 +00:00 committed by GitHub
commit f81b92842a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 9 deletions

View file

@ -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 .control import Control
from .segment import Segment
from .text import Text
from .style import StyleType
from ._loop import loop_last
from .text import Text
VerticalOverflowMethod = Literal["crop", "ellipsis", "visible"]
@ -88,4 +90,4 @@ class LiveRender:
for last, line in loop_last(lines):
yield from line
if not last:
yield _Segment.line()
yield _Segment.line()

View file

@ -72,7 +72,7 @@ def install(
def excepthook(
type_: Type[BaseException],
value: BaseException,
traceback: TracebackType,
traceback: Optional[TracebackType],
) -> None:
traceback_console.print(
Traceback.from_exception(
@ -88,9 +88,55 @@ def install(
)
)
old_excepthook = sys.excepthook
sys.excepthook = excepthook
return old_excepthook
def ipy_excepthook_closure(ip) -> None: # pragma: no cover
tb_data = {} # store information about showtraceback call
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

View file

@ -491,4 +491,4 @@ def test_no_nested_live():
with pytest.raises(errors.LiveError):
with console.status("foo"):
with console.status("bar"):
pass
pass