execnb 0.1.5__tar.gz → 0.1.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: execnb
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: A description of your project
5
- Home-page: https://github.com/fastai/execnb/tree/master/
5
+ Home-page: https://github.com/fastai/execnb/
6
6
  Author: Jeremy Howard
7
7
  Author-email: j@fast.ai
8
8
  License: Apache Software License 2.0
@@ -15,11 +15,16 @@ Classifier: Programming Language :: Python :: 3.8
15
15
  Classifier: License :: OSI Approved :: Apache Software License
16
16
  Requires-Python: >=3.7
17
17
  Description-Content-Type: text/markdown
18
- Provides-Extra: dev
19
18
  License-File: LICENSE
19
+ Requires-Dist: fastcore>=1.5.5
20
+ Requires-Dist: ipython
21
+ Requires-Dist: mistletoe
22
+ Provides-Extra: dev
23
+ Requires-Dist: matplotlib; extra == "dev"
24
+ Requires-Dist: Pillow; extra == "dev"
25
+
26
+ # execnb
20
27
 
21
- execnb
22
- ================
23
28
 
24
29
  <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
25
30
 
@@ -1,5 +1,5 @@
1
- execnb
2
- ================
1
+ # execnb
2
+
3
3
 
4
4
  <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
5
5
 
@@ -0,0 +1 @@
1
+ __version__ = "0.1.7"
@@ -3,10 +3,9 @@
3
3
  d = { 'settings': { 'branch': 'master',
4
4
  'doc_baseurl': '/execnb/',
5
5
  'doc_host': 'https://fastai.github.io',
6
- 'git_url': 'https://github.com/fastai/execnb/tree/master/',
6
+ 'git_url': 'https://github.com/fastai/execnb/',
7
7
  'lib_path': 'execnb'},
8
- 'syms': { 'execnb.fastshell': {},
9
- 'execnb.nbio': { 'execnb.nbio.NbCell': ('nbio.html#nbcell', 'execnb/nbio.py'),
8
+ 'syms': { 'execnb.nbio': { 'execnb.nbio.NbCell': ('nbio.html#nbcell', 'execnb/nbio.py'),
10
9
  'execnb.nbio.NbCell.__eq__': ('nbio.html#nbcell.__eq__', 'execnb/nbio.py'),
11
10
  'execnb.nbio.NbCell.__hash__': ('nbio.html#nbcell.__hash__', 'execnb/nbio.py'),
12
11
  'execnb.nbio.NbCell.__init__': ('nbio.html#nbcell.__init__', 'execnb/nbio.py'),
@@ -23,27 +22,36 @@ d = { 'settings': { 'branch': 'master',
23
22
  'execnb.nbio.write_nb': ('nbio.html#write_nb', 'execnb/nbio.py')},
24
23
  'execnb.shell': { 'execnb.shell.CaptureShell': ('shell.html#captureshell', 'execnb/shell.py'),
25
24
  'execnb.shell.CaptureShell.__init__': ('shell.html#captureshell.__init__', 'execnb/shell.py'),
26
- 'execnb.shell.CaptureShell._add_exec': ('shell.html#captureshell._add_exec', 'execnb/shell.py'),
27
- 'execnb.shell.CaptureShell._add_out': ('shell.html#captureshell._add_out', 'execnb/shell.py'),
28
- 'execnb.shell.CaptureShell._result': ('shell.html#captureshell._result', 'execnb/shell.py'),
29
- 'execnb.shell.CaptureShell._showtraceback': ('shell.html#captureshell._showtraceback', 'execnb/shell.py'),
30
- 'execnb.shell.CaptureShell._stream': ('shell.html#captureshell._stream', 'execnb/shell.py'),
31
25
  'execnb.shell.CaptureShell.cell': ('shell.html#captureshell.cell', 'execnb/shell.py'),
26
+ 'execnb.shell.CaptureShell.complete': ('shell.html#captureshell.complete', 'execnb/shell.py'),
32
27
  'execnb.shell.CaptureShell.enable_gui': ('shell.html#captureshell.enable_gui', 'execnb/shell.py'),
33
- 'execnb.shell.CaptureShell.enable_matplotlib': ( 'shell.html#captureshell.enable_matplotlib',
34
- 'execnb/shell.py'),
35
28
  'execnb.shell.CaptureShell.execute': ('shell.html#captureshell.execute', 'execnb/shell.py'),
36
29
  'execnb.shell.CaptureShell.prettytb': ('shell.html#captureshell.prettytb', 'execnb/shell.py'),
37
30
  'execnb.shell.CaptureShell.run': ('shell.html#captureshell.run', 'execnb/shell.py'),
38
31
  'execnb.shell.CaptureShell.run_all': ('shell.html#captureshell.run_all', 'execnb/shell.py'),
32
+ 'execnb.shell.CaptureShell.run_cell': ('shell.html#captureshell.run_cell', 'execnb/shell.py'),
39
33
  'execnb.shell.CaptureShell.set_path': ('shell.html#captureshell.set_path', 'execnb/shell.py'),
40
- 'execnb.shell._CaptureHook': ('shell.html#_capturehook', 'execnb/shell.py'),
41
- 'execnb.shell._CaptureHook.__call__': ('shell.html#_capturehook.__call__', 'execnb/shell.py'),
42
- 'execnb.shell._CaptureHook.quiet': ('shell.html#_capturehook.quiet', 'execnb/shell.py'),
43
- 'execnb.shell._CapturePub': ('shell.html#_capturepub', 'execnb/shell.py'),
44
- 'execnb.shell._CapturePub.publish': ('shell.html#_capturepub.publish', 'execnb/shell.py'),
34
+ 'execnb.shell.ExecutionInfo.__repr__': ('shell.html#executioninfo.__repr__', 'execnb/shell.py'),
35
+ 'execnb.shell.ExecutionResult.__repr__': ('shell.html#executionresult.__repr__', 'execnb/shell.py'),
36
+ 'execnb.shell.SmartCompleter': ('shell.html#smartcompleter', 'execnb/shell.py'),
37
+ 'execnb.shell.SmartCompleter.__call__': ('shell.html#smartcompleter.__call__', 'execnb/shell.py'),
38
+ 'execnb.shell.SmartCompleter.__init__': ('shell.html#smartcompleter.__init__', 'execnb/shell.py'),
39
+ 'execnb.shell._CustDisplayHook': ('shell.html#_custdisplayhook', 'execnb/shell.py'),
40
+ 'execnb.shell._CustDisplayHook.log_output': ('shell.html#_custdisplayhook.log_output', 'execnb/shell.py'),
41
+ 'execnb.shell._CustDisplayHook.write_format_data': ( 'shell.html#_custdisplayhook.write_format_data',
42
+ 'execnb/shell.py'),
43
+ 'execnb.shell._CustDisplayHook.write_output_prompt': ( 'shell.html#_custdisplayhook.write_output_prompt',
44
+ 'execnb/shell.py'),
45
45
  'execnb.shell._false': ('shell.html#_false', 'execnb/shell.py'),
46
46
  'execnb.shell._format_mimedata': ('shell.html#_format_mimedata', 'execnb/shell.py'),
47
+ 'execnb.shell._mk_out': ('shell.html#_mk_out', 'execnb/shell.py'),
47
48
  'execnb.shell._out_exc': ('shell.html#_out_exc', 'execnb/shell.py'),
49
+ 'execnb.shell._out_nb': ('shell.html#_out_nb', 'execnb/shell.py'),
48
50
  'execnb.shell._out_stream': ('shell.html#_out_stream', 'execnb/shell.py'),
49
- 'execnb.shell.exec_nb': ('shell.html#exec_nb', 'execnb/shell.py')}}}
51
+ 'execnb.shell.exec_nb': ('shell.html#exec_nb', 'execnb/shell.py'),
52
+ 'execnb.shell.find_output': ('shell.html#find_output', 'execnb/shell.py'),
53
+ 'execnb.shell.format_exc': ('shell.html#format_exc', 'execnb/shell.py'),
54
+ 'execnb.shell.out_error': ('shell.html#out_error', 'execnb/shell.py'),
55
+ 'execnb.shell.out_exec': ('shell.html#out_exec', 'execnb/shell.py'),
56
+ 'execnb.shell.out_stream': ('shell.html#out_stream', 'execnb/shell.py'),
57
+ 'execnb.shell.render_outputs': ('shell.html#render_outputs', 'execnb/shell.py')}}}
@@ -1,9 +1,11 @@
1
+ """Reading and writing Jupyter notebooks"""
2
+
1
3
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_nbio.ipynb.
2
4
 
3
5
  # %% auto 0
4
6
  __all__ = ['NbCell', 'dict2nb', 'read_nb', 'new_nb', 'mk_cell', 'nb2dict', 'nb2str', 'write_nb']
5
7
 
6
- # %% ../nbs/01_nbio.ipynb 2
8
+ # %% ../nbs/01_nbio.ipynb
7
9
  from fastcore.basics import *
8
10
  from fastcore.imports import *
9
11
 
@@ -11,11 +13,11 @@ import ast,functools
11
13
  from pprint import pformat,pprint
12
14
  from json import loads,dumps
13
15
 
14
- # %% ../nbs/01_nbio.ipynb 6
16
+ # %% ../nbs/01_nbio.ipynb
15
17
  def _read_json(self, encoding=None, errors=None):
16
18
  return loads(Path(self).read_text(encoding=encoding, errors=errors))
17
19
 
18
- # %% ../nbs/01_nbio.ipynb 13
20
+ # %% ../nbs/01_nbio.ipynb
19
21
  class NbCell(AttrDict):
20
22
  def __init__(self, idx, cell):
21
23
  super().__init__(cell)
@@ -28,7 +30,7 @@ class NbCell(AttrDict):
28
30
 
29
31
  def parsed_(self):
30
32
  if self.cell_type!='code' or self.source.strip()[:1] in ['%', '!']: return
31
- if '_parsed_' not in self:
33
+ if '_parsed_' not in self:
32
34
  try: self._parsed_ = ast.parse(self.source).body
33
35
  # you can assign the result of ! to a variable in a notebook cell
34
36
  # which will result in a syntax error if parsed with the ast module.
@@ -38,7 +40,7 @@ class NbCell(AttrDict):
38
40
  def __hash__(self): return hash(self.source) + hash(self.cell_type)
39
41
  def __eq__(self,o): return self.source==o.source and self.cell_type==o.cell_type
40
42
 
41
- # %% ../nbs/01_nbio.ipynb 15
43
+ # %% ../nbs/01_nbio.ipynb
42
44
  def _dict2obj(d, list_func=list, dict_func=AttrDict):
43
45
  "Convert (possibly nested) dicts (or lists of dicts) to `AttrDict`"
44
46
  if isinstance(d, list): return list(map(_dict2obj, d))
@@ -51,28 +53,31 @@ def dict2nb(js=None, **kwargs):
51
53
  nb.cells = [NbCell(*o) for o in enumerate(nb.cells)]
52
54
  return nb
53
55
 
54
- # %% ../nbs/01_nbio.ipynb 20
56
+ # %% ../nbs/01_nbio.ipynb
55
57
  def read_nb(path):
56
58
  "Return notebook at `path`"
57
59
  res = dict2nb(_read_json(path, encoding='utf-8'))
58
60
  res['path_'] = str(path)
59
61
  return res
60
62
 
61
- # %% ../nbs/01_nbio.ipynb 26
63
+ # %% ../nbs/01_nbio.ipynb
62
64
  def new_nb(cells=None, meta=None, nbformat=4, nbformat_minor=5):
63
65
  "Returns an empty new notebook"
64
66
  return dict2nb(cells=cells or [],metadata=meta or {},nbformat=nbformat,nbformat_minor=nbformat_minor)
65
67
 
66
- # %% ../nbs/01_nbio.ipynb 28
68
+ # %% ../nbs/01_nbio.ipynb
67
69
  def mk_cell(text, # `source` attr in cell
68
70
  cell_type='code', # `cell_type` attr in cell
69
71
  **kwargs): # any other attrs to add to cell
70
72
  "Create an `NbCell` containing `text`"
71
73
  assert cell_type in {'code', 'markdown', 'raw'}
72
74
  if 'metadata' not in kwargs: kwargs['metadata']={}
75
+ if cell_type == 'code':
76
+ kwargs['outputs']=[]
77
+ kwargs['execution_count']=0
73
78
  return NbCell(0, dict(cell_type=cell_type, source=text, directives_={}, **kwargs))
74
79
 
75
- # %% ../nbs/01_nbio.ipynb 31
80
+ # %% ../nbs/01_nbio.ipynb
76
81
  def nb2dict(d, k=None):
77
82
  "Convert parsed notebook to `dict`"
78
83
  if k=='source': return d.splitlines(keepends=True)
@@ -80,13 +85,13 @@ def nb2dict(d, k=None):
80
85
  if not isinstance(d, dict): return d
81
86
  return dict(**{k:nb2dict(v,k) for k,v in d.items() if k[-1] != '_'})
82
87
 
83
- # %% ../nbs/01_nbio.ipynb 34
88
+ # %% ../nbs/01_nbio.ipynb
84
89
  def nb2str(nb):
85
90
  "Convert `nb` to a `str`"
86
91
  if isinstance(nb, (AttrDict,list)): nb = nb2dict(nb)
87
92
  return dumps(nb, sort_keys=True, indent=1, ensure_ascii=False) + "\n"
88
93
 
89
- # %% ../nbs/01_nbio.ipynb 37
94
+ # %% ../nbs/01_nbio.ipynb
90
95
  def write_nb(nb, path):
91
96
  "Write `nb` to `path`"
92
97
  new = nb2str(nb)
@@ -0,0 +1,289 @@
1
+ """A shell for running notebook code without a notebook server"""
2
+
3
+ # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_shell.ipynb.
4
+
5
+ # %% ../nbs/02_shell.ipynb 2
6
+ from __future__ import annotations
7
+
8
+ from fastcore.utils import *
9
+ from fastcore.script import call_parse
10
+ from fastcore.ansi import ansi2html
11
+
12
+ import mistletoe,multiprocessing,types,traceback,signal
13
+ try:
14
+ if sys.platform == 'darwin': multiprocessing.set_start_method("fork")
15
+ except RuntimeError: pass # if re-running cell
16
+
17
+ from IPython.core.interactiveshell import InteractiveShell, ExecutionInfo, ExecutionResult
18
+ from IPython.core.displayhook import DisplayHook
19
+ from IPython.utils.capture import capture_output
20
+ from IPython.utils.text import strip_ansi
21
+ from IPython.core.completer import IPCompleter,provisionalcompleter
22
+ from IPython.core.hooks import CommandChainDispatcher
23
+ from IPython.core.completerlib import module_completer
24
+ from IPython.utils.strdispatch import StrDispatch
25
+ from IPython.display import display as disp, HTML
26
+
27
+ from base64 import b64encode
28
+ from html import escape
29
+ try: from matplotlib_inline.backend_inline import set_matplotlib_formats
30
+ except ImportError: set_matplotlib_formats = None
31
+
32
+
33
+ from .nbio import *
34
+ from .nbio import _dict2obj
35
+
36
+ # %% auto 0
37
+ __all__ = ['CaptureShell', 'format_exc', 'render_outputs', 'find_output', 'out_exec', 'out_stream', 'out_error', 'exec_nb',
38
+ 'SmartCompleter']
39
+
40
+ # %% ../nbs/02_shell.ipynb
41
+ class _CustDisplayHook(DisplayHook):
42
+ def write_output_prompt(self): pass
43
+ def write_format_data(self, data, md_dict): pass
44
+ def log_output(self, format_dict): pass
45
+
46
+ @patch
47
+ def __repr__(self: ExecutionInfo): return f'cell: {self.raw_cell}; id: {self.cell_id}'
48
+
49
+ @patch
50
+ def __repr__(self: ExecutionResult): return f'result: {self.result}; err: {self.error_in_exec}; info: <{self.info}>'
51
+
52
+ # %% ../nbs/02_shell.ipynb
53
+ class CaptureShell(InteractiveShell):
54
+ displayhook_class = _CustDisplayHook
55
+
56
+ def __init__(self, path:str|Path=None, mpl_format='retina', history=False, timeout=None):
57
+ super().__init__()
58
+ self.history_manager.enabled = history
59
+ self.timeout = timeout
60
+ self.result,self.exc = None,None
61
+ if path: self.set_path(path)
62
+ self.display_formatter.active = True
63
+ if not IN_NOTEBOOK: InteractiveShell._instance = self
64
+ if set_matplotlib_formats:
65
+ self.enable_matplotlib("inline")
66
+ self.run_cell("from matplotlib_inline.backend_inline import set_matplotlib_formats")
67
+ self.run_cell(f"set_matplotlib_formats('{mpl_format}')")
68
+
69
+ def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True, cell_id=None,
70
+ stdout=True, stderr=True, display=True, timeout=None):
71
+ if not timeout: timeout = self.timeout
72
+ # TODO what if there's a comment?
73
+ semic = raw_cell.rstrip().endswith(';')
74
+ if timeout:
75
+ def handler(*args): raise TimeoutError()
76
+ signal.signal(signal.SIGALRM, handler)
77
+ signal.alarm(timeout)
78
+ with capture_output(display=display, stdout=stdout, stderr=stderr) as c:
79
+ result = super().run_cell(raw_cell, store_history, silent, shell_futures=shell_futures, cell_id=cell_id)
80
+ if timeout: signal.alarm(0)
81
+ return AttrDict(result=result, stdout='' if semic else c.stdout, stderr=c.stderr,
82
+ display_objects=c.outputs, exception=result.error_in_exec, quiet=semic)
83
+
84
+ def set_path(self, path):
85
+ "Add `path` to python path, or `path.parent` if it's a file"
86
+ path = Path(path)
87
+ if path.is_file(): path = path.parent
88
+ self.run_cell(f"import sys; sys.path.insert(0, '{path.as_posix()}')")
89
+
90
+ def enable_gui(self, gui=None): pass
91
+
92
+ # %% ../nbs/02_shell.ipynb
93
+ def format_exc(e):
94
+ "Format exception `e` as a string"
95
+ return ''.join(traceback.format_exception(type(e), e, e.__traceback__))
96
+
97
+ # %% ../nbs/02_shell.ipynb
98
+ def _out_stream(text, name): return dict(name=name, output_type='stream', text=text.splitlines(True))
99
+ def _out_exc(e):
100
+ ename = type(e).__name__
101
+ tb = traceback.extract_tb(e.__traceback__)#.format()
102
+ return dict(ename=str(ename), evalue=str(e), output_type='error', traceback=format_exc(e))
103
+
104
+ def _format_mimedata(k, v):
105
+ "Format mime-type keyed data consistently with Jupyter"
106
+ if k.startswith('text/'): return v.splitlines(True)
107
+ if k.startswith('image/') and isinstance(v, bytes):
108
+ v = b64encode(v).decode()
109
+ return v+'\n' if not v.endswith('\n') else v
110
+ return v
111
+
112
+ def _mk_out(data, meta, output_type='display_data'):
113
+ fd = {k:_format_mimedata(k,v) for k,v in data.items()}
114
+ return dict(data=fd, metadata=meta, output_type=output_type)
115
+
116
+ def _out_nb(o, fmt):
117
+ res = []
118
+ if o.stdout: res.append(_out_stream(o.stdout, 'stdout'))
119
+ if o.stderr: res.append(_out_stream(o.stderr, 'stderr'))
120
+ if o.exception: res.append(_out_exc(o.exception))
121
+ r = o.result.result
122
+ for x in o.display_objects: res.append(_mk_out(x.data, x.metadata))
123
+ if r is not None and not o.quiet:
124
+ res.append(_mk_out(*fmt.format(r), 'execute_result'))
125
+ return res
126
+
127
+ # %% ../nbs/02_shell.ipynb
128
+ @patch
129
+ def run(self:CaptureShell,
130
+ code:str, # Python/IPython code to run
131
+ stdout=True, # Capture stdout and save as output?
132
+ stderr=True): # Capture stderr and save as output?
133
+ "Run `code`, returning a list of all outputs in Jupyter notebook format"
134
+ res = self.run_cell(code, stdout=stdout, stderr=stderr)
135
+ self.result = res.result.result
136
+ self.exc = res.exception
137
+ return _out_nb(res, self.display_formatter)
138
+
139
+ # %% ../nbs/02_shell.ipynb
140
+ def render_outputs(outputs, ansi_renderer=strip_ansi):
141
+ def render_output(out):
142
+ otype = out['output_type']
143
+ if otype == 'stream':
144
+ txt = ansi_renderer(''.join(out['text']))
145
+ return f"<pre>{txt}</pre>" if out['name']=='stdout' else f"<pre class='stderr'>{txt}</pre>"
146
+ elif otype in ('display_data','execute_result'):
147
+ data = out['data']
148
+ _g = lambda t: ''.join(data[t]) if t in data else None
149
+ if d := _g('text/html'): return d
150
+ if d := _g('application/javascript'): return f'<script>{d}</script>'
151
+ if d := _g('text/markdown'): return mistletoe.markdown(d)
152
+ if d := _g('image/svg+xml'): return d
153
+ if d := _g('image/jpeg'): return f'<img src="data:image/jpeg;base64,{d}"/>'
154
+ if d := _g('image/png'): return f'<img src="data:image/png;base64,{d}"/>'
155
+ if d := _g('text/latex'): return f'<div class="math">${d}$</div>'
156
+ if d := _g('text/plain'): return f"<pre>{escape(d)}</pre>"
157
+ return ''
158
+
159
+ return '\n'.join(map(render_output, outputs))
160
+
161
+ # %% ../nbs/02_shell.ipynb
162
+ @patch
163
+ def cell(self:CaptureShell, cell, stdout=True, stderr=True):
164
+ "Run `cell`, skipping if not code, and store outputs back in cell"
165
+ if cell.cell_type!='code': return
166
+ self._cell_idx = cell.idx_ + 1
167
+ outs = self.run(cell.source)
168
+ if outs:
169
+ cell.outputs = _dict2obj(outs)
170
+ for o in outs:
171
+ if 'execution_count' in o: cell['execution_count'] = o['execution_count']
172
+
173
+ # %% ../nbs/02_shell.ipynb
174
+ def find_output(outp, # Output from `run`
175
+ ot='execute_result' # Output_type to find
176
+ ):
177
+ "Find first output of type `ot` in `CaptureShell.run` output"
178
+ return first(o for o in outp if o['output_type']==ot)
179
+
180
+ # %% ../nbs/02_shell.ipynb
181
+ def out_exec(outp):
182
+ "Get data from execution result in `outp`."
183
+ out = find_output(outp)
184
+ if out: return '\n'.join(first(out['data'].values()))
185
+
186
+ # %% ../nbs/02_shell.ipynb
187
+ def out_stream(outp):
188
+ "Get text from stream in `outp`."
189
+ out = find_output(outp, 'stream')
190
+ if out: return ('\n'.join(out['text'])).strip()
191
+
192
+ # %% ../nbs/02_shell.ipynb
193
+ def out_error(outp):
194
+ "Get traceback from error in `outp`."
195
+ out = find_output(outp, 'error')
196
+ if out: return '\n'.join(out['traceback'])
197
+
198
+ # %% ../nbs/02_shell.ipynb
199
+ def _false(o): return False
200
+
201
+ @patch
202
+ def run_all(self:CaptureShell,
203
+ nb, # A notebook read with `nbclient` or `read_nb`
204
+ exc_stop:bool=False, # Stop on exceptions?
205
+ preproc:callable=_false, # Called before each cell is executed
206
+ postproc:callable=_false, # Called after each cell is executed
207
+ inject_code:str|None=None, # Code to inject into a cell
208
+ inject_idx:int=0 # Cell to replace with `inject_code`
209
+ ):
210
+ "Run all cells in `nb`, stopping at first exception if `exc_stop`"
211
+ if inject_code is not None: nb.cells[inject_idx].source = inject_code
212
+ for cell in nb.cells:
213
+ if not preproc(cell):
214
+ self.cell(cell)
215
+ postproc(cell)
216
+ if self.exc and exc_stop: raise self.exc from None
217
+
218
+ # %% ../nbs/02_shell.ipynb
219
+ @patch
220
+ def execute(self:CaptureShell,
221
+ src:str|Path, # Notebook path to read from
222
+ dest:str|None=None, # Notebook path to write to
223
+ exc_stop:bool=False, # Stop on exceptions?
224
+ preproc:callable=_false, # Called before each cell is executed
225
+ postproc:callable=_false, # Called after each cell is executed
226
+ inject_code:str|None=None, # Code to inject into a cell
227
+ inject_path:str|Path|None=None, # Path to file containing code to inject into a cell
228
+ inject_idx:int=0 # Cell to replace with `inject_code`
229
+ ):
230
+ "Execute notebook from `src` and save with outputs to `dest"
231
+ nb = read_nb(src)
232
+ self._fname = src
233
+ self.set_path(Path(src).parent.resolve())
234
+ if inject_path is not None: inject_code = Path(inject_path).read_text()
235
+ self.run_all(nb, exc_stop=exc_stop, preproc=preproc, postproc=postproc,
236
+ inject_code=inject_code, inject_idx=inject_idx)
237
+ if dest: write_nb(nb, dest)
238
+
239
+ # %% ../nbs/02_shell.ipynb
240
+ @patch
241
+ def prettytb(self:CaptureShell,
242
+ fname:str|Path=None): # filename to print alongside the traceback
243
+ "Show a pretty traceback for notebooks, optionally printing `fname`."
244
+ fname = fname if fname else self._fname
245
+ _fence = '='*75
246
+ cell_intro_str = f"While Executing Cell #{self._cell_idx}:" if self._cell_idx else "While Executing:"
247
+ cell_str = f"\n{cell_intro_str}\n{format_exc(self.exc)}"
248
+ fname_str = f' in {fname}' if fname else ''
249
+ return f"{type(self.exc).__name__}{fname_str}:\n{_fence}\n{cell_str}\n"
250
+
251
+ # %% ../nbs/02_shell.ipynb
252
+ @call_parse
253
+ def exec_nb(
254
+ src:str, # Notebook path to read from
255
+ dest:str='', # Notebook path to write to
256
+ exc_stop:bool=False, # Stop on exceptions?
257
+ inject_code:str=None, # Code to inject into a cell
258
+ inject_path:str=None, # Path to file containing code to inject into a cell
259
+ inject_idx:int=0 # Cell to replace with `inject_code`
260
+ ):
261
+ "Execute notebook from `src` and save with outputs to `dest`"
262
+ CaptureShell().execute(src, dest, exc_stop=exc_stop, inject_code=inject_code,
263
+ inject_path=inject_path, inject_idx=inject_idx)
264
+
265
+ # %% ../nbs/02_shell.ipynb
266
+ class SmartCompleter(IPCompleter):
267
+ def __init__(self, shell, namespace=None, jedi=False):
268
+ if namespace is None: namespace = shell.user_ns
269
+ super().__init__(shell, namespace)
270
+ self.use_jedi = jedi
271
+ sdisp = StrDispatch()
272
+ self.custom_completers = sdisp
273
+ import_disp = CommandChainDispatcher()
274
+ import_disp.add(types.MethodType(module_completer, shell))
275
+ sdisp.add_s('import', import_disp)
276
+ sdisp.add_s('from', import_disp)
277
+
278
+ def __call__(self, c):
279
+ if not c: return []
280
+ with provisionalcompleter():
281
+ return [o.text.rpartition('.')[-1]
282
+ for o in self.completions(c, len(c))
283
+ if o.type not in ('magic', 'path')]
284
+
285
+ # %% ../nbs/02_shell.ipynb
286
+ @patch
287
+ def complete(self:CaptureShell, c):
288
+ if not hasattr(self, '_completer'): self._completer = SmartCompleter(self)
289
+ return self._completer(c)
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: execnb
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: A description of your project
5
- Home-page: https://github.com/fastai/execnb/tree/master/
5
+ Home-page: https://github.com/fastai/execnb/
6
6
  Author: Jeremy Howard
