Skip to content

Commit 7667137

Browse files
committed
fixes #801
1 parent 6936188 commit 7667137

3 files changed

Lines changed: 892 additions & 5 deletions

File tree

fastcore/_modidx.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,30 @@
494494
'fastcore.nbio.NbCell.__init__': ('nbio.html#nbcell.__init__', 'fastcore/nbio.py'),
495495
'fastcore.nbio.NbCell.parsed_': ('nbio.html#nbcell.parsed_', 'fastcore/nbio.py'),
496496
'fastcore.nbio.NbCell.set_source': ('nbio.html#nbcell.set_source', 'fastcore/nbio.py'),
497+
'fastcore.nbio.Notebook': ('nbio.html#notebook', 'fastcore/nbio.py'),
498+
'fastcore.nbio.Notebook.__contains__': ('nbio.html#notebook.__contains__', 'fastcore/nbio.py'),
499+
'fastcore.nbio.Notebook.__delitem__': ('nbio.html#notebook.__delitem__', 'fastcore/nbio.py'),
500+
'fastcore.nbio.Notebook.__getitem__': ('nbio.html#notebook.__getitem__', 'fastcore/nbio.py'),
501+
'fastcore.nbio.Notebook.__init__': ('nbio.html#notebook.__init__', 'fastcore/nbio.py'),
502+
'fastcore.nbio.Notebook.__iter__': ('nbio.html#notebook.__iter__', 'fastcore/nbio.py'),
503+
'fastcore.nbio.Notebook.__len__': ('nbio.html#notebook.__len__', 'fastcore/nbio.py'),
504+
'fastcore.nbio.Notebook.__repr__': ('nbio.html#notebook.__repr__', 'fastcore/nbio.py'),
505+
'fastcore.nbio.Notebook.__setitem__': ('nbio.html#notebook.__setitem__', 'fastcore/nbio.py'),
506+
'fastcore.nbio.Notebook._id2cell': ('nbio.html#notebook._id2cell', 'fastcore/nbio.py'),
507+
'fastcore.nbio.Notebook.add': ('nbio.html#notebook.add', 'fastcore/nbio.py'),
508+
'fastcore.nbio.Notebook.cells': ('nbio.html#notebook.cells', 'fastcore/nbio.py'),
509+
'fastcore.nbio.Notebook.concise': ('nbio.html#notebook.concise', 'fastcore/nbio.py'),
510+
'fastcore.nbio.Notebook.find': ('nbio.html#notebook.find', 'fastcore/nbio.py'),
511+
'fastcore.nbio.Notebook.md': ('nbio.html#notebook.md', 'fastcore/nbio.py'),
512+
'fastcore.nbio.Notebook.meta': ('nbio.html#notebook.meta', 'fastcore/nbio.py'),
513+
'fastcore.nbio.Notebook.move': ('nbio.html#notebook.move', 'fastcore/nbio.py'),
514+
'fastcore.nbio.Notebook.open': ('nbio.html#notebook.open', 'fastcore/nbio.py'),
515+
'fastcore.nbio.Notebook.save': ('nbio.html#notebook.save', 'fastcore/nbio.py'),
516+
'fastcore.nbio.Notebook.view': ('nbio.html#notebook.view', 'fastcore/nbio.py'),
497517
'fastcore.nbio._dict2obj': ('nbio.html#_dict2obj', 'fastcore/nbio.py'),
498518
'fastcore.nbio._read_json': ('nbio.html#_read_json', 'fastcore/nbio.py'),
519+
'fastcore.nbio.cell2xml': ('nbio.html#cell2xml', 'fastcore/nbio.py'),
520+
'fastcore.nbio.cells2xml': ('nbio.html#cells2xml', 'fastcore/nbio.py'),
499521
'fastcore.nbio.dict2nb': ('nbio.html#dict2nb', 'fastcore/nbio.py'),
500522
'fastcore.nbio.mk_cell': ('nbio.html#mk_cell', 'fastcore/nbio.py'),
501523
'fastcore.nbio.nb2dict': ('nbio.html#nb2dict', 'fastcore/nbio.py'),

fastcore/nbio.py

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/13_nbio.ipynb.
44

