Skip to content

Commit 2a39f58

Browse files
committed
update
1 parent c05153d commit 2a39f58

File tree

10 files changed

+380
-131
lines changed

10 files changed

+380
-131
lines changed

nbprocess/docs.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/06_docs.ipynb.
22

33
# %% auto 0
4-
__all__ = ['rm_blank_proc', 'strip_ansi_proc', 'html_escape', 'insert_warning', 'write_md']
4+
__all__ = ['rm_blank_proc', 'html_escape', 'insert_warning', 'write_md']
55

66
# %% ../nbs/06_docs.ipynb 3
77
from .read import *
@@ -11,35 +11,25 @@
1111

1212
from fastcore.script import *
1313
from fastcore.imports import *
14+
from fastcore.basics import *
1415
from fastcore.xtras import *
1516

1617
import uuid
1718
import tempfile
1819

19-
# %% ../nbs/06_docs.ipynb 5
20+
# %% ../nbs/06_docs.ipynb 6
2021
def rm_blank_proc(cell):
2122
"Remove empty cells"
2223
if(cell.source.strip()==''): cell.source = None
2324

24-
# %% ../nbs/06_docs.ipynb 6
25-
_re_ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
26-
27-
def strip_ansi_proc(cell):
28-
"Strip Ansi Characters"
29-
for o in cell.get('outputs', []):
30-
if o.get('name') == 'stdout': o['text'] = _re_ansi_escape.sub('', o.text)
31-
3225
# %% ../nbs/06_docs.ipynb 7
33-
def html_escape(cell):
26+
@outp_proc('data', 'text/html')
27+
def html_escape(cell, outp, item):
3428
"Place HTML in a codeblock and surround it with a <HTMLOutputBlock> component."
35-
for o in cell.get('outputs', []):
36-
html = nested_idx(o, 'data', 'text/html')
37-
if html:
38-
cell.metadata.html_output = True
39-
html = ''.join(html).strip()
40-
o['data']['text/html'] = f'```html\n{html}\n```'
29+
cell.metadata.html_output = True
30+
return f'```html\n{item}\n```'
4131

42-
# %% ../nbs/06_docs.ipynb 8
32+
# %% ../nbs/06_docs.ipynb 10
4333
def _get_cell_id(id_length=36): return uuid.uuid4().hex[:id_length]
4434

4535
def _get_md_cell(content="<!--- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT!-->"):
@@ -50,10 +40,11 @@ def insert_warning(nb):
5040
"Insert Autogenerated Warning Into Notebook after the first cell."
5141
nb.cells = nb.cells[:1] + [_get_md_cell()] + nb.cells[1:]
5242

53-
# %% ../nbs/06_docs.ipynb 10
54-
def write_md(nb_path, procs=None, post_procs=None, tpl_file='ob.tpl'):
55-
nbp = NBProcessor(nb_path, procs)
43+
# %% ../nbs/06_docs.ipynb 11
44+
def write_md(nb_path, procs=None, post_procs=None, outp_procs=None, pre_procs=None, tpl_file='ob.tpl'):
45+
nbp = NBProcessor(nb_path, procs, outp_procs=outp_procs)
5646
nb = nbp.nb
47+
for proc in L(pre_procs): proc(nb)
5748
nbp.process()
5849
for proc in L(post_procs): proc(nb)
5950

nbprocess/export.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_export.ipynb.
22

33
# %% auto 0
4-
__all__ = ['extract_comments', 'NBProcessor', 'ExportModuleProc', 'rm_comments_proc', 'create_modules', 'nb_export', 'nbs_export']
4+
__all__ = ['extract_comments', 'NBProcessor', 'outp_proc', 'strip_ansi', 'ExportModuleProc', 'rm_comments_proc', 'create_modules',
5+
'nb_export', 'nbs_export']
56

67
# %% ../nbs/02_export.ipynb 3
78
from .read import *
@@ -36,31 +37,51 @@ def _param_count(f):
3637
# %% ../nbs/02_export.ipynb 12
3738
class NBProcessor:
3839
"Process cells and nbdev comments in a notebook"
39-
def __init__(self, path=None, procs=None, nb=None, debug=False):
40+
def __init__(self, path=None, procs=None, outp_procs=None, nb=None, debug=False):
4041
self.nb = read_nb(path) if nb is None else nb
41-
self.procs,self.debug = L(procs),debug
42+
self.procs,self.outp_procs,self.debug = L(procs),L(outp_procs),debug
4243

