from fastcore.test import *
from base64 import b64decode
from io import BytesIO
from PIL import Image
shell
ExecutionResult.__repr__
ExecutionResult.__repr__ ()
Return repr(self).
ExecutionInfo.__repr__
ExecutionInfo.__repr__ ()
Return repr(self).
CaptureShell
CaptureShell (path:str|pathlib.Path=None, mpl_format='retina', history=False, timeout=None)
An enhanced, interactive shell for Python.
= CaptureShell(mpl_format='retina') s
'a=1'); s.run_cell(
= s.run_cell('print(a)')
o o
{ 'display_objects': [],
'exception': None,
'quiet': False,
'result': result: None; err: None; info: <cell: print(a); id: None>,
'stderr': '',
'stdout': '1\n'}
= s.run_cell('from warnings import warn; warn("1")')
o o
{ 'display_objects': [],
'exception': None,
'quiet': False,
'result': result: None; err: None; info: <cell: from warnings import warn; warn("1"); id: None>,
'stderr': '<ipython-input-1-a51443ae013a>:1: UserWarning: 1\n'
' from warnings import warn; warn("1")\n',
'stdout': ''}
= s.run_cell('1')
o o
{ 'display_objects': [],
'exception': None,
'quiet': False,
'result': result: 1; err: None; info: <cell: 1; id: None>,
'stderr': '',
'stdout': ''}
= s.run_cell('from IPython.display import Markdown,display; print(0); display(Markdown("*2*")); Markdown("*1*")')
o o
{ 'display_objects': [<IPython.utils.capture.RichOutput object>],
'exception': None,
'quiet': False,
'result': result: <IPython.core.display.Markdown object>; err: None; info: <cell: from IPython.display import Markdown,display; print(0); display(Markdown("*2*")); Markdown("*1*"); id: None>,
'stderr': '',
'stdout': '0\n'}
o.result.result
1
0] o.display_objects[
2
= s.run_cell('1;')
o o
{ 'display_objects': [],
'exception': None,
'quiet': True,
'result': result: 1; err: None; info: <cell: 1;; id: None>,
'stderr': '',
'stdout': ''}
= s.run_cell('import matplotlib.pyplot as plt; plt.plot([1,2,3])')
o o
{ 'display_objects': [<IPython.utils.capture.RichOutput object>],
'exception': None,
'quiet': False,
'result': result: [<matplotlib.lines.Line2D object>]; err: None; info: <cell: import matplotlib.pyplot as plt; plt.plot([1,2,3]); id: None>,
'stderr': '',
'stdout': ''}
0] o.result.result[
0] o.display_objects[
= s.run_cell('''
o import pandas as pd
pd.DataFrame({'A': [1, 2], 'B': [3, 4]})''')
o
{ 'display_objects': [],
'exception': None,
'quiet': False,
'result': result: A B
0 1 3
1 2 4; err: None; info: <cell:
import pandas as pd
pd.DataFrame({'A': [1, 2], 'B': [3, 4]}); id: None>,
'stderr': '',
'stdout': ''}
o.result.result
A | B | |
---|---|---|
0 | 1 | 3 |
1 | 2 | 4 |
= s.run_cell('1/0')
o o
{ 'display_objects': [],
'exception': ZeroDivisionError('division by zero'),
'quiet': False,
'result': result: None; err: division by zero; info: <cell: 1/0; id: None>,
'stderr': '',
'stdout': '\x1b[0;31m---------------------------------------------------------------------------\x1b[0m\n'
'\x1b[0;31mZeroDivisionError\x1b[0m '
'Traceback (most recent call last)\n'
'File \x1b[0;32m<ipython-input-1-9e1622b385b6>:1\x1b[0m\n'
'\x1b[0;32m----> 1\x1b[0m '
'\x1b[38;5;241m1\x1b[39m\x1b[38;5;241m/\x1b[39m\x1b[38;5;241m0\x1b[39m\n'
'\n'
'\x1b[0;31mZeroDivisionError\x1b[0m: division by zero\n'}
= s.run_cell('import time; time.sleep(2)', timeout=1)
o 'exception'] o[
TimeoutError()
Cells
format_exc
format_exc (e)
Format exception e
as a string
CaptureShell.run
CaptureShell.run (code:str, stdout=True, stderr=True)
Run code
, returning a list of all outputs in Jupyter notebook format
Type | Default | Details | |
---|---|---|---|
code | str | Python/IPython code to run | |
stdout | bool | True | Capture stdout and save as output? |
stderr | bool | True | Capture stderr and save as output? |
= CaptureShell() s
"print(1)") s.run(
[{'name': 'stdout', 'output_type': 'stream', 'text': ['1\n']}]
Code can include magics and !
shell commands:
= s.run("%time 1+1")
o o
[{'name': 'stdout',
'output_type': 'stream',
'text': ['CPU times: user 3 us, sys: 1e+03 ns, total: 4 us\n',
'Wall time: 5.96 us\n']},
{'data': {'text/plain': ['2']},
'metadata': {},
'output_type': 'execute_result'}]
= s.run("1/0")
o o
[{'name': 'stdout',
'output_type': 'stream',
'text': ['\x1b[0;31m---------------------------------------------------------------------------\x1b[0m\n',
'\x1b[0;31mZeroDivisionError\x1b[0m Traceback (most recent call last)\n',
'File \x1b[0;32m<ipython-input-1-9e1622b385b6>:1\x1b[0m\n',
'\x1b[0;32m----> 1\x1b[0m \x1b[38;5;241m1\x1b[39m\x1b[38;5;241m/\x1b[39m\x1b[38;5;241m0\x1b[39m\n',
'\n',
'\x1b[0;31mZeroDivisionError\x1b[0m: division by zero\n']},
{'ename': 'ZeroDivisionError',
'evalue': 'division by zero',
'output_type': 'error',
'traceback': 'Traceback (most recent call last):\n File "/home/jhoward/miniconda3/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code\n exec(code_obj, self.user_global_ns, self.user_ns)\n File "<ipython-input-1-9e1622b385b6>", line 1, in <module>\n 1/0\n ~^~\nZeroDivisionError: division by zero\n'}]
render_outputs
render_outputs (outputs, ansi_renderer=<function strip_ansi>)
HTML(render_outputs(o))
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) File:1 ----> 1 1/0 ZeroDivisionError: division by zero
We can use ansi2html
to convert from ANSI to HTML for color rendering. You need some css styles for the colors to render properly. Jupyter already has these built in so it’s not neccessary here, but if you plan on using this in another web app you will need to ensure that css styling is included.
HTML(render_outputs(o, ansi2html))
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) File <ipython-input-1-9e1622b385b6>:1 ----> 1 1/0 ZeroDivisionError: division by zero
The result of the last successful execution is stored in result
:
s.result
A trailing ;
stops the result from being captured:
"1+2;") s.run(
[]
Images and matplotlib figures are captured:
= s.run('''import matplotlib.pyplot as plt
res plt.figure(figsize=(2,1))
plt.plot([1,2,4]);''')
HTML(render_outputs(res))
If an exception is raised then the exception type, object, and stacktrace are stored in exc
:
= s.run('raise Exception("Oops")')
o o
[{'name': 'stdout',
'output_type': 'stream',
'text': ['\x1b[0;31m---------------------------------------------------------------------------\x1b[0m\n',
'\x1b[0;31mException\x1b[0m Traceback (most recent call last)\n',
'File \x1b[0;32m<ipython-input-1-01648acb07bd>:1\x1b[0m\n',
'\x1b[0;32m----> 1\x1b[0m \x1b[38;5;28;01mraise\x1b[39;00m \x1b[38;5;167;01mException\x1b[39;00m(\x1b[38;5;124m"\x1b[39m\x1b[38;5;124mOops\x1b[39m\x1b[38;5;124m"\x1b[39m)\n',
'\n',
'\x1b[0;31mException\x1b[0m: Oops\n']},
{'ename': 'Exception',
'evalue': 'Oops',
'output_type': 'error',
'traceback': 'Traceback (most recent call last):\n File "/home/jhoward/miniconda3/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code\n exec(code_obj, self.user_global_ns, self.user_ns)\n File "<ipython-input-1-01648acb07bd>", line 1, in <module>\n raise Exception("Oops")\nException: Oops\n'}]
s.exc
Exception('Oops')
CaptureShell.cell
CaptureShell.cell (cell, stdout=True, stderr=True)
Run cell
, skipping if not code, and store outputs back in cell
= Path('../tests/clean.ipynb')
clean = read_nb(clean)
nb = nb.cells[1]
c c
{ 'cell_type': 'code',
'execution_count': None,
'id': 'b123d6d0',
'idx_': 1,
'metadata': {},
'outputs': [],
'source': 'print(1)\n2'}
s.cell(c) c.outputs
[{'name': 'stdout', 'output_type': 'stream', 'text': ['1\n']},
{'data': {'text/plain': ['2']},
'metadata': {},
'output_type': 'execute_result'}]
find_output
find_output (outp, ot='execute_result')
Find first output of type ot
in CaptureShell.run
output
Type | Default | Details | |
---|---|---|---|
outp | Output from run |
||
ot | str | execute_result | Output_type to find |
'data'] find_output(c.outputs)[
{'text/plain': ['2']}
'stream')['text'] find_output(c.outputs,
['1\n']
out_exec
out_exec (outp)
Get data from execution result in outp
.
out_exec(c.outputs)
'2'
out_stream
out_stream (outp)
Get text from stream in outp
.
out_stream(c.outputs)
'1'
out_error
out_error (outp)
Get traceback from error in outp
.
CaptureShell.run_all
CaptureShell.run_all (nb, exc_stop:bool=False, preproc:<built- infunctioncallable>=<function _false>, postproc:<built-infunctioncallable>=<function _false>, inject_code:str|None=None, inject_idx:int=0)
Run all cells in nb
, stopping at first exception if exc_stop
Type | Default | Details | |
---|---|---|---|
nb | A notebook read with nbclient or read_nb |
||
exc_stop | bool | False | Stop on exceptions? |
preproc | callable | _false | Called before each cell is executed |
postproc | callable | _false | Called after each cell is executed |
inject_code | str | None | None | Code to inject into a cell |
inject_idx | int | 0 | Cell to replace with inject_code |
2].outputs nb.cells[
[]
s.run_all(nb)2].outputs nb.cells[
[{'data': {'text/plain': ['<IPython.core.display.Markdown object>'],
'text/markdown': ["This is *bold*. Here's a [link](https://www.fast.ai)."]},
'metadata': {},
'output_type': 'execute_result'}]
With exc_stop=False
(the default), execution continues after exceptions, and exception details are stored into the appropriate cell’s output:
-1].source nb.cells[
'raise Exception("Oopsie!")'
-1].outputs nb.cells[
[{'name': 'stdout',
'output_type': 'stream',
'text': ['\x1b[0;31m---------------------------------------------------------------------------\x1b[0m\n',
'\x1b[0;31mException\x1b[0m Traceback (most recent call last)\n',
'File \x1b[0;32m<ipython-input-1-1c97c1d317ab>:1\x1b[0m\n',
'\x1b[0;32m----> 1\x1b[0m \x1b[38;5;28;01mraise\x1b[39;00m \x1b[38;5;167;01mException\x1b[39;00m(\x1b[38;5;124m"\x1b[39m\x1b[38;5;124mOopsie!\x1b[39m\x1b[38;5;124m"\x1b[39m)\n',
'\n',
'\x1b[0;31mException\x1b[0m: Oopsie!\n']},
{'ename': 'Exception',
'evalue': 'Oopsie!',
'output_type': 'error',
'traceback': 'Traceback (most recent call last):\n File "/home/jhoward/miniconda3/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code\n exec(code_obj, self.user_global_ns, self.user_ns)\n File "<ipython-input-1-1c97c1d317ab>", line 1, in <module>\n raise Exception("Oopsie!")\nException: Oopsie!\n'}]
With exc_stop=True
, exceptions in a cell are raised and no further processing occurs:
try: s.run_all(nb, exc_stop=True)
except Exception as e: print(f"got exception: {e}")
got exception: Oopsie!
We can pass a function to preproc
to have it run on every cell. It can modify the cell as needed. If the function returns True
, then that cell will not be executed. For instance, to skip the cell which raises an exception:
= read_nb(clean)
nb =lambda c: 'raise' in c.source) s.run_all(nb, preproc
This cell will contain no output, since it was skipped.
-1].outputs nb.cells[
[]
1].outputs nb.cells[
[{'name': 'stdout', 'output_type': 'stream', 'text': ['1\n']},
{'data': {'text/plain': ['2']},
'metadata': {},
'output_type': 'execute_result'}]
You can also pass a function to postproc
to modify a cell after it is executed.
CaptureShell.execute
CaptureShell.execute (src:str|pathlib.Path, dest:str|None=None, exc_stop:bool=False, preproc:<built- infunctioncallable>=<function _false>, postproc:<built-infunctioncallable>=<function _false>, inject_code:str|None=None, inject_path:str|pathlib.Path|None=None, inject_idx:int=0)
Execute notebook from src
and save with outputs to `dest
Type | Default | Details | |
---|---|---|---|
src | str | pathlib.Path | Notebook path to read from | |
dest | str | None | None | Notebook path to write to |
exc_stop | bool | False | Stop on exceptions? |
preproc | callable | _false | Called before each cell is executed |
postproc | callable | _false | Called after each cell is executed |
inject_code | str | None | None | Code to inject into a cell |
inject_path | str | pathlib.Path | None | None | Path to file containing code to inject into a cell |
inject_idx | int | 0 | Cell to replace with inject_code |
This is a shortcut for the combination of read_nb
, CaptureShell.run_all
, and write_nb
.
= CaptureShell()
s try:
'tmp.ipynb')
s.execute(clean, print(read_nb('tmp.ipynb').cells[1].outputs)
finally: Path('tmp.ipynb').unlink()
[{'name': 'stdout', 'output_type': 'stream', 'text': ['1\n']}, {'data': {'text/plain': ['2']}, 'metadata': {}, 'output_type': 'execute_result'}]
= Path.home()/'git'/'fastcore'/'nbs'
p = p/'03a_parallel.ipynb' n
CaptureShell.prettytb
CaptureShell.prettytb (fname:str|pathlib.Path=None)
Show a pretty traceback for notebooks, optionally printing fname
.
Type | Default | Details | |
---|---|---|---|
fname | str | pathlib.Path | None | filename to print alongside the traceback |
If an error occurs while running a notebook, you can retrieve a pretty version of the error with the prettytb
method:
= CaptureShell()
s try:
'../tests/error.ipynb', exc_stop=True)
s.execute(except:
print(s.prettytb())
AssertionError in ../tests/error.ipynb:
===========================================================================
While Executing Cell #2:
Traceback (most recent call last):
File "/tmp/ipykernel_17198/1421292703.py", line 3, in <module>
s.execute('../tests/error.ipynb', exc_stop=True)
File "/tmp/ipykernel_17198/3609882568.py", line 18, in execute
self.run_all(nb, exc_stop=exc_stop, preproc=preproc, postproc=postproc,
File "/tmp/ipykernel_17198/3068237356.py", line 19, in run_all
if self.exc and exc_stop: raise self.exc from None
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jhoward/miniconda3/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-1-b968a57a586e>", line 3, in <module>
foo()
File "/home/jhoward/git/execnb/tests/err.py", line 2, in foo
assert 13 == 98
^^^^^^^^
AssertionError
If you pass inject_code
to CaptureShell.execute
or CaptureShell.run_all
, the source of nb.cells[inject_idx]
will be replaced with inject_code
. By default, the first cell is replaced. For instance consider this notebook:
= read_nb('../tests/params.ipynb')
nb for c in nb.cells: print('- ',c.source)
- a=1
- print(a)
We can replace the first cell with a=2
by passing that as inject_code
, and the notebook will run with that change:
= read_nb('../tests/params.ipynb')
nb ="a=2")
s.run_all(nb, inject_codelist(nb.cells)
[{'cell_type': 'code',
'execution_count': None,
'id': 'a63ce885',
'metadata': {},
'outputs': [],
'source': 'a=2',
'idx_': 0},
{'cell_type': 'code',
'execution_count': None,
'id': 'ea528db5',
'metadata': {},
'outputs': [{'name': 'stdout', 'output_type': 'stream', 'text': ['2\n']}],
'source': 'print(a)',
'idx_': 1}]
This can be used with CaptureShell.execute
to parameterise runs of models in notebooks. Place any defaults for configuration code needed in the first cell, and then when running execute
pass in new parameters as needed in inject_code
. To replace only some of the defaults, leave an empty cell as the second cell, and inject code using inject_idx=1
to replace the empty second cell with code that overrides some of the defaults set in the first cell. When using execute
you can pass inject_path
instead of inject_code
to read the injected code from a file.
exec_nb
exec_nb (src:str, dest:str='', exc_stop:bool=False, inject_code:str=None, inject_path:str=None, inject_idx:int=0)
Execute notebook from src
and save with outputs to dest
Type | Default | Details | |
---|---|---|---|
src | str | Notebook path to read from | |
dest | str | Notebook path to write to | |
exc_stop | bool | False | Stop on exceptions? |
inject_code | str | None | Code to inject into a cell |
inject_path | str | None | Path to file containing code to inject into a cell |
inject_idx | int | 0 | Cell to replace with inject_code |
This is the command-line version of CaptureShell.execute
. Run exec_nb -h
from the command line to see how to pass arguments. If you don’t pass dest
then the output notebook won’t be saved; this is mainly useful for running tests.
SmartCompleter
SmartCompleter (shell, namespace=None, jedi=False)
Extension of the completer class with IPython-specific features
= SmartCompleter(get_ipython())
cc
def test_set(a,b): return test_eq(set(a), set(b))
class _f:
def __init__(self): self.bar,self.baz,self.room = 0,0,0
= _f()
foo
assert set(cc("b")).issuperset(['bool', 'bytes'])
"foo.b"), ['bar', 'baz'])
test_set(cc("x=1; x = foo.b"), ['bar', 'baz'])
test_set(cc("ab"), ['abs'])
test_set(cc("b = ab"), ['abs'])
test_set(cc(""), [])
test_set(cc("foo."), ['bar', 'baz', 'room'])
test_set(cc("nonexistent.b"), [])
test_set(cc("foo.nonexistent.b"), [])
test_set(cc(assert set(cc("import ab")).issuperset(['abc'])
"from abc import AB"), ['ABC', 'ABCMeta']) test_set(cc(
CaptureShell.complete
CaptureShell.complete (c)
Return the completed text and a list of completions.
Type | Details | |
---|---|---|
c | ||
Returns | string | The actual text that was completed. |
= CaptureShell()
s 'a=1')
s.run('a.b') s.complete(
['bit_count', 'bit_length']