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/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 2
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 4
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 8
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 10
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 13
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 16
63
- def nb_export(nbname, lib_path=None, procs=None, debug=False, mod_maker=ModuleMaker, name=None):
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
- all_cells = exp.in_all[mod]
71
- nm = ifnone(name, getattr(exp, 'default_exp', None) if mod=='#' else mod)
72
- if not nm:
73
- warn(f"Notebook '{nbname}' uses `#|export` without `#|default_exp` cell.\n"
74
- "Note nbdev2 no longer supports nbdev1 syntax. Run `nbdev_migrate` to upgrade.\n"
75
- "See https://nbdev.fast.ai/getting_started.html for more information.")
76
- return
77
- mm = mod_maker(dest=lib_path, name=nm, nb_path=nbname, is_new=bool(name) or mod=='#')
78
- mm.make(cells, all_cells, lib_path=lib_path)
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 2
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 5
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 6
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 3
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 8
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 10
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 12
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 15
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 18
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 19
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 20
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 21
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 24
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 26
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 27
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 29
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 30
171
- def _import2relative(cells, lib_name=None):
172
- "Converts `cells` to use `import2relative` based on `lib_name`"
173
- if lib_name is None: lib_name = get_config().lib_name
174
- for cell in cells: cell.import2relative(lib_name)
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 31
178
+ # %% ../nbs/api/02_maker.ipynb
177
179
  def _retr_mdoc(cells):
178
- "Search for `_doc_` variable, used to create module docstring"
179
- trees = L(cells).map(NbCell.parsed_).concat()
180
- for o in trees:
181
- if isinstance(o, _assign_types) and getattr(_targets(o)[0],'id',None)=='_doc_':
182
- v = try_attrs(o.value, 'value', 's') # py37 uses `ast.Str.s`
183
- return f'"""{v}"""\n\n'
184
- return ""
185
-
186
- # %% ../nbs/api/02_maker.ipynb 33
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 38
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 44
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 2
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 16
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 17
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 22
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 23
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 27
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 28
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 29
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 30
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 2
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 5
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 7
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 9
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 10
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 11
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 14
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 21
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 30
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 31
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(f"^[ \f\v\t]*?(#)\s*({_tmp})(?!\S)", re.MULTILINE)
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 33
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 38
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 45
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 49
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 50
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 51
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 52
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 2
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 6
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 7
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 9
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 11
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 12
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 14
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 17
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 18
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 21
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 22
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 23
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 24
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 34
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