nbdev 2.3.25__py3-none-any.whl → 2.4.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nbdev/__init__.py +1 -1
- nbdev/_modidx.py +22 -32
- nbdev/clean.py +18 -16
- nbdev/cli.py +57 -22
- nbdev/config.py +65 -37
- nbdev/diff.py +92 -0
- nbdev/doclinks.py +109 -50
- nbdev/export.py +33 -18
- nbdev/frontmatter.py +5 -3
- nbdev/maker.py +35 -33
- nbdev/merge.py +11 -9
- nbdev/migrate.py +20 -18
- nbdev/process.py +17 -15
- nbdev/processors.py +43 -30
- nbdev/qmd.py +9 -7
- nbdev/quarto.py +68 -29
- nbdev/release.py +46 -36
- nbdev/serve.py +8 -5
- nbdev/showdoc.py +45 -162
- nbdev/sync.py +17 -11
- nbdev/test.py +6 -4
- {nbdev-2.3.25.dist-info → nbdev-2.4.8.dist-info}/METADATA +40 -19
- nbdev-2.4.8.dist-info/RECORD +30 -0
- {nbdev-2.3.25.dist-info → nbdev-2.4.8.dist-info}/WHEEL +1 -1
- {nbdev-2.3.25.dist-info → nbdev-2.4.8.dist-info}/entry_points.txt +3 -0
- nbdev-2.3.25.dist-info/RECORD +0 -29
- {nbdev-2.3.25.dist-info → nbdev-2.4.8.dist-info/licenses}/LICENSE +0 -0
- {nbdev-2.3.25.dist-info → nbdev-2.4.8.dist-info}/top_level.txt +0 -0
nbdev/export.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"""Exporting a notebook to a library"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/04_export.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% auto 0
|
|
4
6
|
__all__ = ['ExportModuleProc', 'black_format', 'scrub_magics', 'optional_procs', 'nb_export']
|
|
5
7
|
|
|
6
|
-
# %% ../nbs/api/04_export.ipynb
|
|
8
|
+
# %% ../nbs/api/04_export.ipynb
|
|
7
9
|
from .config import *
|
|
8
10
|
from .maker import *
|
|
9
11
|
from .imports import *
|
|
@@ -12,10 +14,11 @@ from .process import *
|
|
|
12
14
|
from fastcore.script import *
|
|
13
15
|
from fastcore.basics import *
|
|
14
16
|
from fastcore.imports import *
|
|
17
|
+
from fastcore.meta import *
|
|
15
18
|
|
|
16
19
|
from collections import defaultdict
|
|
17
20
|
|
|
18
|
-
# %% ../nbs/api/04_export.ipynb
|
|
21
|
+
# %% ../nbs/api/04_export.ipynb
|
|
19
22
|
class ExportModuleProc:
|
|
20
23
|
"A processor which exports code to a module"
|
|
21
24
|
def begin(self): self.modules,self.in_all = defaultdict(L),defaultdict(L)
|
|
@@ -24,9 +27,13 @@ class ExportModuleProc:
|
|
|
24
27
|
def _export_(self, cell, exp_to=None):
|
|
25
28
|
self._exporti_(cell, exp_to)
|
|
26
29
|
self.in_all[ifnone(exp_to, '#')].append(cell)
|
|
30
|
+
def __call__(self, cell):
|
|
31
|
+
src = cell.source
|
|
32
|
+
if not src: return
|
|
33
|
+
if cell.cell_type=='markdown' and src.startswith('# '): self.modules['#'].append(cell)
|
|
27
34
|
_exports_=_export_
|
|
28
35
|
|
|
29
|
-
# %% ../nbs/api/04_export.ipynb
|
|
36
|
+
# %% ../nbs/api/04_export.ipynb
|
|
30
37
|
def black_format(cell, # Cell to format
|
|
31
38
|
force=False): # Turn black formatting on regardless of settings.ini
|
|
32
39
|
"Processor to format code with `black`"
|
|
@@ -40,7 +47,7 @@ def black_format(cell, # Cell to format
|
|
|
40
47
|
try: cell.source = _format_str(cell.source).strip()
|
|
41
48
|
except: pass
|
|
42
49
|
|
|
43
|
-
# %% ../nbs/api/04_export.ipynb
|
|
50
|
+
# %% ../nbs/api/04_export.ipynb
|
|
44
51
|
# includes the newline, because calling .strip() would affect all cells.
|
|
45
52
|
_magics_pattern = re.compile(r'^\s*(%%|%).*\n?', re.MULTILINE)
|
|
46
53
|
|
|
@@ -52,27 +59,35 @@ def scrub_magics(cell): # Cell to format
|
|
|
52
59
|
try: cell.source = _magics_pattern.sub('', cell.source)
|
|
53
60
|
except: pass
|
|
54
61
|
|
|
55
|
-
# %% ../nbs/api/04_export.ipynb
|
|
62
|
+
# %% ../nbs/api/04_export.ipynb
|
|
56
63
|
import nbdev.export
|
|
57
64
|
def optional_procs():
|
|
58
65
|
"An explicit list of processors that could be used by `nb_export`"
|
|
59
66
|
return L([p for p in nbdev.export.__all__
|
|
60
|
-
if p not in ["nb_export", "ExportModuleProc", "optional_procs"]])
|
|
67
|
+
if p not in ["nb_export", "nb_export_cli", "ExportModuleProc", "optional_procs"]])
|
|
61
68
|
|
|
62
|
-
# %% ../nbs/api/04_export.ipynb
|
|
63
|
-
def nb_export(nbname,
|
|
69
|
+
# %% ../nbs/api/04_export.ipynb
|
|
70
|
+
def nb_export(nbname:str, # Filename of notebook
|
|
71
|
+
lib_path:str=None, # Path to destination library. If not in a nbdev project, defaults to current directory.
|
|
72
|
+
procs=None, # Processors to use
|
|
73
|
+
name:str=None, # Name of python script {name}.py to create.
|
|
74
|
+
mod_maker=ModuleMaker,
|
|
75
|
+
debug:bool=False, # Debug mode
|
|
76
|
+
solo_nb:bool=False # Export single notebook outside of an nbdev project.
|
|
77
|
+
):
|
|
64
78
|
"Create module(s) from notebook"
|
|
65
|
-
if lib_path is None: lib_path = get_config().lib_path
|
|
79
|
+
if lib_path is None: lib_path = get_config().lib_path if is_nbdev() else '.'
|
|
66
80
|
exp = ExportModuleProc()
|
|
67
81
|
nb = NBProcessor(nbname, [exp]+L(procs), debug=debug)
|
|
68
82
|
nb.process()
|
|
69
83
|
for mod,cells in exp.modules.items():
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
if first(1 for o in cells if o.cell_type=='code'):
|
|
85
|
+
all_cells = exp.in_all[mod]
|
|
86
|
+
nm = ifnone(name, getattr(exp, 'default_exp', None) if mod=='#' else mod)
|
|
87
|
+
if not nm:
|
|
88
|
+
warn(f"Notebook '{nbname}' uses `#| export` without `#| default_exp` cell.\n"
|
|
89
|
+
"Note nbdev2 no longer supports nbdev1 syntax. Run `nbdev_migrate` to upgrade.\n"
|
|
90
|
+
"See https://nbdev.fast.ai/getting_started.html for more information.")
|
|
91
|
+
return
|
|
92
|
+
mm = mod_maker(dest=lib_path, name=nm, nb_path=nbname, is_new=bool(name) or mod=='#', solo_nb=solo_nb)
|
|
93
|
+
mm.make(cells, all_cells, lib_path=lib_path)
|
nbdev/frontmatter.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"""A YAML and formatted-markdown frontmatter processor"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/09_frontmatter.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% auto 0
|
|
4
6
|
__all__ = ['FrontmatterProc']
|
|
5
7
|
|
|
6
|
-
# %% ../nbs/api/09_frontmatter.ipynb
|
|
8
|
+
# %% ../nbs/api/09_frontmatter.ipynb
|
|
7
9
|
from .imports import *
|
|
8
10
|
from .process import *
|
|
9
11
|
from .doclinks import _nbpath2html
|
|
@@ -12,7 +14,7 @@ from execnb.nbio import *
|
|
|
12
14
|
from fastcore.imports import *
|
|
13
15
|
import yaml
|
|
14
16
|
|
|
15
|
-
# %% ../nbs/api/09_frontmatter.ipynb
|
|
17
|
+
# %% ../nbs/api/09_frontmatter.ipynb
|
|
16
18
|
_RE_FM_BASE=r'''^---\s*
|
|
17
19
|
(.*?\S+.*?)
|
|
18
20
|
---\s*'''
|
|
@@ -40,7 +42,7 @@ def _md2dict(s:str):
|
|
|
40
42
|
except Exception as e: warn(f'Failed to create YAML dict for:\n{r}\n\n{e}\n')
|
|
41
43
|
return res
|
|
42
44
|
|
|
43
|
-
# %% ../nbs/api/09_frontmatter.ipynb
|
|
45
|
+
# %% ../nbs/api/09_frontmatter.ipynb
|
|
44
46
|
def _dict2fm(d): return f'---\n{yaml.dump(d)}\n---\n\n'
|
|
45
47
|
def _insertfm(nb, fm): nb.cells.insert(0, mk_cell(_dict2fm(fm), 'raw'))
|
|
46
48
|
|
nbdev/maker.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Create one or more modules from selected notebook cells"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/02_maker.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% ../nbs/api/02_maker.ipynb 1
|
|
@@ -6,7 +8,7 @@ from __future__ import annotations
|
|
|
6
8
|
# %% auto 0
|
|
7
9
|
__all__ = ['find_var', 'read_var', 'update_var', 'ModuleMaker', 'decor_id', 'make_code_cells', 'relative_import', 'update_import']
|
|
8
10
|
|
|
9
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
11
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
10
12
|
from .config import *
|
|
11
13
|
from .imports import *
|
|
12
14
|
|
|
@@ -21,7 +23,7 @@ from collections import defaultdict
|
|
|
21
23
|
from pprint import pformat
|
|
22
24
|
from textwrap import TextWrapper
|
|
23
25
|
|
|
24
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
26
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
25
27
|
def find_var(lines, varname):
|
|
26
28
|
"Find the line numbers where `varname` is defined in `lines`"
|
|
27
29
|
start = first(i for i,o in enumerate(lines) if o.startswith(varname))
|
|
@@ -31,7 +33,7 @@ def find_var(lines, varname):
|
|
|
31
33
|
end = first(i for i,o in enumerate(lines[start+1:]) if o[:1] not in empty)
|
|
32
34
|
return start,len(lines) if end is None else (end+start+1)
|
|
33
35
|
|
|
34
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
36
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
35
37
|
def read_var(code, varname):
|
|
36
38
|
"Eval and return the value of `varname` defined in `code`"
|
|
37
39
|
lines = code.splitlines()
|
|
@@ -42,7 +44,7 @@ def read_var(code, varname):
|
|
|
42
44
|
try: return eval('\n'.join(res))
|
|
43
45
|
except SyntaxError: raise Exception('\n'.join(res)) from None
|
|
44
46
|
|
|
45
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
47
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
46
48
|
def update_var(varname, func, fn=None, code=None):
|
|
47
49
|
"Update the definition of `varname` in file `fn`, by calling `func` with the current definition"
|
|
48
50
|
if fn:
|
|
@@ -58,10 +60,10 @@ def update_var(varname, func, fn=None, code=None):
|
|
|
58
60
|
if fn: fn.write_text(code)
|
|
59
61
|
else: return code
|
|
60
62
|
|
|
61
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
63
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
62
64
|
class ModuleMaker:
|
|
63
65
|
"Helper class to create exported library from notebook source cells"
|
|
64
|
-
def __init__(self, dest, name, nb_path, is_new=True, parse=True):
|
|
66
|
+
def __init__(self, dest, name, nb_path, is_new=True, parse=True, solo_nb=False):
|
|
65
67
|
dest,nb_path = Path(dest),Path(nb_path)
|
|
66
68
|
store_attr()
|
|
67
69
|
self.fname = dest/(name.replace('.','/') + ".py")
|
|
@@ -70,12 +72,12 @@ class ModuleMaker:
|
|
|
70
72
|
self.dest2nb = nb_path.relpath(self.fname.parent).as_posix()
|
|
71
73
|
self.hdr = f"# %% {self.dest2nb}"
|
|
72
74
|
|
|
73
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
75
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
74
76
|
def decor_id(d):
|
|
75
77
|
"`id` attr of decorator, regardless of whether called as function or bare"
|
|
76
78
|
return d.id if hasattr(d, 'id') else nested_attr(d, 'func.id', '')
|
|
77
79
|
|
|
78
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
80
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
79
81
|
_def_types = ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef
|
|
80
82
|
_assign_types = ast.AnnAssign, ast.Assign, ast.AugAssign
|
|
81
83
|
|
|
@@ -86,7 +88,7 @@ def _all_targets(a): return L(getattr(a,'elts',a))
|
|
|
86
88
|
def _filt_dec(x): return decor_id(x).startswith('patch')
|
|
87
89
|
def _wants(o): return isinstance(o,_def_types) and not any(L(o.decorator_list).filter(_filt_dec))
|
|
88
90
|
|
|
89
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
91
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
90
92
|
def _targets(o): return [o.target] if isinstance(o, (ast.AugAssign,ast.AnnAssign)) else o.targets
|
|
91
93
|
|
|
92
94
|
@patch
|
|
@@ -105,10 +107,10 @@ def make_all(self:ModuleMaker, cells):
|
|
|
105
107
|
exports = (assign_targs.attrgot('id')+syms).filter(lambda o: o and o[0]!='_')
|
|
106
108
|
return (exports+all_vals).unique()
|
|
107
109
|
|
|
108
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
110
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
109
111
|
def make_code_cells(*ss): return dict2nb({'cells':L(ss).map(mk_cell)}).cells
|
|
110
112
|
|
|
111
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
113
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
112
114
|
def relative_import(name, fname, level=0):
|
|
113
115
|
"Convert a module `name` to a name relative to `fname`"
|
|
114
116
|
assert not level
|
|
@@ -120,7 +122,7 @@ def relative_import(name, fname, level=0):
|
|
|
120
122
|
if not all(o=='.' for o in res): res='.'+res
|
|
121
123
|
return res.replace(os.path.sep, ".")
|
|
122
124
|
|
|
123
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
125
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
124
126
|
# Based on https://github.com/thonny/thonny/blob/master/thonny/ast_utils.py
|
|
125
127
|
def _mark_text_ranges(
|
|
126
128
|
source: str|bytes, # Source code to add ranges to
|
|
@@ -138,7 +140,7 @@ def _mark_text_ranges(
|
|
|
138
140
|
child.end_lineno, child.end_col_offset = child.lineno, child.col_offset+2
|
|
139
141
|
return root.body
|
|
140
142
|
|
|
141
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
143
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
142
144
|
def update_import(source, tree, libname, f=relative_import):
|
|
143
145
|
if not tree: return
|
|
144
146
|
if sys.version_info < (3,8): tree = _mark_text_ranges(source)
|
|
@@ -158,7 +160,7 @@ def import2relative(cell:NbCell, libname):
|
|
|
158
160
|
src = update_import(cell.source, cell.parsed_(), libname)
|
|
159
161
|
if src: cell.set_source(src)
|
|
160
162
|
|
|
161
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
163
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
162
164
|
@patch
|
|
163
165
|
def _last_future(self:ModuleMaker, cells):
|
|
164
166
|
"Returns the location of a `__future__` in `cells`"
|
|
@@ -167,23 +169,23 @@ def _last_future(self:ModuleMaker, cells):
|
|
|
167
169
|
isinstance(t,ast.ImportFrom) and t.module=='__future__' for t in tree))+1
|
|
168
170
|
except ValueError: return 0
|
|
169
171
|
|
|
170
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
171
|
-
def _import2relative(cells,
|
|
172
|
-
"Converts `cells` to use `import2relative` based on `
|
|
173
|
-
if
|
|
174
|
-
for cell in cells: cell.import2relative(
|
|
172
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
173
|
+
def _import2relative(cells, lib_path=None):
|
|
174
|
+
"Converts `cells` to use `import2relative` based on `lib_path`"
|
|
175
|
+
if lib_path is None: lib_path = get_config().lib_path
|
|
176
|
+
for cell in cells: cell.import2relative(lib_path)
|
|
175
177
|
|
|
176
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
178
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
177
179
|
def _retr_mdoc(cells):
|
|
178
|
-
"Search for
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return ""
|
|
185
|
-
|
|
186
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
180
|
+
"Search for md meta quote line, used to create module docstring"
|
|
181
|
+
md1 = first(o for o in cells if o.cell_type=='markdown' and o.source.startswith('# '))
|
|
182
|
+
if not md1: return ''
|
|
183
|
+
summ = first(o for o in md1.source.splitlines() if o.startswith('> '))
|
|
184
|
+
if not summ: return ''
|
|
185
|
+
summ = summ.lstrip('> ').strip()
|
|
186
|
+
return f'"""{summ}"""\n\n' if summ else ''
|
|
187
|
+
|
|
188
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
187
189
|
@patch
|
|
188
190
|
def make(self:ModuleMaker, cells, all_cells=None, lib_path=None):
|
|
189
191
|
"Write module containing `cells` with `__all__` generated from `all_cells`"
|
|
@@ -206,11 +208,11 @@ def make(self:ModuleMaker, cells, all_cells=None, lib_path=None):
|
|
|
206
208
|
f.write(_retr_mdoc(cells))
|
|
207
209
|
f.write(f"# AUTOGENERATED! DO NOT EDIT! File to edit: {self.dest2nb}.")
|
|
208
210
|
if last_future > 0: write_cells(cells[:last_future], self.hdr, f)
|
|
209
|
-
if self.parse: f.write(f"\n\n# %% auto 0\n__all__ = {all_str}")
|
|
210
|
-
write_cells(cells[last_future:], self.hdr, f)
|
|
211
|
+
if self.parse and not self.solo_nb: f.write(f"\n\n# %% auto 0\n__all__ = {all_str}")
|
|
212
|
+
write_cells(cells[last_future:], self.hdr, f, cell_number=get_config().cell_number, solo_nb=self.solo_nb)
|
|
211
213
|
f.write('\n')
|
|
212
214
|
|
|
213
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
215
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
214
216
|
@patch
|
|
215
217
|
def _update_all(self:ModuleMaker, all_cells, alls):
|
|
216
218
|
return pformat(alls + self.make_all(all_cells), width=160)
|
|
@@ -222,7 +224,7 @@ def _make_exists(self:ModuleMaker, cells, all_cells=None):
|
|
|
222
224
|
update_var('__all__', partial(self._update_all, all_cells), fn=self.fname)
|
|
223
225
|
with self.fname.open('a', encoding="utf-8") as f: write_cells(cells, self.hdr, f)
|
|
224
226
|
|
|
225
|
-
# %% ../nbs/api/02_maker.ipynb
|
|
227
|
+
# %% ../nbs/api/02_maker.ipynb
|
|
226
228
|
def _basic_export_nb2(fname, name, dest=None):
|
|
227
229
|
"A basic exporter to bootstrap nbdev using `ModuleMaker`"
|
|
228
230
|
if dest is None: dest = get_config().lib_path
|
nbdev/merge.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"""Fix merge conflicts in jupyter notebooks"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/07_merge.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% auto 0
|
|
4
6
|
__all__ = ['conf_re', 'unpatch', 'nbdev_fix', 'nbdev_merge']
|
|
5
7
|
|
|
6
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
8
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
7
9
|
from .imports import *
|
|
8
10
|
from .config import *
|
|
9
11
|
from .export import *
|
|
@@ -16,7 +18,7 @@ from fastcore import shutil
|
|
|
16
18
|
import subprocess
|
|
17
19
|
from difflib import SequenceMatcher
|
|
18
20
|
|
|
19
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
21
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
20
22
|
_BEG,_MID,_END = '<'*7,'='*7,'>'*7
|
|
21
23
|
conf_re = re.compile(rf'^{_BEG}\s+(\S+)\n(.*?)^{_MID}\n(.*?)^{_END}\s+([\S ]+)\n', re.MULTILINE|re.DOTALL)
|
|
22
24
|
|
|
@@ -26,7 +28,7 @@ def _unpatch_f(before, cb1, cb2, c, r):
|
|
|
26
28
|
r.append(c)
|
|
27
29
|
return cb2
|
|
28
30
|
|
|
29
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
31
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
30
32
|
def unpatch(s:str):
|
|
31
33
|
"Takes a string with conflict markers and returns the two original files, and their branch names"
|
|
32
34
|
*main,last = conf_re.split(s)
|
|
@@ -36,7 +38,7 @@ def unpatch(s:str):
|
|
|
36
38
|
c2b = _unpatch_f(before, c2b, c2_branch, c2, r2)
|
|
37
39
|
return ''.join(r1+[last]), ''.join(r2+[last]), c1b, c2b
|
|
38
40
|
|
|
39
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
41
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
40
42
|
def _make_md(code): return [dict(source=f'`{code}`', cell_type="markdown", metadata={})]
|
|
41
43
|
def _make_conflict(a,b, branch1, branch2):
|
|
42
44
|
return _make_md(f'{_BEG} {branch1}') + a+_make_md(_MID)+b + _make_md(f'{_END} {branch2}')
|
|
@@ -53,7 +55,7 @@ def _merge_cells(a, b, brancha, branchb, theirs):
|
|
|
53
55
|
prev_sa,prev_sb = sa+sz,sb+sz
|
|
54
56
|
return res,conflict
|
|
55
57
|
|
|
56
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
58
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
57
59
|
@call_parse
|
|
58
60
|
def nbdev_fix(nbname:str, # Notebook filename to fix
|
|
59
61
|
outname:str=None, # Filename of output notebook (defaults to `nbname`)
|
|
@@ -75,12 +77,12 @@ def nbdev_fix(nbname:str, # Notebook filename to fix
|
|
|
75
77
|
else: print("Successfully merged conflicts!")
|
|
76
78
|
return conflict
|
|
77
79
|
|
|
78
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
80
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
79
81
|
def _git_branch_merge():
|
|
80
82
|
try: return only(v for k,v in os.environ.items() if k.startswith('GITHEAD'))
|
|
81
83
|
except ValueError: return
|
|
82
84
|
|
|
83
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
85
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
84
86
|
def _git_rebase_head():
|
|
85
87
|
for d in ('apply','merge'):
|
|
86
88
|
d = Path(f'.git/rebase-{d}')
|
|
@@ -89,14 +91,14 @@ def _git_rebase_head():
|
|
|
89
91
|
msg = run(f'git show-branch --no-name {cmt}')
|
|
90
92
|
return f'{cmt[:7]} ({msg})'
|
|
91
93
|
|
|
92
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
94
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
93
95
|
def _git_merge_file(base, ours, theirs):
|
|
94
96
|
"`git merge-file` with expected labels depending on if a `merge` or `rebase` is in-progress"
|
|
95
97
|
l_theirs = _git_rebase_head() or _git_branch_merge() or 'THEIRS'
|
|
96
98
|
cmd = f"git merge-file -L HEAD -L BASE -L '{l_theirs}' {ours} {base} {theirs}"
|
|
97
99
|
return subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
98
100
|
|
|
99
|
-
# %% ../nbs/api/07_merge.ipynb
|
|
101
|
+
# %% ../nbs/api/07_merge.ipynb
|
|
100
102
|
@call_parse
|
|
101
103
|
def nbdev_merge(base:str, ours:str, theirs:str, path:str):
|
|
102
104
|
"Git merge driver for notebooks"
|
nbdev/migrate.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"""Utilities for migrating to nbdev"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/16_migrate.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% auto 0
|
|
4
6
|
__all__ = ['MigrateProc', 'fp_md_fm', 'migrate_nb', 'migrate_md', 'nbdev_migrate']
|
|
5
7
|
|
|
6
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
8
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
7
9
|
from .process import *
|
|
8
10
|
from .frontmatter import *
|
|
9
11
|
from .frontmatter import _fm2dict, _re_fm_md, _dict2fm, _insertfm
|
|
@@ -14,20 +16,20 @@ from .showdoc import show_doc
|
|
|
14
16
|
from fastcore.all import *
|
|
15
17
|
import shutil
|
|
16
18
|
|
|
17
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
19
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
18
20
|
def _cat_slug(fmdict):
|
|
19
21
|
"Get the partial slug from the category front matter."
|
|
20
22
|
slug = '/'.join(fmdict.get('categories', ''))
|
|
21
23
|
return '/' + slug if slug else ''
|
|
22
24
|
|
|
23
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
25
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
24
26
|
def _file_slug(fname):
|
|
25
27
|
"Get the partial slug from the filename."
|
|
26
28
|
p = Path(fname)
|
|
27
29
|
dt = '/'+p.name[:10].replace('-', '/')+'/'
|
|
28
30
|
return dt + p.stem[11:]
|
|
29
31
|
|
|
30
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
32
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
31
33
|
def _replace_fm(d:dict, # dictionary you wish to conditionally change
|
|
32
34
|
k:str, # key to check
|
|
33
35
|
val:str,# value to check if d[k] == v
|
|
@@ -45,14 +47,14 @@ def _fp_fm(d):
|
|
|
45
47
|
d = _replace_fm(d, 'hide', 'true', {'draft': 'true'})
|
|
46
48
|
return d
|
|
47
49
|
|
|
48
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
50
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
49
51
|
def _fp_image(d):
|
|
50
52
|
"Correct path of fastpages images to reference the local directory."
|
|
51
53
|
prefix = 'images/copied_from_nb/'
|
|
52
54
|
if d.get('image', '').startswith(prefix): d['image'] = d['image'].replace(prefix, '')
|
|
53
55
|
return d
|
|
54
56
|
|
|
55
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
57
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
56
58
|
def _rm_quote(s):
|
|
57
59
|
title = re.search('''"(.*?)"''', s)
|
|
58
60
|
return title.group(1) if title else s
|
|
@@ -79,7 +81,7 @@ def _fp_convert(fm:dict, path:Path):
|
|
|
79
81
|
if fm.get('comments'): fm.pop('comments') #true by itself is not a valid value for comments https://quarto.org/docs/output-formats/html-basics.html#commenting, and the default is true
|
|
80
82
|
return fm
|
|
81
83
|
|
|
82
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
84
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
83
85
|
class MigrateProc(Processor):
|
|
84
86
|
"Migrate fastpages front matter in notebooks to a raw cell."
|
|
85
87
|
def begin(self):
|
|
@@ -87,7 +89,7 @@ class MigrateProc(Processor):
|
|
|
87
89
|
if getattr(first(self.nb.cells), 'cell_type', None) == 'raw': del self.nb.cells[0]
|
|
88
90
|
_insertfm(self.nb, self.nb.frontmatter_)
|
|
89
91
|
|
|
90
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
92
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
91
93
|
def fp_md_fm(path):
|
|
92
94
|
"Make fastpages front matter in markdown files quarto compliant."
|
|
93
95
|
p = Path(path)
|
|
@@ -98,25 +100,25 @@ def fp_md_fm(path):
|
|
|
98
100
|
return _re_fm_md.sub(_dict2fm(fm), md)
|
|
99
101
|
else: return md
|
|
100
102
|
|
|
101
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
103
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
102
104
|
_alias = merge({k:'code-fold: true' for k in ['collapse', 'collapse_input', 'collapse_hide']},
|
|
103
105
|
{'collapse_show':'code-fold: show', 'hide_input': 'echo: false', 'hide': 'include: false', 'hide_output': 'output: false'})
|
|
104
106
|
def _subv1(s): return _alias.get(s, s)
|
|
105
107
|
|
|
106
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
108
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
107
109
|
def _re_v1():
|
|
108
110
|
d = ['default_exp', 'export', 'exports', 'exporti', 'hide', 'hide_input', 'collapse_show', 'collapse',
|
|
109
111
|
'collapse_hide', 'collapse_input', 'hide_output', 'default_cls_lvl']
|
|
110
112
|
d += L(get_config().tst_flags).filter()
|
|
111
113
|
d += [s.replace('_', '-') for s in d] # allow for hyphenated version of old directives
|
|
112
114
|
_tmp = '|'.join(list(set(d)))
|
|
113
|
-
return re.compile(
|
|
115
|
+
return re.compile(fr"^[ \f\v\t]*?(#)\s*({_tmp})(?!\S)", re.MULTILINE)
|
|
114
116
|
|
|
115
117
|
def _repl_directives(code_str):
|
|
116
118
|
def _fmt(x): return f"#| {_subv1(x[2].replace('-', '_').strip())}"
|
|
117
119
|
return _re_v1().sub(_fmt, code_str)
|
|
118
120
|
|
|
119
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
121
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
120
122
|
def _repl_v1dir(cell):
|
|
121
123
|
"Replace nbdev v1 with v2 directives."
|
|
122
124
|
if cell.get('source') and cell.get('cell_type') == 'code':
|
|
@@ -126,21 +128,21 @@ def _repl_v1dir(cell):
|
|
|
126
128
|
if not ss: pass
|
|
127
129
|
else: cell['source'] = '\n'.join([_repl_directives(c) for c in ss[:first_code]] + ss[first_code:])
|
|
128
130
|
|
|
129
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
131
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
130
132
|
_re_callout = re.compile(r'^>\s(Warning|Note|Important|Tip):(.*)', flags=re.MULTILINE)
|
|
131
133
|
def _co(x): return ":::{.callout-"+x[1].lower()+"}\n\n" + f"{x[2].strip()}\n\n" + ":::"
|
|
132
134
|
def _convert_callout(s):
|
|
133
135
|
"Convert nbdev v1 to v2 callouts."
|
|
134
136
|
return _re_callout.sub(_co, s)
|
|
135
137
|
|
|
136
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
138
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
137
139
|
_re_video = re.compile(r'^>\syoutube:(.*)', flags=re.MULTILINE)
|
|
138
140
|
def _v(x): return "{{< " + f"video {x[1].strip()}" + " >}}"
|
|
139
141
|
def _convert_video(s):
|
|
140
142
|
"Replace nbdev v1 with v2 video embeds."
|
|
141
143
|
return _re_video.sub(_v, s)
|
|
142
144
|
|
|
143
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
145
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
144
146
|
_shortcuts = compose(_convert_video, _convert_callout)
|
|
145
147
|
|
|
146
148
|
def _repl_v1shortcuts(cell):
|
|
@@ -148,7 +150,7 @@ def _repl_v1shortcuts(cell):
|
|
|
148
150
|
if cell.get('source') and cell.get('cell_type') == 'markdown':
|
|
149
151
|
cell['source'] = _shortcuts(cell['source'])
|
|
150
152
|
|
|
151
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
153
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
152
154
|
def migrate_nb(path, overwrite=True):
|
|
153
155
|
"Migrate Notebooks from nbdev v1 and fastpages."
|
|
154
156
|
nbp = NBProcessor(path, procs=[FrontmatterProc, MigrateProc, _repl_v1shortcuts, _repl_v1dir], rm_directives=False)
|
|
@@ -156,14 +158,14 @@ def migrate_nb(path, overwrite=True):
|
|
|
156
158
|
if overwrite: write_nb(nbp.nb, path)
|
|
157
159
|
return nbp.nb
|
|
158
160
|
|
|
159
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
161
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
160
162
|
def migrate_md(path, overwrite=True):
|
|
161
163
|
"Migrate Markdown Files from fastpages."
|
|
162
164
|
txt = fp_md_fm(path)
|
|
163
165
|
if overwrite: path.write_text(txt)
|
|
164
166
|
return txt
|
|
165
167
|
|
|
166
|
-
# %% ../nbs/api/16_migrate.ipynb
|
|
168
|
+
# %% ../nbs/api/16_migrate.ipynb
|
|
167
169
|
@call_parse
|
|
168
170
|
def nbdev_migrate(
|
|
169
171
|
path:str=None, # A path or glob containing notebooks and markdown files to migrate
|
nbdev/process.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"""A notebook processor"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/03_process.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% auto 0
|
|
4
6
|
__all__ = ['langs', 'nb_lang', 'first_code_ln', 'extract_directives', 'opt_set', 'instantiate', 'NBProcessor', 'Processor']
|
|
5
7
|
|
|
6
|
-
# %% ../nbs/api/03_process.ipynb
|
|
8
|
+
# %% ../nbs/api/03_process.ipynb
|
|
7
9
|
from .config import *
|
|
8
10
|
from .maker import *
|
|
9
11
|
from .imports import *
|
|
@@ -14,7 +16,7 @@ from fastcore.imports import *
|
|
|
14
16
|
|
|
15
17
|
from collections import defaultdict
|
|
16
18
|
|
|
17
|
-
# %% ../nbs/api/03_process.ipynb
|
|
19
|
+
# %% ../nbs/api/03_process.ipynb
|
|
18
20
|
# from https://github.com/quarto-dev/quarto-cli/blob/main/src/resources/jupyter/notebook.py
|
|
19
21
|
langs = defaultdict(
|
|
20
22
|
lambda: '#', r = "#", python = "#", julia = "#", scala = "//", matlab = "%", csharp = "//", fsharp = "//",
|
|
@@ -23,30 +25,30 @@ langs = defaultdict(
|
|
|
23
25
|
java = "//", groovy = "//", sed = "#", perl = "#", ruby = "#", tikz = "%", javascript = "//", js = "//", d3 = "//", node = "//",
|
|
24
26
|
sass = "//", coffee = "#", go = "//", asy = "//", haskell = "--", dot = "//", apl = "⍝")
|
|
25
27
|
|
|
26
|
-
# %% ../nbs/api/03_process.ipynb
|
|
28
|
+
# %% ../nbs/api/03_process.ipynb
|
|
27
29
|
def nb_lang(nb): return nested_attr(nb, 'metadata.kernelspec.language', 'python')
|
|
28
30
|
|
|
29
|
-
# %% ../nbs/api/03_process.ipynb
|
|
31
|
+
# %% ../nbs/api/03_process.ipynb
|
|
30
32
|
def _dir_pre(lang=None): return fr"\s*{langs[lang]}\s*\|"
|
|
31
33
|
def _quarto_re(lang=None): return re.compile(_dir_pre(lang) + r'\s*[\w|-]+\s*:')
|
|
32
34
|
|
|
33
|
-
# %% ../nbs/api/03_process.ipynb
|
|
35
|
+
# %% ../nbs/api/03_process.ipynb
|
|
34
36
|
def _directive(s, lang='python'):
|
|
35
37
|
s = re.sub('^'+_dir_pre(lang), f"{langs[lang]}|", s)
|
|
36
|
-
if s.strip().endswith(':'): s = s.replace(':', '') # You can append colon at the end to be Quarto compliant. Ex: #|hide:
|
|
38
|
+
if s.strip().endswith(':'): s = s.replace(':', '') # You can append colon at the end to be Quarto compliant. Ex: #| hide:
|
|
37
39
|
if ':' in s: s = s.replace(':', ': ')
|
|
38
40
|
s = (s.strip()[2:]).strip().split()
|
|
39
41
|
if not s: return None
|
|
40
42
|
direc,*args = s
|
|
41
43
|
return direc,args
|
|
42
44
|
|
|
43
|
-
# %% ../nbs/api/03_process.ipynb
|
|
45
|
+
# %% ../nbs/api/03_process.ipynb
|
|
44
46
|
def _norm_quarto(s, lang='python'):
|
|
45
47
|
"normalize quarto directives so they have a space after the colon"
|
|
46
48
|
m = _quarto_re(lang).match(s)
|
|
47
49
|
return m.group(0) + ' ' + _quarto_re(lang).sub('', s).lstrip() if m else s
|
|
48
50
|
|
|
49
|
-
# %% ../nbs/api/03_process.ipynb
|
|
51
|
+
# %% ../nbs/api/03_process.ipynb
|
|
50
52
|
_cell_mgc = re.compile(r"^\s*%%\w+")
|
|
51
53
|
|
|
52
54
|
def first_code_ln(code_list, re_pattern=None, lang='python'):
|
|
@@ -54,14 +56,14 @@ def first_code_ln(code_list, re_pattern=None, lang='python'):
|
|
|
54
56
|
if re_pattern is None: re_pattern = _dir_pre(lang)
|
|
55
57
|
return first(i for i,o in enumerate(code_list) if o.strip() != '' and not re.match(re_pattern, o) and not _cell_mgc.match(o))
|
|
56
58
|
|
|
57
|
-
# %% ../nbs/api/03_process.ipynb
|
|
59
|
+
# %% ../nbs/api/03_process.ipynb
|
|
58
60
|
def _partition_cell(cell, lang):
|
|
59
61
|
if not cell.source: return [],[]
|
|
60
62
|
lines = cell.source.splitlines(True)
|
|
61
63
|
first_code = first_code_ln(lines, lang=lang)
|
|
62
64
|
return lines[:first_code],lines[first_code:]
|
|
63
65
|
|
|
64
|
-
# %% ../nbs/api/03_process.ipynb
|
|
66
|
+
# %% ../nbs/api/03_process.ipynb
|
|
65
67
|
def extract_directives(cell, remove=True, lang='python'):
|
|
66
68
|
"Take leading comment directives from lines of code in `ss`, remove `#|`, and split"
|
|
67
69
|
dirs,code = _partition_cell(cell, lang)
|
|
@@ -71,22 +73,22 @@ def extract_directives(cell, remove=True, lang='python'):
|
|
|
71
73
|
cell['source'] = ''.join([_norm_quarto(o, lang) for o in dirs if _quarto_re(lang).match(o) or _cell_mgc.match(o)] + code)
|
|
72
74
|
return dict(L(_directive(s, lang) for s in dirs).filter())
|
|
73
75
|
|
|
74
|
-
# %% ../nbs/api/03_process.ipynb
|
|
76
|
+
# %% ../nbs/api/03_process.ipynb
|
|
75
77
|
def opt_set(var, newval):
|
|
76
78
|
"newval if newval else var"
|
|
77
79
|
return newval if newval else var
|
|
78
80
|
|
|
79
|
-
# %% ../nbs/api/03_process.ipynb
|
|
81
|
+
# %% ../nbs/api/03_process.ipynb
|
|
80
82
|
def instantiate(x, **kwargs):
|
|
81
83
|
"Instantiate `x` if it's a type"
|
|
82
84
|
return x(**kwargs) if isinstance(x,type) else x
|
|
83
85
|
|
|
84
86
|
def _mk_procs(procs, nb): return L(procs).map(instantiate, nb=nb)
|
|
85
87
|
|
|
86
|
-
# %% ../nbs/api/03_process.ipynb
|
|
88
|
+
# %% ../nbs/api/03_process.ipynb
|
|
87
89
|
def _is_direc(f): return getattr(f, '__name__', '-')[-1]=='_'
|
|
88
90
|
|
|
89
|
-
# %% ../nbs/api/03_process.ipynb
|
|
91
|
+
# %% ../nbs/api/03_process.ipynb
|
|
90
92
|
class NBProcessor:
|
|
91
93
|
"Process cells and nbdev comments in a notebook"
|
|
92
94
|
def __init__(self, path=None, procs=None, nb=None, debug=False, rm_directives=True, process=False):
|
|
@@ -126,7 +128,7 @@ class NBProcessor:
|
|
|
126
128
|
"Process all cells with all processors"
|
|
127
129
|
for proc in self.procs: self._proc(proc)
|
|
128
130
|
|
|
129
|
-
# %% ../nbs/api/03_process.ipynb
|
|
131
|
+
# %% ../nbs/api/03_process.ipynb
|
|
130
132
|
class Processor:
|
|
131
133
|
"Base class for processors"
|
|
132
134
|
def __init__(self, nb): self.nb = nb
|