Skip to content

Commit 006658f

Browse files
committed
fixes #60
1 parent bf76fb4 commit 006658f

File tree

4 files changed

+74
-36
lines changed

4 files changed

+74
-36
lines changed

execnb/nbio.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
# %% auto 0
66
__all__ = ['NbCell', 'dict2nb', 'read_nb', 'new_nb', 'mk_cell', 'nb2dict', 'nb2str', 'write_nb']
77

8-
# %% ../nbs/01_nbio.ipynb 2
8+
# %% ../nbs/01_nbio.ipynb
99
from fastcore.basics import *
1010
from fastcore.imports import *
1111

1212
import ast,functools
1313
from pprint import pformat,pprint
1414
from json import loads,dumps
1515

16-
# %% ../nbs/01_nbio.ipynb 6
16+
# %% ../nbs/01_nbio.ipynb
1717
def _read_json(self, encoding=None, errors=None):
1818
return loads(Path(self).read_text(encoding=encoding, errors=errors))
1919

20-
# %% ../nbs/01_nbio.ipynb 13
20+
# %% ../nbs/01_nbio.ipynb
2121
class NbCell(AttrDict):
2222
def __init__(self, idx, cell):
2323
super().__init__(cell)
@@ -40,7 +40,7 @@ def parsed_(self):
4040
def __hash__(self): return hash(self.source) + hash(self.cell_type)
4141
def __eq__(self,o): return self.source==o.source and self.cell_type==o.cell_type
4242

43-
# %% ../nbs/01_nbio.ipynb 15
43+
# %% ../nbs/01_nbio.ipynb
4444
def _dict2obj(d, list_func=list, dict_func=AttrDict):
4545
"Convert (possibly nested) dicts (or lists of dicts) to `AttrDict`"
4646
if isinstance(d, list): return list(map(_dict2obj, d))
@@ -53,19 +53,19 @@ def dict2nb(js=None, **kwargs):
5353
nb.cells = [NbCell(*o) for o in enumerate(nb.cells)]
5454
return nb
5555

56-
# %% ../nbs/01_nbio.ipynb 20
56+
# %% ../nbs/01_nbio.ipynb
5757
def read_nb(path):
5858
"Return notebook at `path`"
5959
res = dict2nb(_read_json(path, encoding='utf-8'))
6060
res['path_'] = str(path)
6161
return res
6262

63-
# %% ../nbs/01_nbio.ipynb 26
63+
# %% ../nbs/01_nbio.ipynb
6464
def new_nb(cells=None, meta=None, nbformat=4, nbformat_minor=5):
6565
"Returns an empty new notebook"
6666
return dict2nb(cells=cells or [],metadata=meta or {},nbformat=nbformat,nbformat_minor=nbformat_minor)
6767