4344
def _process_cell(self, cell):
4445
self.cell = cell
4546
cell._comments = extract_comments(cell.source)
4647
for proc in self.procs:
4748
if callable(proc): proc(cell)
48-
if cell.cell_type=='code':
49-
for comment in cell._comments: self._process_comment(proc, comment)
49+
if cell.cell_type=='code': cell._comments.map(self._process_comment,proc)
50+
for proc in self.outp_procs: L(cell.get('outputs', [])).map(proc, cell)
5051

5152
def _process_comment(self, proc, comment):
5253
cmd,*args = comment
5354
f = getattr(proc, f'_{cmd}_', None)
5455
if not f or _param_count(f)-1<len(args): return True
5556
if self.debug: print(cmd, args, f)
5657
return f(self, *args)
57-
58+
5859
def process(self):
5960
"Process all cells with `process_cell`"
6061
for i in range_of(self.nb.cells): self._process_cell(self.nb.cells[i])
6162
self.nb.cells = [c for c in self.nb.cells if c.source is not None]
6263

6364
# %% ../nbs/02_export.ipynb 16
65+
class outp_proc:
66+
"Decorator for funcs to pass to `NBProcessor` for processing cell output"
67+
def __init__(self, *idxs): self.idxs = idxs
68+
def __call__(self, func):
69+
def _inner(cell, outp):
70+
item = nested_idx(outp, *self.idxs)
71+
if not item: return
72+
res = func(cell, outp, ''.join(item))
73+
if res is not None: set_nested_idx(outp, res.splitlines(True), *self.idxs)
74+
return _inner
75+
76+
# %% ../nbs/02_export.ipynb 17
77+
_re_ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
78+
79+
@outp_proc('text')
80+
def strip_ansi(cell, outp, item):
81+
"Output processor to strip Ansi Characters"
82+
if outp.get('name') == 'stdout': return _re_ansi_escape.sub('', item)
83+
84+
# %% ../nbs/02_export.ipynb 20
6485
class ExportModuleProc:
6586
"A processor which exports code to a module"
6687
def __init__(self): self.modules,self.in_all = defaultdict(L),defaultdict(L)
@@ -70,12 +91,12 @@ def _export_(self, nbp, exp_to=None):
7091
self._exporti_(nbp, exp_to)
7192
self.in_all[ifnone(exp_to, '#')].append(nbp.cell)
7293

73-
# %% ../nbs/02_export.ipynb 19
94+
# %% ../nbs/02_export.ipynb 23
7495
def rm_comments_proc(cell):
7596
"A proc that removes comments from each NB cell source"
7697
cell.source = ''.join(cell.source.splitlines(True)[len(cell._comments):])
7798

78-
# %% ../nbs/02_export.ipynb 20
99+
# %% ../nbs/02_export.ipynb 24
79100
def create_modules(path, dest, procs=None, debug=False, mod_maker=ModuleMaker):
80101
"Create module(s) from notebook"
81102
exp = ExportModuleProc()
@@ -87,12 +108,12 @@ def create_modules(path, dest, procs=None, debug=False, mod_maker=ModuleMaker):
87108
mm = mod_maker(dest=dest, name=name, nb_path=path, is_new=mod=='#')
88109
mm.make(cells, all_cells)
89110

90-
# %% ../nbs/02_export.ipynb 27
111+
# %% ../nbs/02_export.ipynb 31
91112
def nb_export(nbname, lib_name=None):
92113
if lib_name is None: lib_name = get_config().lib_name
93114
create_modules(nbname, lib_name)
94115