7
7
  Author-email: j@fast.ai
8
8
  License: Apache Software License 2.0
@@ -15,11 +15,16 @@ Classifier: Programming Language :: Python :: 3.8
15
15
  Classifier: License :: OSI Approved :: Apache Software License
16
16
  Requires-Python: >=3.7
17
17
  Description-Content-Type: text/markdown
18
- Provides-Extra: dev
19
18
  License-File: LICENSE
19
+ Requires-Dist: fastcore>=1.5.5
20
+ Requires-Dist: ipython
21
+ Requires-Dist: mistletoe
22
+ Provides-Extra: dev
23
+ Requires-Dist: matplotlib; extra == "dev"
24
+ Requires-Dist: Pillow; extra == "dev"
25
+
26
+ # execnb
20
27
 
21
- execnb
22
- ================
23
28
 
24
29
  <!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
25
30
 
@@ -1,11 +1,11 @@
1
1
  LICENSE
2
2
  MANIFEST.in
3
3
  README.md
4
+ pyproject.toml
4
5
  settings.ini
5
6
  setup.py
6
7
  execnb/__init__.py
7
8
  execnb/_modidx.py
8
- execnb/fastshell.py
9
9
  execnb/nbio.py
10
10
  execnb/shell.py
11
11
  execnb.egg-info/PKG-INFO