68-
# %% ../nbs/01_nbio.ipynb 28
68+
# %% ../nbs/01_nbio.ipynb
6969
def mk_cell(text, # `source` attr in cell
7070
cell_type='code', # `cell_type` attr in cell
7171
**kwargs): # any other attrs to add to cell
@@ -77,21 +77,21 @@ def mk_cell(text, # `source` attr in cell
7777
kwargs['execution_count']=0
7878
return NbCell(0, dict(cell_type=cell_type, source=text, directives_={}, **kwargs))
7979

80-
# %% ../nbs/01_nbio.ipynb 31
80+
# %% ../nbs/01_nbio.ipynb
8181
def nb2dict(d, k=None):
8282
"Convert parsed notebook to `dict`"
8383
if k=='source': return d.splitlines(keepends=True)
8484
if isinstance(d, list): return list(map(nb2dict,d))
8585
if not isinstance(d, dict): return d
8686
return dict(**{k:nb2dict(v,k) for k,v in d.items() if k[-1] != '_'})
8787

88-
# %% ../nbs/01_nbio.ipynb 34
88+
# %% ../nbs/01_nbio.ipynb
8989
def nb2str(nb):
9090
"Convert `nb` to a `str`"
9191
if isinstance(nb, (AttrDict,list)): nb = nb2dict(nb)
9292
return dumps(nb, sort_keys=True, indent=1, ensure_ascii=False) + "\n"
9393

94-
# %% ../nbs/01_nbio.ipynb 37
94+
# %% ../nbs/01_nbio.ipynb
9595
def write_nb(nb, path):
9696
"Write `nb` to `path`"
9797
new = nb2str(nb)

execnb/shell.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from fastcore.script import call_parse
1010
from fastcore.ansi import ansi2html
1111

12-
import mistletoe,multiprocessing,types,traceback
12+
import mistletoe,multiprocessing,types,traceback,signal
1313
try:
1414
if sys.platform == 'darwin': multiprocessing.set_start_method("fork")
1515
except RuntimeError: pass # if re-running cell
@@ -37,7 +37,7 @@
3737
__all__ = ['CaptureShell', 'format_exc', 'render_outputs', 'find_output', 'out_exec', 'out_stream', 'out_error', 'exec_nb',
3838
'SmartCompleter']
3939

40-
# %% ../nbs/02_shell.ipynb 5
40+
# %% ../nbs/02_shell.ipynb
4141
class _CustDisplayHook(DisplayHook):
4242
def write_output_prompt(self): pass
4343
def write_format_data(self, data, md_dict): pass
@@ -49,12 +49,14 @@ def __repr__(self: ExecutionInfo): return f'cell: {self.raw_cell}; id: {self.cel
4949
@patch
5050
def __repr__(self: ExecutionResult): return f'result: {self.result}; err: {self.error_in_exec}; info: <{self.info}>'
5151

52-
# %% ../nbs/02_shell.ipynb 6
52+
# %% ../nbs/02_shell.ipynb
5353
class CaptureShell(InteractiveShell):
5454
displayhook_class = _CustDisplayHook
5555

56-
def __init__(self, path:str|Path=None, mpl_format='retina'):
56+
def __init__(self, path:str|Path=None, mpl_format='retina', history=False, timeout=None):
5757
super().__init__()
58+
self.history_manager.enabled = history
59+
self.timeout = timeout
5860
self.result,self.exc = None,None
5961
if path: self.set_path(path)
6062
self.display_formatter.active = True
@@ -65,11 +67,17 @@ def __init__(self, path:str|Path=None, mpl_format='retina'):
6567
self.run_cell(f"set_matplotlib_formats('{mpl_format}')")
6668

6769
def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True, cell_id=None,
68-
stdout=True, stderr=True, display=True):
70+
stdout=True, stderr=True, display=True, timeout=None):
71+
if not timeout: timeout = self.timeout
6972
# TODO what if there's a comment?
7073
semic = raw_cell.rstrip().endswith(';')
74+
if timeout:
75+
def handler(*args): raise TimeoutError()
76+
signal.signal(signal.SIGALRM, handler)
77+
signal.alarm(timeout)
7178
with capture_output(display=display, stdout=stdout, stderr=stderr) as c:
7279
result = super().run_cell(raw_cell, store_history, silent, shell_futures=shell_futures, cell_id=cell_id)
80+
if timeout: signal.alarm(0)
7381
return AttrDict(result=result, stdout='' if semic else c.stdout, stderr=c.stderr,
7482
display_objects=c.outputs, exception=result.error_in_exec, quiet=semic)
7583

@@ -81,12 +89,12 @@ def set_path(self, path):
8189

8290
def enable_gui(self, gui=None): pass
8391

84-
# %% ../nbs/02_shell.ipynb 23
92+
# %% ../nbs/02_shell.ipynb
8593
def format_exc(e):
8694
"Format exception `e` as a string"
8795
return ''.join(traceback.format_exception(type(e), e, e.__traceback__))
8896

89-
# %% ../nbs/02_shell.ipynb 24
97+
# %% ../nbs/02_shell.ipynb
9098
def _out_stream(text, name): return dict(name=name, output_type='stream', text=text.splitlines(True))
9199
def _out_exc(e):
92100
ename = type(e).__name__
@@ -116,7 +124,7 @@ def _out_nb(o, fmt):
116124
res.append(_mk_out(*fmt.format(r), 'execute_result'))
117125
return res
118126