55
# %% auto #0
6-
__all__ = ['NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'nb2dict', 'nb2str', 'write_nb']
6+
__all__ = ['NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'nb2dict', 'nb2str', 'write_nb', 'cell2xml', 'cells2xml',
7+
'Notebook']
78

89
# %% ../nbs/13_nbio.ipynb #5faca748
910
from .basics import *
@@ -102,3 +103,100 @@ def write_nb(nb, path):
102103
old = Path(path).read_text(encoding='utf-8') if path.exists() else None
103104
if new!=old:
104105
with open(path, 'w', encoding='utf-8') as f: f.write(new)
106+
107+
# %% ../nbs/13_nbio.ipynb #102e32c6
108+
from .xml import Code,Markdown,Raw,NB,Source,Out,to_xml
109+
110+
# %% ../nbs/13_nbio.ipynb #ca73be1c
111+
_ctfuns_nb = dict(code=Code, markdown=Markdown, raw=Raw)
112+
113+
def cell2xml(cell, ids=True, incl_out=True):
114+
"Convert NbCell to concise XML format"
115+
f = _ctfuns_nb[cell.cell_type]
116+
kw = dict(id=cell.id) if ids else {}
117+
output = getattr(cell, 'outputs', None) if incl_out else None
118+
if not output: return f(cell.source, **kw)
119+
return f(Source(cell.source), Out(str(output)), **kw)
120+
121+
def cells2xml(cells, wrap=NB, ids=True, incl_out=True, **kw):
122+
"Convert notebook cells to XML format"
123+
return to_xml(wrap(*[cell2xml(c, ids=ids, incl_out=incl_out) for c in cells], **kw), do_escape=False)
124+
125+
126+
# %% ../nbs/13_nbio.ipynb #f9eda9e3
127+
class Notebook:
128+
"Read, query, and edit Jupyter notebooks"
129+
def __init__(self, nb, path=None):
130+
if not path: path = Path("unnamed.ipynb")
131+
store_attr()
132+
133+
@property
134+
def _id2cell(self): return {c.id: c for c in self.nb.cells}
135+
136+
@classmethod
137+
def open(cls, path):
138+
path = Path(path).resolve()
139+
return cls(read_nb(path), path)
140+
141+
def save(self, path=None): write_nb(self.nb, path or self.path)
142+
143+
@property
144+
def cells(self): return self.nb.cells
145+
@property
146+
def meta(self): return self.nb.metadata
147+
148+
def __getitem__(self, k): return self.cells[k] if isinstance(k, (int, slice)) else self._id2cell[k]
149+
def __setitem__(self, k, source): self[k].set_source(source)
150+
def __len__(self): return len(self.cells)
151+
def __iter__(self): return iter(self.cells)
152+
def __contains__(self, k): return k in self._id2cell
153+
def __delitem__(self, k): self.cells.remove(self[k])
154+
def __repr__(self): return cells2xml(self.cells, path=self.path)
155+
@property
156+
def concise (self): return cells2xml(self.cells, path=self.path.name, incl_out=False)
157+
158+
# %% ../nbs/13_nbio.ipynb #161b6662
159+
@patch
160+
def add(self:Notebook, source, cell_type='code', idx=None, after=None, before=None, **kwargs):
161+
"Add a new cell with `source` at `idx` (default: end), or `after`/`before` a cell id"
162+
if after: idx = next((i for i,c in enumerate(self.cells) if c.id==after), None)
163+
if idx is None and after: raise KeyError(after)
164+
if idx is not None: idx += 1
165+
elif before: idx = next((i for i,c in enumerate(self.cells) if c.id==before), None)
166+
if idx is None and before: raise KeyError(before)
167+
c = mk_cell(source, cell_type=cell_type, **kwargs)
168+
if idx is None: self.cells.append(c)
169+
else: self.cells.insert(idx, c)
170+
return c
171+
172+
# %% ../nbs/13_nbio.ipynb #8ad4e43b
173+
@patch
174+
def md(self:Notebook, source, idx=None, after=None, before=None, **kwargs):
175+
"Add a new cell with `source` at `idx` (default: end), or `after`/`before` a cell id"
176+
return self.add(source, cell_type='markdown', idx=idx, after=after, before=before, **kwargs)
177+
178+
# %% ../nbs/13_nbio.ipynb #5a09a2fa
179+
@patch
180+
def find(self:Notebook, pat, cell_type=None):
181+
"Find cells with source matching regex `pat`"
182+
return [c for c in self.cells if re.search(pat, c.source) and (not cell_type or c.cell_type==cell_type)]
183+
184+
# %% ../nbs/13_nbio.ipynb #63ba4a93
185+
@patch
186+
def move(self:Notebook, src_ids, after=None, before=None):
187+
"Move cells with `src_ids` after/before a cell id, or to end"
188+
cells = [self[k] for k in listify(src_ids)]
189+
for c in cells: self.cells.remove(c)
190+
if after: idx = next((i+1 for i,c in enumerate(self.cells) if c.id==after), None)
191+
elif before: idx = next((i for i,c in enumerate(self.cells) if c.id==before), None)
192+
else: idx = len(self.cells)
193+
if idx is None: raise KeyError(after or before)
194+
for i,c in enumerate(cells): self.cells.insert(idx+i, c)
195+
196+
# %% ../nbs/13_nbio.ipynb #e064c39e
197+
@patch
198+
def view(self:Notebook, id, nums=True):
199+
"Show cell source with optional line numbers"
200+
lines = self[id].source.splitlines()
201+
if nums: lines = [f'{i+1:6d}{l}' for i,l in enumerate(lines)]
202+
return '\n'.join(lines)

0 commit comments

Comments
 (0)