@@ -1,5 +1,6 @@
1
1
  fastcore>=1.5.5
2
2
  ipython
3
+ mistletoe
3
4
 
4
5
  [dev]
5
6
  matplotlib
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64.0"]
3
+ build-backend = "setuptools.build_meta"
@@ -9,9 +9,9 @@ user = fastai
9
9
  author = Jeremy Howard
10
10
  author_email = j@fast.ai
11
11
  branch = master
12
- version = 0.1.5
12
+ version = 0.1.7
13
13
  min_python = 3.7
14
- requirements = fastcore>=1.5.5 ipython
14
+ requirements = fastcore>=1.5.5 ipython mistletoe
15
15
  dev_requirements = matplotlib Pillow
16
16
  console_scripts = exec_nb=execnb.shell:exec_nb
17
17
  audience = Developers
@@ -25,7 +25,7 @@ recursive = False
25
25
  tst_flags = notest
26
26
  doc_host = https://fastai.github.io
27
27
  doc_baseurl = /execnb/
28
- git_url = https://github.com/fastai/execnb/tree/master/
28
+ git_url = https://github.com/fastai/execnb/
29
29
  lib_path = execnb
30
30
  title = execnb
31
31
  black_formatting = False
@@ -39,4 +39,6 @@ jupyter_hooks = True
39
39
  clean_ids = True
