Skip to content

Commit a3c1ada

Browse files
committed
fixes #800
1 parent ebe62c2 commit a3c1ada

4 files changed

Lines changed: 838 additions & 0 deletions

File tree

fastcore/_modidx.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,21 @@
486486
'fastcore.meta.use_kwargs': ('meta.html#use_kwargs', 'fastcore/meta.py'),
487487
'fastcore.meta.use_kwargs_dict': ('meta.html#use_kwargs_dict', 'fastcore/meta.py')},
488488
'fastcore.nb_imports': {},
489+
'fastcore.nbio': { 'fastcore.nbio.NbCell': ('nbio.html#nbcell', 'fastcore/nbio.py'),
490+
'fastcore.nbio.NbCell.__eq__': ('nbio.html#nbcell.__eq__', 'fastcore/nbio.py'),
491+
'fastcore.nbio.NbCell.__hash__': ('nbio.html#nbcell.__hash__', 'fastcore/nbio.py'),
492+
'fastcore.nbio.NbCell.__init__': ('nbio.html#nbcell.__init__', 'fastcore/nbio.py'),
493+
'fastcore.nbio.NbCell.parsed_': ('nbio.html#nbcell.parsed_', 'fastcore/nbio.py'),
494+
'fastcore.nbio.NbCell.set_source': ('nbio.html#nbcell.set_source', 'fastcore/nbio.py'),
495+
'fastcore.nbio._dict2obj': ('nbio.html#_dict2obj', 'fastcore/nbio.py'),
496+
'fastcore.nbio._read_json': ('nbio.html#_read_json', 'fastcore/nbio.py'),
497+
'fastcore.nbio.dict2nb': ('nbio.html#dict2nb', 'fastcore/nbio.py'),
498+
'fastcore.nbio.mk_cell': ('nbio.html#mk_cell', 'fastcore/nbio.py'),
499+
'fastcore.nbio.nb2dict': ('nbio.html#nb2dict', 'fastcore/nbio.py'),
500+
'fastcore.nbio.nb2str': ('nbio.html#nb2str', 'fastcore/nbio.py'),
501+
'fastcore.nbio.new_nb': ('nbio.html#new_nb', 'fastcore/nbio.py'),
502+
'fastcore.nbio.read_nb': ('nbio.html#read_nb', 'fastcore/nbio.py'),
503+
'fastcore.nbio.write_nb': ('nbio.html#write_nb', 'fastcore/nbio.py')},
489504
'fastcore.net': { 'fastcore.net.HTTP4xxClientError': ('net.html#http4xxclienterror', 'fastcore/net.py'),
490505
'fastcore.net.HTTP5xxServerError': ('net.html#http5xxservererror', 'fastcore/net.py'),
491506
'fastcore.net.Request.summary': ('net.html#request.summary', 'fastcore/net.py'),

fastcore/nbio.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""Reading and writing Jupyter notebooks"""
2+
3+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/13_nbio.ipynb.
4+
5+
# %% auto #0
6+
__all__ = ['NbCell', 'dict2nb', 'read_nb', 'mk_cell', 'new_nb', 'nb2dict', 'nb2str', 'write_nb']
7+
8+
# %% ../nbs/13_nbio.ipynb #5faca748
9+
from .basics import *
10+
from .xtras import rtoken_hex
11+
from .imports import *
12+
13+
import ast,functools
14+
from pprint import pformat,pprint
15+
from json import loads,dumps
16+
17+
# %% ../nbs/13_nbio.ipynb #9d2e7add
18+
def _read_json(self, encoding=None, errors=None):
19+
return loads(Path(self).read_text(encoding=encoding, errors=errors))
20+
21+
# %% ../nbs/13_nbio.ipynb #49d7bf89
22+
class NbCell(AttrDict):
23+
def __init__(self, idx, cell):
24+
super().__init__(cell)
25+
self.idx_ = idx
26+
if 'id' not in self: self.id = rtoken_hex(4)
27+
if 'source' in self: self.set_source(self.source)
28+
29+
def set_source(self, source):
30+
self.source = ''.join(source)
31+
if '_parsed_' in self: del(self['_parsed_'])
32+
33+
def parsed_(self):
34+
if self.cell_type!='code' or self.source.strip()[:1] in ['%', '!']: return
35+
if '_parsed_' not in self:
36+
try: self._parsed_ = ast.parse(self.source).body
37+
# you can assign the result of ! to a variable in a notebook cell
38+
# which will result in a syntax error if parsed with the ast module.
39+
except SyntaxError: return
40+
return self._parsed_
41+
42+
def __hash__(self): return hash(self.source) + hash(self.cell_type)
43+
def __eq__(self,o): return self.source==o.source and self.cell_type==o.cell_type
44+
45+
# %% ../nbs/13_nbio.ipynb #6c742618
46+
def _dict2obj(d, list_func=list, dict_func=AttrDict):
47+
"Convert (possibly nested) dicts (or lists of dicts) to `AttrDict`"
48+
if isinstance(d, list): return list(map(_dict2obj, d))
49+
if not isinstance(d, dict): return d
50+
return dict_func(**{k:_dict2obj(v) for k,v in d.items()})
51+
52+
def dict2nb(js=None, **kwargs):
53+
"Convert dict `js` to an `AttrDict`, "
54+
nb = _dict2obj(js or kwargs)
55+
nb.cells = [NbCell(*o) for o in enumerate(nb.cells)]
56+
return nb
57+
58+
# %% ../nbs/13_nbio.ipynb #0626f06e
59+
def read_nb(path):
60+
"Return notebook at `path`"
61+
res = dict2nb(_read_json(path, encoding='utf-8'))
62+
res['path_'] = str(path)
63+
return res
64+
65+
# %% ../nbs/13_nbio.ipynb #c86fe311
66+
def mk_cell(text, # `source` attr in cell
67+
cell_type='code', # `cell_type` attr in cell
68+
**kwargs): # any other attrs to add to cell
69+
"Create an `NbCell` containing `text`"
70+
assert cell_type in {'code', 'markdown', 'raw'}
71+
if 'metadata' not in kwargs: kwargs['metadata']={}
72+
if cell_type == 'code':
73+
kwargs['outputs']=[]
74+
kwargs['execution_count']=0
75+
return NbCell(0, dict(cell_type=cell_type, source=text, directives_={}, **kwargs))
76+
77+
# %% ../nbs/13_nbio.ipynb #e7af3290
78+
def new_nb(cells=None, meta=None, nbformat=4, nbformat_minor=5):
79+
"Returns an empty new notebook"
80+
cells = [o if isinstance(o,dict) else mk_cell(o) for o in cells]
81+
return dict2nb(cells=cells or [],metadata=meta or {},nbformat=nbformat,nbformat_minor=nbformat_minor)
82+
83+
# %% ../nbs/13_nbio.ipynb #7c7bab22
84+
def nb2dict(d, k=None):
85+
"Convert parsed notebook to `dict`"
86+
if k=='source': return d.splitlines(keepends=True)
87+
if isinstance(d, list): return list(map(nb2dict,d))
88+
if not isinstance(d, dict): return d
89+
return dict(**{k:nb2dict(v,k) for k,v in d.items() if k[-1] != '_'})
90+
91+
# %% ../nbs/13_nbio.ipynb #21ffe533
92+
def nb2str(nb):
93+
"Convert `nb` to a `str`"
94+
if isinstance(nb, (AttrDict,list)): nb = nb2dict(nb)
95+
return dumps(nb, sort_keys=True, indent=1, ensure_ascii=False) + "\n"
96+
97+
# %% ../nbs/13_nbio.ipynb #d979e25a
98+
def write_nb(nb, path):
99+
"Write `nb` to `path`"
100+
new = nb2str(nb)
101+
path = Path(path)
102+
old = Path(path).read_text(encoding='utf-8') if path.exists() else None
103+
if new!=old:
104+
with open(path, 'w', encoding='utf-8') as f: f.write(new)

0 commit comments

Comments
 (0)