#|hide
#|default_exp export
Exporting a notebook to a library
#|export
from nbdev.config import *
from nbdev.maker import *
from nbdev.imports import *
from nbdev.process import *
from fastcore.script import *
from fastcore.basics import *
from fastcore.imports import *
from collections import defaultdict
#|hide
from fastcore.test import *
from pdb import set_trace
from importlib import reload
from fastcore import shutil
from execnb.nbio import read_nb
#|export
class ExportModuleProc:
"A processor which exports code to a module"
def begin(self): self.modules,self.in_all = defaultdict(L),defaultdict(L)
def _default_exp_(self, cell, exp_to): self.default_exp = exp_to
def _exporti_(self, cell, exp_to=None): self.modules[ifnone(exp_to, '#')].append(cell)
def _export_(self, cell, exp_to=None):
self._exporti_(cell, exp_to)
self.in_all[ifnone(exp_to, '#')].append(cell)
_exports_=_export_
Specify dest
where the module(s) will be exported to, and optionally a class to use to create the module (ModuleMaker
, by default).
Exported cells are stored in a dict
called modules
, where the keys are the modules exported to. Those without an explicit module are stored in the '#'
key, which will be exported to default_exp
.
everything_fn = '..//tests/01_everything.ipynb'
exp = ExportModuleProc()
proc = NBProcessor(everything_fn, exp)
proc.process()
test_eq(exp.default_exp, 'everything')
assert 'print_function' in exp.modules['#'][0].source
assert 'h_n' in exp.in_all['some.thing'][0].source
#|export
def black_format(cell, # Cell to format
force=False): # Turn black formatting on regardless of settings.ini
"Processor to format code with `black`"
try: cfg = get_config()
except FileNotFoundError: return
if (str(cfg.get('black_formatting')).lower() != 'true' and not force) or cell.cell_type != 'code': return
try: import black
except: raise ImportError("You must install black: `pip install black` if you wish to use black formatting with nbdev")
else:
_format_str = partial(black.format_str, mode = black.Mode())
try: cell.source = _format_str(cell.source).strip()
except: pass
_cell = read_nb('..//tests/black.ipynb')['cells'][0]
black_format(_cell, force=True)
test_eq(_cell.source, 'j = [1, 2, 3]')
#|export
def nb_export(nbname, lib_path=None, procs=black_format, debug=False, mod_maker=ModuleMaker):
"Create module(s) from notebook"
if lib_path is None: lib_path = get_config().path('lib_path')
exp = ExportModuleProc()
nb = NBProcessor(nbname, [exp]+L(procs), debug=debug)
nb.process()
for mod,cells in exp.modules.items():
all_cells = exp.in_all[mod]
name = getattr(exp, 'default_exp', None) if mod=='#' else mod
if not name:
warn(f"Notebook '{nbname}' uses `#|export` without `#|default_exp` cell.\n"
"Note nbdev2 no longer supports nbdev1 syntax. Run `nbdev_migrate` to upgrade.\n"
"See https://nbdev.fast.ai/getting_started.html for more information.")
return
mm = mod_maker(dest=lib_path, name=name, nb_path=nbname, is_new=mod=='#')
mm.make(cells, all_cells, lib_path=lib_path)
Let's check we can import a test file:
#|eval: false
shutil.rmtree('tmp', ignore_errors=True)
nb_export('..//tests/00_some.thing.ipynb', 'tmp')
g = exec_new('import tmp.some.thing')
test_eq(g['tmp'].some.thing.__all__, ['a'])
test_eq(g['tmp'].some.thing.a, 1)
We'll also check that our 'everything' file exports correctly:
#|eval: false
nb_export(everything_fn, 'tmp')
g = exec_new('import tmp.everything; from tmp.everything import *')
_alls = L("a b d e m n o p q".split())
for s in _alls.map("{}_y"): assert s in g, s
for s in "c_y_nall _f_y_nall g_n h_n i_n j_n k_n l_n".split(): assert s not in g, s
for s in _alls.map("{}_y") + ["c_y_nall", "_f_y_nall"]: assert hasattr(g['tmp'].everything,s), s
That notebook should also export one extra function to tmp.some.thing
:
#|eval: false
del(sys.modules['tmp.some.thing']) # remove from module cache
g = exec_new('import tmp.some.thing')
test_eq(g['tmp'].some.thing.__all__, ['a','h_n'])
test_eq(g['tmp'].some.thing.h_n(), None)
#|eval: false
Path('../nbdev/export.py').unlink(missing_ok=True)
nb_export('04a_export.ipynb')
g = exec_new('import nbdev.export')
assert hasattr(g['nbdev'].export, 'nb_export')