40
40
  clear_all = False
41
41
  put_version_in_init = True
42
+ cell_number = False
43
+ skip_procs =
42
44
 
@@ -1 +0,0 @@
1
- __version__ = "0.1.5"
@@ -1,124 +0,0 @@
1
- # Copyright (c) IPython Development Team.
2
- # Modifications by Jeremy Howard
3
- # Distributed under the terms of the Modified BSD License.
4
-
5
- import os, sys, warnings
6
- from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
7
- from IPython.core.history import HistoryAccessorBase
8
- from IPython.core.autocall import ZMQExitAutocall
9
- from IPython.core.magic import magics_class, line_magic, Magics
10
- from IPython.utils.process import system
11
- from traitlets import Instance, Type, Any, default, observe
12
-
13
- @magics_class
14
- class KernelMagics(Magics):
15
- @line_magic
16
- def edit(self, parameter_s='', last_call=['','']): pass
17
-
18
- @line_magic
19
- def clear(self, arg_s): pass
20
-
21
- @line_magic
22
- def less(self, arg_s): pass
23
-
24
- more = line_magic('more')(less)
25
-
26
- # Man calls a pager, so we also need to redefine it
27
- if os.name == 'posix':
28
- @line_magic
29
- def man(self, arg_s): pass
30
-
31
- @line_magic
32
- def autosave(self, arg_s): pass
33
-
34
- def noop(*args, **kwargs): return args
35
-
36
- class DummyHistory(HistoryAccessorBase):
37
- def __init__(self, *args, **kw): self.enabled=False
38
- def __getattr__(self, k): return noop
39
- _log_validate = False
40
-
41
- class FastInteractiveShell(InteractiveShell):
42
- data_pub_class = Any()
43
- parent_header = Any()
44
- exiter = Instance(ZMQExitAutocall)
45
-
46
- def init_history(self): self.history_manager=DummyHistory()
47
- def atexit_operations(self, *args, **kwargs): pass
48
-
49
- @default('exiter')
50
- def _default_exiter(self): return ZMQExitAutocall(self)
51
-
52
- @observe('exit_now')
53
- def _update_exit_now(self, change): pass
54
-
55
- keepkernel_on_exit = None
56
-
57
- def init_environment(self):
58
- "Configure the user's environment."
59
- env = os.environ
60
- env['TERM'] = 'xterm-color'
61
- env['CLICOLOR'] = '1'
62
- env['PAGER'] = 'cat'
63
- env['GIT_PAGER'] = 'cat'
64
-
65
- def init_data_pub(self): pass
66
-
67
- @property
68
- def data_pub(self):
69
- if not hasattr(self, '_data_pub'):
70
- warnings.warn("InteractiveShell.data_pub is deprecated outside IPython parallel.", DeprecationWarning, stacklevel=2)
71
- self._data_pub = self.data_pub_class(parent=self)
72
- self._data_pub.session = self.display_pub.session
73
- self._data_pub.pub_socket = self.display_pub.pub_socket
74
- return self._data_pub
75
-
76
- @data_pub.setter
77
- def data_pub(self, pub): self._data_pub = pub
78
-
79
- def ask_exit(self):
80
- "Engage the exit actions."
81
- self.exit_now = (not self.keepkernel_on_exit)
82
- payload = dict( source='ask_exit', keepkernel=self.keepkernel_on_exit,)
83
- self.payload_manager.write_payload(payload)
84
-
85
- def set_next_input(self, text, replace=False):
86
- "Send the specified text to the frontend to be presented at the next input cell."
87
- payload = dict( source='set_next_input', text=text, replace=replace,)
88
- self.payload_manager.write_payload(payload)
89
-
90
- def set_parent(self, parent):
91
- "Set the parent header for associating output with its triggering input"
92
- self.parent_header = parent
93
- self.displayhook.set_parent(parent)
94
- self.display_pub.set_parent(parent)
95
- if hasattr(self, '_data_pub'): self.data_pub.set_parent(parent)
96
- try: sys.stdout.set_parent(parent)
97
- except AttributeError: pass
98
- try: sys.stderr.set_parent(parent)
99
- except AttributeError: pass
100
-
101
- def get_parent(self): return self.parent_header
102
-
103
- def init_magics(self):
104
- super().init_magics()
105
- self.register_magics(KernelMagics)
106
- self.magics_manager.register_alias('ed', 'edit')
107
-
108
- def init_virtualenv(self): pass
109
-
110
- def system_piped(self, cmd):
111
- "Call the given cmd in a subprocess, piping stdout/err "
112
- if cmd.rstrip().endswith('&'): raise OSError("Background processes not supported.")
113
- if sys.platform == 'win32':
114
- cmd = self.var_expand(cmd, depth=1)
115
- from IPython.utils._process_win32 import AvoidUNCPath
116
- with AvoidUNCPath() as path:
117
- if path is not None: cmd = 'pushd %s &&%s' % (path, cmd)
118
- self.user_ns['_exit_code'] = system(cmd)
119
- else: self.user_ns['_exit_code'] = system(self.var_expand(cmd, depth=1))
120
-
121
- system = system_piped
122
-
123
- InteractiveShellABC.register(FastInteractiveShell)
124
-
@@ -1,223 +0,0 @@
1
- # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_shell.ipynb.
2
-
3
- # %% ../nbs/02_shell.ipynb 2
4
- from __future__ import annotations
5
-
6
- import os
7
- os.environ['MPLBACKEND'] = 'module://matplotlib_inline.backend_inline'
8
-
9
- from fastcore.imports import *
10
- from fastcore.basics import *
11
- from fastcore.script import call_parse
12
-
13
- import multiprocessing
14
- try:
15
- if sys.platform == 'darwin': multiprocessing.set_start_method("fork")
16
- except RuntimeError: pass # if re-running cell
17
-
18
- import tokenize
19
- from IPython.core.interactiveshell import InteractiveShell
20
- from IPython.core.displayhook import DisplayHook
21
- from IPython.core.displaypub import DisplayPublisher
22
- from base64 import b64encode
23
- from io import StringIO
24
-
25
- from .fastshell import FastInteractiveShell
26
- from .nbio import *
27
- from .nbio import _dict2obj
28
-
29
- from collections.abc import Callable
30
-
31
- # %% auto 0
32
- __all__ = ['CaptureShell', 'exec_nb']
33
-
34
- # %% ../nbs/02_shell.ipynb 4
35
- # IPython requires a DisplayHook and DisplayPublisher
36
- # We override `__call__` and `publish` to save outputs instead of printing them
37
- class _CaptureHook(DisplayHook):
38
- "Called when displaying a result"
39
-
40
- def quiet(self):
41
- "Should we silence because of ';'?"
42
- sio = StringIO(self.shell._code)
43
- tokens = list(tokenize.generate_tokens(sio.readline))
44
- for t in reversed(tokens):
45
- if t.type in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT): continue
46
- return t.type == tokenize.OP and t.string == ';'
47
-
48
- def __call__(self, result=None):
49
- if result is None or self.quiet(): return
50
- self.fill_exec_result(result)
51
- self.shell._result(result)
52
-
53
- class _CapturePub(DisplayPublisher):
54
- "Called when adding an output"
55
- def publish(self, data, metadata=None, **kwargs): self.shell._add_out(data, metadata, typ='display_data')
56
-
57
- # %% ../nbs/02_shell.ipynb 5
58
- # These are the standard notebook formats for exception and stream data (e.g stdout)
59
- def _out_exc(ename, evalue, traceback): return dict(ename=str(ename), evalue=str(evalue), output_type='error', traceback=traceback)
60
- def _out_stream(text, name): return dict(name=name, output_type='stream', text=text.splitlines(True))
61
-
62
- # %% ../nbs/02_shell.ipynb 6
63
- def _format_mimedata(k, v):
64
- "Format mime-type keyed data consistently with Jupyter"
65
- if k.startswith('text/'): return v.splitlines(True)
66
- if k.startswith('image/') and isinstance(v, bytes):
67
- v = b64encode(v).decode()
68
- return v+'\n' if not v.endswith('\n') else v
69
- return v
70
-
71
- # %% ../nbs/02_shell.ipynb 8
72
- class CaptureShell(FastInteractiveShell):
73
- "Execute the IPython/Jupyter source code"
74
- def __init__(self,
75
- path:str|Path=None): # Add `path` to python path
76
- super().__init__(displayhook_class=_CaptureHook, display_pub_class=_CapturePub)
77
- InteractiveShell._instance = self
78
- self.out,self.count = [],1
79
- self.exc = self.result = self._fname = self._cell_idx = self._stdout = self._stderr = None
80
- if IN_NOTEBOOK:
81
- try: self.enable_matplotlib('inline')
82
- except ModuleNotFoundError: pass
83
- if path: self.set_path(path)
84
-
85
- def enable_matplotlib(self, gui=None):
86
- "Enable `matplotlib` in a nested shell"
87
- from matplotlib_inline.backend_inline import configure_inline_support
88
- configure_inline_support.current_backend = 'unset'
89
- return super().enable_matplotlib(gui)
90
-
91
- def set_path(self, path):
92
- "Add `path` to python path, or `path.parent` if it's a file"
93
- path = Path(path)
94
- if path.is_file(): path = path.parent
95
- self.run_cell(f"import sys; sys.path.insert(0, '{path.as_posix()}')")
96
-
97
- def enable_gui(self, gui=None):
98
- "Disable GUI (over-ridden; called by IPython)"
99
- pass
100
-
101
- def _showtraceback(self, etype, evalue, stb: str):
102
- self.out.append(_out_exc(etype, evalue, stb))
103
- self.exc = (etype, evalue, '\n'.join(stb))
104
-
105
- def _add_out(self, data, meta, typ='execute_result', **kwargs):
106
- self._stream()
107
- fd = {k:_format_mimedata(k,v) for k,v in data.items()}
108
- self.out.append(dict(data=fd, metadata=meta, output_type=typ, **kwargs))
109
-
110
- def _add_exec(self, result, meta, typ='execute_result'):
111
- self._add_out(result, meta, execution_count=self.count)
112
- self.count += 1
113
-
114
- def _result(self, result):
115
- self.result = result
116
- self._add_exec(*self.display_formatter.format(result))
117
-
118
- def _stream(self):
119
- for nm in ('stdout','stderr'):
120
- attr = '_'+nm
121
- std = getattr(self, attr)
122
- if std is not None:
123
- text = std.getvalue()
124
- if text:
125
- self.out.append(_out_stream(text, nm))
126
- setattr(self, attr, StringIO())
127
-
128
- # %% ../nbs/02_shell.ipynb 11
129
- @patch
130
- def run(self:CaptureShell,
131
- code:str, # Python/IPython code to run
132
- stdout=True, # Capture stdout and save as output?
133
- stderr=True): # Capture stderr and save as output?
134
- "Run `code`, returning a list of all outputs in Jupyter notebook format"
135
- self._code = code
136
- self.exc = False
137
- self.out.clear()
138
- sys_stdout,sys_stderr = sys.stdout,sys.stderr
139
- if stdout: self._stdout = sys.stdout = StringIO()
140
- if stderr: self._stderr = sys.stderr = StringIO()
141
- try: self.run_cell(code)
142
- finally: sys.stdout,sys.stderr = sys_stdout,sys_stderr
143
- self._stream()
144
- return [*self.out]
145
-
146
- # %% ../nbs/02_shell.ipynb 24
147
- @patch
148
- def cell(self:CaptureShell, cell, stdout=True, stderr=True):
149
- "Run `cell`, skipping if not code, and store outputs back in cell"
150
- if cell.cell_type!='code': return
151
- self._cell_idx = cell.idx_ + 1
152
- outs = self.run(cell.source)
153
- if outs:
154
- cell.outputs = _dict2obj(outs)
155
- for o in outs:
156
- if 'execution_count' in o: cell['execution_count'] = o['execution_count']
157
-
158
- # %% ../nbs/02_shell.ipynb 28
159
- def _false(o): return False
160
-
161
- @patch
162
- def run_all(self:CaptureShell,
163
- nb, # A notebook read with `nbclient` or `read_nb`
164
- exc_stop:bool=False, # Stop on exceptions?
165
- preproc:Callable=_false, # Called before each cell is executed
166
- postproc:Callable=_false, # Called after each cell is executed
167
- inject_code:str|None=None, # Code to inject into a cell
168
- inject_idx:int=0 # Cell to replace with `inject_code`
169
- ):
170
- "Run all cells in `nb`, stopping at first exception if `exc_stop`"
171
- if inject_code is not None: nb.cells[inject_idx].source = inject_code
172
- for cell in nb.cells:
173
- if not preproc(cell):
174
- self.cell(cell)
175
- postproc(cell)
176
- if self.exc and exc_stop: raise self.exc[1] from None
177
-
178
- # %% ../nbs/02_shell.ipynb 42
179
- @patch
180
- def execute(self:CaptureShell,
181
- src:str|Path, # Notebook path to read from
182
- dest:str|None=None, # Notebook path to write to
183
- exc_stop:bool=False, # Stop on exceptions?
184
- preproc:Callable=_false, # Called before each cell is executed
185
- postproc:Callable=_false, # Called after each cell is executed
186
- inject_code:str|None=None, # Code to inject into a cell
187
- inject_path:str|Path|None=None, # Path to file containing code to inject into a cell
188
- inject_idx:int=0 # Cell to replace with `inject_code`
189
- ):
190
- "Execute notebook from `src` and save with outputs to `dest"
191
- nb = read_nb(src)
192
- self._fname = src
193
- self.set_path(Path(src).parent.resolve())
194
- if inject_path is not None: inject_code = Path(inject_path).read_text()
195
- self.run_all(nb, exc_stop=exc_stop, preproc=preproc, postproc=postproc,
196
- inject_code=inject_code, inject_idx=inject_idx)
197
- if dest: write_nb(nb, dest)
198
-
199
- # %% ../nbs/02_shell.ipynb 46
200
- @patch
201
- def prettytb(self:CaptureShell,
202
- fname:str|Path=None): # filename to print alongside the traceback
203
- "Show a pretty traceback for notebooks, optionally printing `fname`."
204
- fname = fname if fname else self._fname
205
- _fence = '='*75
206
- cell_intro_str = f"While Executing Cell #{self._cell_idx}:" if self._cell_idx else "While Executing:"
207
- cell_str = f"\n{cell_intro_str}\n{self.exc[-1]}"
208
- fname_str = f' in {fname}' if fname else ''
209
- return f"{type(self.exc[1]).__name__}{fname_str}:\n{_fence}\n{cell_str}\n"
210
-
211
- # %% ../nbs/02_shell.ipynb 65
212
- @call_parse
213
- def exec_nb(
214
- src:str, # Notebook path to read from
215
- dest:str='', # Notebook path to write to
216
- exc_stop:bool=False, # Stop on exceptions?
217
- inject_code:str=None, # Code to inject into a cell
218
- inject_path:str=None, # Path to file containing code to inject into a cell
219
- inject_idx:int=0 # Cell to replace with `inject_code`
220
- ):
221
- "Execute notebook from `src` and save with outputs to `dest`"
222
- CaptureShell().execute(src, dest, exc_stop=exc_stop, inject_code=inject_code,
223
- inject_path=inject_path, inject_idx=inject_idx)
File without changes
File without changes
File without changes
File without changes