diff --git a/execnb/shell.py b/execnb/shell.py index 752e8a5..c20bc63 100644 --- a/execnb/shell.py +++ b/execnb/shell.py @@ -16,6 +16,7 @@ from IPython.core.interactiveshell import InteractiveShell, ExecutionInfo, ExecutionResult from IPython.core.displayhook import DisplayHook from IPython.utils.capture import capture_output +from IPython.utils.text import strip_ansi from IPython.core.completer import IPCompleter,provisionalcompleter from IPython.core.hooks import CommandChainDispatcher from IPython.core.completerlib import module_completer @@ -123,12 +124,12 @@ def run(self:CaptureShell, self.exc = res.exception return _out_nb(res, self.display_formatter) -# %% ../nbs/02_shell.ipynb 29 +# %% ../nbs/02_shell.ipynb 30 def render_outputs(outputs): def render_output(out): otype = out['output_type'] if otype == 'stream': - txt = ''.join(out['text']) + txt = strip_ansi(''.join(out['text'])) return f"
{txt}" if out['name']=='stdout' else f"
{txt}" elif otype in ('display_data','execute_result'): data = out['data'] @@ -141,13 +142,11 @@ def render_output(out): if d := _g('image/png'): return f'' if d := _g('text/latex'): return f'
{escape(d)}" - elif otype == 'error': - return f"
{out['ename']}: {out['evalue']}\n{''.join(out['traceback'])}" return '' return '\n'.join(map(render_output, outputs)) -# %% ../nbs/02_shell.ipynb 40 +# %% ../nbs/02_shell.ipynb 41 @patch def cell(self:CaptureShell, cell, stdout=True, stderr=True): "Run `cell`, skipping if not code, and store outputs back in cell" @@ -159,32 +158,32 @@ def cell(self:CaptureShell, cell, stdout=True, stderr=True): for o in outs: if 'execution_count' in o: cell['execution_count'] = o['execution_count'] -# %% ../nbs/02_shell.ipynb 43 +# %% ../nbs/02_shell.ipynb 44 def find_output(outp, # Output from `run` ot='execute_result' # Output_type to find ): "Find first output of type `ot` in `CaptureShell.run` output" return first(o for o in outp if o['output_type']==ot) -# %% ../nbs/02_shell.ipynb 46 +# %% ../nbs/02_shell.ipynb 47 def out_exec(outp): "Get data from execution result in `outp`." out = find_output(outp) if out: return '\n'.join(first(out['data'].values())) -# %% ../nbs/02_shell.ipynb 48 +# %% ../nbs/02_shell.ipynb 49 def out_stream(outp): "Get text from stream in `outp`." out = find_output(outp, 'stream') if out: return ('\n'.join(out['text'])).strip() -# %% ../nbs/02_shell.ipynb 50 +# %% ../nbs/02_shell.ipynb 51 def out_error(outp): "Get traceback from error in `outp`." out = find_output(outp, 'error') if out: return '\n'.join(out['traceback']) -# %% ../nbs/02_shell.ipynb 52 +# %% ../nbs/02_shell.ipynb 53 def _false(o): return False @patch @@ -204,7 +203,7 @@ def run_all(self:CaptureShell, postproc(cell) if self.exc and exc_stop: raise self.exc from None -# %% ../nbs/02_shell.ipynb 66 +# %% ../nbs/02_shell.ipynb 67 @patch def execute(self:CaptureShell, src:str|Path, # Notebook path to read from @@ -225,7 +224,7 @@ def execute(self:CaptureShell, inject_code=inject_code, inject_idx=inject_idx) if dest: write_nb(nb, dest) -# %% ../nbs/02_shell.ipynb 70 +# %% ../nbs/02_shell.ipynb 71 @patch def prettytb(self:CaptureShell, fname:str|Path=None): # filename to print alongside the traceback @@ -237,7 +236,7 @@ def prettytb(self:CaptureShell, fname_str = f' in {fname}' if fname else '' return f"{type(self.exc).__name__}{fname_str}:\n{_fence}\n{cell_str}\n" -# %% ../nbs/02_shell.ipynb 89 +# %% ../nbs/02_shell.ipynb 90 @call_parse def exec_nb( src:str, # Notebook path to read from @@ -251,7 +250,7 @@ def exec_nb( CaptureShell().execute(src, dest, exc_stop=exc_stop, inject_code=inject_code, inject_path=inject_path, inject_idx=inject_idx) -# %% ../nbs/02_shell.ipynb 92 +# %% ../nbs/02_shell.ipynb 93 class SmartCompleter(IPCompleter): def __init__(self, shell, namespace=None, jedi=False): if namespace is None: namespace = shell.user_ns @@ -271,7 +270,7 @@ def __call__(self, c): for o in self.completions(c, len(c)) if o.type=='
{txt}\" if out['name']=='stdout' else f\"
{txt}\"\n", " elif otype in ('display_data','execute_result'):\n", " data = out['data']\n", @@ -747,8 +780,6 @@ " if d := _g('image/png'): return f''\n", " if d := _g('text/latex'): return f'
{escape(d)}\"\n", - " elif otype == 'error':\n", - " return f\"
{out['ename']}: {out['evalue']}\\n{''.join(out['traceback'])}\"\n", " return ''\n", " \n", " return '\\n'.join(map(render_output, outputs))" @@ -762,10 +793,13 @@ { "data": { "text/html": [ - "
CPU times: user 1e+03 ns, sys: 1e+03 ns, total: 2 us\n", - "Wall time: 3.1 us\n", - "\n", - "
2" + "
---------------------------------------------------------------------------\n", + "ZeroDivisionError Traceback (most recent call last)\n", + "File\n" ], "text/plain": [ ":1\n", + "----> 1 1/0\n", + "\n", + "ZeroDivisionError: division by zero\n", + "