95-
# %% ../nbs/02_export.ipynb 28
116+
# %% ../nbs/02_export.ipynb 32
96117
@call_parse
97118
def nbs_export(
98119
path:str='.', # path or filename

nbprocess/read.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ def __init__(self, idx, cell):
2020
super().__init__(cell)
2121
self.idx_ = idx
2222
if 'source' in self: self.set_source(self.source)
23-
for o in cell.get('outputs', []):
24-
if 'text' in o: o['text'] = ''.join(o.text)
2523

2624
def __repr__(self): return self.source
2725

nbprocess/sync.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# %% ../nbs/03_sync.ipynb 5
1818
def nb2dict(d, k=None):
1919
"Convert parsed notebook to `dict`"
20-
if k in ('source','text'): return d.splitlines(keepends=True)
20+
if k in ('source',): return d.splitlines(keepends=True)
2121
if isinstance(d, (L,list)): return list(L(d).map(nb2dict))
2222
if not isinstance(d, dict): return d
2323
return dict(**{k:nb2dict(v,k) for k,v in d.items() if k[-1] != '_'})

nbs/00_read.ipynb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,6 @@
184184
" super().__init__(cell)\n",
185185
" self.idx_ = idx\n",
186186
" if 'source' in self: self.set_source(self.source)\n",
187-
" for o in cell.get('outputs', []):\n",
188-
" if 'text' in o: o['text'] = ''.join(o.text)\n",
189187
"\n",
190188
" def __repr__(self): return self.source\n",
191189
"\n",

nbs/02_export.ipynb

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -126,32 +126,32 @@
126126
},
127127
{
128128
"cell_type": "code",
129-
"execution_count": 8,
129+
"execution_count": 11,
130130
"metadata": {},
131131
"outputs": [],
132132
"source": [
133133
"#export\n",
134134
"class NBProcessor:\n",
135135
" \"Process cells and nbdev comments in a notebook\"\n",
136-
" def __init__(self, path=None, procs=None, nb=None, debug=False):\n",
136+
" def __init__(self, path=None, procs=None, outp_procs=None, nb=None, debug=False):\n",
137137
" self.nb = read_nb(path) if nb is None else nb\n",
138-
" self.procs,self.debug = L(procs),debug\n",
138+
" self.procs,self.outp_procs,self.debug = L(procs),L(outp_procs),debug\n",
139139
"\n",
140140
" def _process_cell(self, cell):\n",
141141
" self.cell = cell\n",
142142
" cell._comments = extract_comments(cell.source)\n",
143143
" for proc in self.procs:\n",
144144
" if callable(proc): proc(cell)\n",
145-
" if cell.cell_type=='code':\n",
146-
" for comment in cell._comments: self._process_comment(proc, comment)\n",
145+
" if cell.cell_type=='code': cell._comments.map(self._process_comment,proc)\n",
146+
" for proc in self.outp_procs: L(cell.get('outputs', [])).map(proc, cell)\n",
147147
"\n",
148148
" def _process_comment(self, proc, comment):\n",
149149
" cmd,*args = comment\n",
150150
" f = getattr(proc, f'_{cmd}_', None)\n",
151151
" if not f or _param_count(f)-1<len(args): return True\n",
152152
" if self.debug: print(cmd, args, f)\n",
153153
" return f(self, *args)\n",
154-
"\n",
154+
" \n",
155155
" def process(self):\n",
156156
" \"Process all cells with `process_cell`\"\n",
157157
" for i in range_of(self.nb.cells): self._process_cell(self.nb.cells[i])\n",
@@ -160,7 +160,7 @@
160160
},
161161
{
162162
"cell_type": "code",
163-
"execution_count": 9,
163+
"execution_count": 12,
164164
"metadata": {},
165165
"outputs": [],
166166
"source": [
@@ -170,7 +170,7 @@
170170
},
171171
{
172172
"cell_type": "code",
173-
"execution_count": 10,
173+
"execution_count": 13,
174174
"metadata": {},
175175
"outputs": [
176176
{
@@ -187,6 +187,61 @@
187187
"proc.process()"
188188
]
189189
},
190+
{
191+
"cell_type": "markdown",
192+
"metadata": {},
193+
"source": [
194+
"## Output processors -"
195+
]
196+
},
197+
{
198+
"cell_type": "code",
199+
"execution_count": 14,
200+
"metadata": {},
201+
"outputs": [],
202+
"source": [
203+
"#export\n",
204+
"class outp_proc:\n",
205+
" \"Decorator for funcs to pass to `NBProcessor` for processing cell output\"\n",
206+
" def __init__(self, *idxs): self.idxs = idxs\n",
207+
" def __call__(self, func):\n",
208+
" def _inner(cell, outp):\n",
209+
" item = nested_idx(outp, *self.idxs)\n",
210+
" if not item: return\n",
211+
" res = func(cell, outp, ''.join(item))\n",
212+
" if res is not None: set_nested_idx(outp, res.splitlines(True), *self.idxs)\n",
213+
" return _inner"
214+
]
215+
},
216+
{
217+
"cell_type": "code",
218+
"execution_count": 15,
219+
"metadata": {},
220+
"outputs": [],
221+
"source": [
222+
"#export\n",
223+
"_re_ansi_escape = re.compile(r'\\x1B(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])')\n",
224+
"\n",
225+
"@outp_proc('text')\n",
226+
"def strip_ansi(cell, outp, item):\n",
227+
" \"Output processor to strip Ansi Characters\"\n",
228+
" if outp.get('name') == 'stdout': return _re_ansi_escape.sub('', item)"
229+
]
230+
},
231+
{
232+
"cell_type": "code",
233+
"execution_count": 16,
234+
"metadata": {},
235+
"outputs": [],
236+
"source": [
237+
"proc = NBProcessor(everything_fn, outp_procs=strip_ansi)\n",
238+
"proc.process()\n",
239+
"\n",
240+
"def _valid(o): return not o or not _re_ansi_escape.search(o)\n",
241+
"for cell in proc.nb.cells:\n",
242+
" assert all(_valid(nested_idx(o, 'text', 0)) for o in cell.get('outputs', [])) "
243+
]
244+
},
190245
{
191246
"cell_type": "markdown",
192247
"metadata": {},
@@ -196,7 +251,7 @@
196251
},
197252
{
198253
"cell_type": "code",
199-
"execution_count": 11,
254+
"execution_count": 17,
200255
"metadata": {},
201256
"outputs": [],
202257
"source": [
@@ -222,7 +277,7 @@
222277
},
223278
{
224279
"cell_type": "code",
225-
"execution_count": 12,
280+
"execution_count": 18,
226281
"metadata": {},
227282
"outputs": [],
228283
"source": [
@@ -236,7 +291,7 @@
236291
},
237292
{
238293
"cell_type": "code",
239-
"execution_count": 13,
294+
"execution_count": 19,
240295
"metadata": {},
241296
"outputs": [],
242297
"source": [
@@ -248,7 +303,7 @@
248303
},
249304
{
250305
"cell_type": "code",
251-
"execution_count": 14,
306+
"execution_count": 20,
252307
"metadata": {},
253308
"outputs": [],
254309
"source": [
@@ -274,7 +329,7 @@
274329
},
275330
{
276331
"cell_type": "code",
277-
"execution_count": 15,
332+
"execution_count": 21,
278333
"metadata": {},
279334
"outputs": [],
280335
"source": [
@@ -295,7 +350,7 @@
295350
},
296351
{
297352
"cell_type": "code",
298-
"execution_count": 16,
353+
"execution_count": 22,
299354
"metadata": {},
300355
"outputs": [],
301356
"source": [
@@ -317,7 +372,7 @@
317372
},
318373
{
319374
"cell_type": "code",
320-
"execution_count": 17,
375+
"execution_count": 23,
321376
"metadata": {},
322377
"outputs": [],
323378
"source": [
@@ -329,7 +384,7 @@
329384
},
330385
{
331386
"cell_type": "code",
332-
"execution_count": 18,
387+
"execution_count": 24,
333388
"metadata": {},
334389
"outputs": [],
335390
"source": [
@@ -341,7 +396,7 @@
341396
},
342397
{
343398
"cell_type": "code",
344-
"execution_count": 19,
399+
"execution_count": 25,
345400
"metadata": {},
346401
"outputs": [],
347402
"source": [
@@ -374,7 +429,7 @@
374429
},
375430
{
376431
"cell_type": "code",
377-
"execution_count": 20,
432+
"execution_count": 26,
378433
"metadata": {},
379434
"outputs": [],
380435
"source": [

0 commit comments

Comments
 (0)