#|default_exp sync
Propagating small changes in the library back to notebooks
The library is primarily developed in notebooks so any big changes should be made there. But sometimes, it's easier to fix small bugs or typos in the modules directly. nbdev_update_lib
is the function that will propagate those changes back to the corresponding notebooks. Note that you can't create new cells or reorder cells with that functionality, so your corrections should remain limited.
#|export
from nbdev.imports import *
from nbdev.config import *
from nbdev.maker import *
from nbdev.process import *
from nbdev.export import *
from execnb.nbio import *
from fastcore.script import *
from fastcore.xtras import *
import ast,tempfile
#|hide
from pdb import set_trace
from fastcore.test import *
#|export
def absolute_import(name, fname, level):
"Unwarps a relative import in `name` according to `fname`"
if not level: return name
mods = fname.split(os.path.sep)
if not name: return '.'.join(mods)
return '.'.join(mods[:len(mods)-level+1]) + f".{name}"
test_eq(absolute_import('xyz', 'nbdev', 0), 'xyz')
test_eq(absolute_import('', 'nbdev', 1), 'nbdev')
test_eq(absolute_import('core', 'nbdev', 1), 'nbdev.core')
test_eq(absolute_import('core', 'nbdev/vision', 2), 'nbdev.core')
test_eq(absolute_import('transform', 'nbdev/vision', 1), 'nbdev.vision.transform')
test_eq(absolute_import('notebook.core', 'nbdev/data', 2), 'nbdev.notebook.core')
#|export
_re_import = re.compile("from\s+\S+\s+import\s+\S")
#|hide
assert _re_import.match('from foo import bar')
assert not _re_import.match('#from foo import bar')
#|export
def _to_absolute(code, lib_name):
if not _re_import.search(code): return code
res = update_import(code, ast.parse(code).body, lib_name, absolute_import)
return ''.join(res) if res else code
def _update_lib(nbname, nb_locs, lib_name=None):
if lib_name is None: lib_name = get_config().lib_name
nbp = NBProcessor(nbname, ExportModuleProc(), rm_directives=False)
nb = nbp.nb
nbp.process()
for name,idx,code in nb_locs:
assert name==nbname
cell = nb.cells[int(idx)]
lines = cell.source.splitlines(True)
directives = ''.join(cell.source.splitlines(True)[:len(cell.directives_)])
cell.source = directives + _to_absolute(code, lib_name)
write_nb(nb, nbname)
#|export
def _get_call(s):
top,*rest = s.splitlines()
return (*top.split(),'\n'.join(rest))
@call_parse
def nbdev_update(fname:str): # A Python file name to update
"Propagate change in modules matching `fname` to notebooks that created them"
if os.environ.get('IN_TEST',0): return
code_cells = Path(fname).read_text().split("\n# %% ")[1:]
locs = L(_get_call(s) for s in code_cells if not s.startswith('auto '))
for nbname,nb_locs in groupby(locs, 0).items(): _update_lib(nbname, nb_locs)
#|hide
# nbdev_update("../nbdev/sync.py")
#|hide
#|eval: false
from nbdev.doclinks import nbdev_export
nbdev_export()