119-
# %% ../nbs/02_shell.ipynb 25
127+
# %% ../nbs/02_shell.ipynb
120128
@patch
121129
def run(self:CaptureShell,
122130
code:str, # Python/IPython code to run
@@ -128,7 +136,7 @@ def run(self:CaptureShell,
128136
self.exc = res.exception
129137
return _out_nb(res, self.display_formatter)
130138

131-
# %% ../nbs/02_shell.ipynb 31
139+
# %% ../nbs/02_shell.ipynb
132140
def render_outputs(outputs, ansi_renderer=strip_ansi):
133141
def render_output(out):
134142
otype = out['output_type']
@@ -150,7 +158,7 @@ def render_output(out):
150158

151159
return '\n'.join(map(render_output, outputs))
152160

153-
# %% ../nbs/02_shell.ipynb 44
161+
# %% ../nbs/02_shell.ipynb
154162
@patch
155163
def cell(self:CaptureShell, cell, stdout=True, stderr=True):
156164
"Run `cell`, skipping if not code, and store outputs back in cell"
@@ -162,32 +170,32 @@ def cell(self:CaptureShell, cell, stdout=True, stderr=True):
162170
for o in outs:
163171
if 'execution_count' in o: cell['execution_count'] = o['execution_count']
164172

165-
# %% ../nbs/02_shell.ipynb 47
173+
# %% ../nbs/02_shell.ipynb
166174
def find_output(outp, # Output from `run`
167175
ot='execute_result' # Output_type to find
168176
):
169177
"Find first output of type `ot` in `CaptureShell.run` output"
170178
return first(o for o in outp if o['output_type']==ot)
171179

172-
# %% ../nbs/02_shell.ipynb 50
180+
# %% ../nbs/02_shell.ipynb
173181
def out_exec(outp):
174182
"Get data from execution result in `outp`."
175183
out = find_output(outp)
176184
if out: return '\n'.join(first(out['data'].values()))
177185

178-
# %% ../nbs/02_shell.ipynb 52
186+
# %% ../nbs/02_shell.ipynb
179187
def out_stream(outp):
180188
"Get text from stream in `outp`."
181189
out = find_output(outp, 'stream')
182190
if out: return ('\n'.join(out['text'])).strip()
183191

184-
# %% ../nbs/02_shell.ipynb 54
192+
# %% ../nbs/02_shell.ipynb
185193
def out_error(outp):
186194
"Get traceback from error in `outp`."
187195
out = find_output(outp, 'error')
188196
if out: return '\n'.join(out['traceback'])
189197

190-
# %% ../nbs/02_shell.ipynb 56
198+
# %% ../nbs/02_shell.ipynb
191199
def _false(o): return False
192200

193201
@patch
@@ -207,7 +215,7 @@ def run_all(self:CaptureShell,
207215
postproc(cell)
208216
if self.exc and exc_stop: raise self.exc from None
209217

210-
# %% ../nbs/02_shell.ipynb 70
218+
# %% ../nbs/02_shell.ipynb
211219
@patch
212220
def execute(self:CaptureShell,
213221
src:str|Path, # Notebook path to read from
@@ -228,7 +236,7 @@ def execute(self:CaptureShell,
228236
inject_code=inject_code, inject_idx=inject_idx)
229237
if dest: write_nb(nb, dest)
230238

231-
# %% ../nbs/02_shell.ipynb 74
239+
# %% ../nbs/02_shell.ipynb
232240
@patch
233241
def prettytb(self:CaptureShell,
234242
fname:str|Path=None): # filename to print alongside the traceback
@@ -240,7 +248,7 @@ def prettytb(self:CaptureShell,
240248
fname_str = f' in {fname}' if fname else ''
241249
return f"{type(self.exc).__name__}{fname_str}:\n{_fence}\n{cell_str}\n"
242250

243-
# %% ../nbs/02_shell.ipynb 93
251+
# %% ../nbs/02_shell.ipynb
244252
@call_parse
245253
def exec_nb(
246254
src:str, # Notebook path to read from
@@ -254,7 +262,7 @@ def exec_nb(
254262
CaptureShell().execute(src, dest, exc_stop=exc_stop, inject_code=inject_code,
255263
inject_path=inject_path, inject_idx=inject_idx)
256264

257-
# %% ../nbs/02_shell.ipynb 96
265+
# %% ../nbs/02_shell.ipynb
258266
class SmartCompleter(IPCompleter):
259267
def __init__(self, shell, namespace=None, jedi=False):
260268
if namespace is None: namespace = shell.user_ns
@@ -274,7 +282,7 @@ def __call__(self, c):
274282
for o in self.completions(c, len(c))
275283
if o.type not in ('magic', 'path')]
276284

277-
# %% ../nbs/02_shell.ipynb 98
285+
# %% ../nbs/02_shell.ipynb
278286
@patch
279287
def complete(self:CaptureShell, c):
280288
if not hasattr(self, '_completer'): self._completer = SmartCompleter(self)

nbs/02_shell.ipynb

Lines changed: 35 additions & 6 deletions
Large diffs are not rendered by default.

settings.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ jupyter_hooks = True
3939
clean_ids = True
4040
clear_all = False
4141
put_version_in_init = True
42+
cell_number = False
4243

0 commit comments

Comments
 (0)