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/processors.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
"""Some processors for NBProcessor"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/10_processors.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% auto 0
|
|
4
6
|
__all__ = ['populate_language', 'insert_warning', 'cell_lang', 'add_show_docs', 'fdiv', 'boxify', 'mv_exports', 'add_links',
|
|
5
|
-
'add_fold', 'strip_ansi', 'strip_hidden_metadata', 'hide_', 'hide_line', 'filter_stream_', '
|
|
6
|
-
'rm_header_dash', 'rm_export', 'clean_show_doc', 'exec_show_docs', 'FilterDefaults']
|
|
7
|
+
'add_fold', 'strip_ansi', 'strip_hidden_metadata', 'hide_', 'hide_line', 'filter_stream_', 'ai_magics',
|
|
8
|
+
'clean_magics', 'rm_header_dash', 'rm_export', 'clean_show_doc', 'exec_show_docs', 'FilterDefaults']
|
|
7
9
|
|
|
8
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
10
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
9
11
|
import ast
|
|
10
12
|
import importlib
|
|
11
13
|
|
|
@@ -23,7 +25,7 @@ from fastcore.imports import *
|
|
|
23
25
|
from fastcore.xtras import *
|
|
24
26
|
import sys,yaml
|
|
25
27
|
|
|
26
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
28
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
27
29
|
_langs = 'bash|html|javascript|js|latex|markdown|perl|ruby|sh|svg'
|
|
28
30
|
_lang_pattern = re.compile(rf'^\s*%%\s*({_langs})\s*$', flags=re.MULTILINE)
|
|
29
31
|
|
|
@@ -36,13 +38,13 @@ class populate_language(Processor):
|
|
|
36
38
|
if lang: cell.metadata.language = lang[0]
|
|
37
39
|
else: cell.metadata.language = self.language
|
|
38
40
|
|
|
39
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
41
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
40
42
|
class insert_warning(Processor):
|
|
41
43
|
"Insert Autogenerated Warning Into Notebook after the first cell."
|
|
42
44
|
content = "<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->"
|
|
43
45
|
def begin(self): self.nb.cells.insert(1, mk_cell(self.content, 'markdown'))
|
|
44
46
|
|
|
45
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
47
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
46
48
|
_def_types = (ast.FunctionDef,ast.AsyncFunctionDef,ast.ClassDef)
|
|
47
49
|
def _def_names(cell, shown):
|
|
48
50
|
cellp = cell.parsed_()
|
|
@@ -55,7 +57,7 @@ def _get_nm(tree):
|
|
|
55
57
|
else: val = try_attrs(i.value, 'id', 'func', 'attr')
|
|
56
58
|
return f'{val}.{i.attr}' if isinstance(i, ast.Attribute) else i.id
|
|
57
59
|
|
|
58
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
60
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
59
61
|
def _show_docs(trees):
|
|
60
62
|
return [t for t in trees if isinstance(t,ast.Expr) and nested_attr(t, 'value.func.id')=='show_doc']
|
|
61
63
|
|
|
@@ -82,20 +84,20 @@ class add_show_docs(Processor):
|
|
|
82
84
|
nb.cells.insert(cell.idx_+1, new_cell)
|
|
83
85
|
nb.has_docs_ = shown_docs or exports
|
|
84
86
|
|
|
85
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
87
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
86
88
|
def fdiv(attrs=''):
|
|
87
89
|
"Create a fenced div markdown cell in quarto"
|
|
88
90
|
if attrs: attrs = ' {'+attrs+'}'
|
|
89
91
|
return mk_cell(':::'+attrs, cell_type='markdown')
|
|
90
92
|
|
|
91
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
93
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
92
94
|
def boxify(cells):
|
|
93
95
|
"Add a box around `cells`"
|
|
94
96
|
if not isinstance(cells, list): cells = [cells]
|
|
95
97
|
res = [fdiv('.py-2 .px-3 .mb-4 fig-align="center" .border .rounded .shadow-sm')]
|
|
96
98
|
return res+cells+[fdiv()]
|
|
97
99
|
|
|
98
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
100
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
99
101
|
class mv_exports(Processor):
|
|
100
102
|
"Move `exports` cells to after the `show_doc`"
|
|
101
103
|
def begin(self):
|
|
@@ -108,7 +110,7 @@ class mv_exports(Processor):
|
|
|
108
110
|
srccell = cells.pop(idx)
|
|
109
111
|
cells[idx:idx] = [doccell,srccell]
|
|
110
112
|
|
|
111
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
113
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
112
114
|
_re_defaultexp = re.compile(r'^\s*#\|\s*default_exp\s+(\S+)', flags=re.MULTILINE)
|
|
113
115
|
|
|
114
116
|
def _default_exp(nb):
|
|
@@ -117,7 +119,7 @@ def _default_exp(nb):
|
|
|
117
119
|
default_exp = first(code_src.filter().map(_re_defaultexp.search).filter())
|
|
118
120
|
return default_exp.group(1) if default_exp else None
|
|
119
121
|
|
|
120
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
122
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
121
123
|
def add_links(cell):
|
|
122
124
|
"Add links to markdown cells"
|
|
123
125
|
nl = NbdevLookup()
|
|
@@ -126,13 +128,13 @@ def add_links(cell):
|
|
|
126
128
|
if hasattr(o, 'data') and hasattr(o['data'], 'text/markdown'):
|
|
127
129
|
o.data['text/markdown'] = [nl.link_line(s) for s in o.data['text/markdown']]
|
|
128
130
|
|
|
129
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
131
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
130
132
|
def add_fold(cell):
|
|
131
133
|
"Add `code-fold` to `exports` cells"
|
|
132
134
|
if cell.cell_type != 'code' or 'exports' not in cell.directives_: return
|
|
133
135
|
cell.source = f'#| code-fold: show\n#| code-summary: "Exported source"\n{cell.source}'
|
|
134
136
|
|
|
135
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
137
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
136
138
|
_re_ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
137
139
|
|
|
138
140
|
def strip_ansi(cell):
|
|
@@ -140,17 +142,17 @@ def strip_ansi(cell):
|
|
|
140
142
|
for outp in cell.get('outputs', []):
|
|
141
143
|
if outp.get('name')=='stdout': outp['text'] = [_re_ansi_escape.sub('', o) for o in outp.text]
|
|
142
144
|
|
|
143
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
145
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
144
146
|
def strip_hidden_metadata(cell):
|
|
145
147
|
'''Strips "hidden" metadata property from code cells so it doesn't interfere with docs rendering'''
|
|
146
148
|
if cell.cell_type == 'code' and 'metadata' in cell: cell.metadata.pop('hidden',None)
|
|
147
149
|
|
|
148
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
150
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
149
151
|
def hide_(cell):
|
|
150
152
|
"Hide cell from output"
|
|
151
153
|
del(cell['source'])
|
|
152
154
|
|
|
153
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
155
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
154
156
|
def _re_hideline(lang=None): return re.compile(fr'{langs[lang]}\|\s*hide_line\s*$', re.MULTILINE)
|
|
155
157
|
|
|
156
158
|
def hide_line(cell):
|
|
@@ -159,7 +161,7 @@ def hide_line(cell):
|
|
|
159
161
|
if cell.cell_type == 'code' and _re_hideline(lang).search(cell.source):
|
|
160
162
|
cell.source = '\n'.join([c for c in cell.source.splitlines() if not _re_hideline(lang).search(c)])
|
|
161
163
|
|
|
162
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
164
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
163
165
|
def filter_stream_(cell, *words):
|
|
164
166
|
"Remove output lines containing any of `words` in `cell` stream output"
|
|
165
167
|
if not words: return
|
|
@@ -167,14 +169,23 @@ def filter_stream_(cell, *words):
|
|
|
167
169
|
if outp.output_type == 'stream':
|
|
168
170
|
outp['text'] = [l for l in outp.text if not re.search('|'.join(words), l)]
|
|
169
171
|
|
|
170
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
172
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
173
|
+
_aimagics_pattern = re.compile(r'^\s*(%%ai.? |%%ai.?$)', re.MULTILINE)
|
|
174
|
+
|
|
175
|
+
def ai_magics(cell):
|
|
176
|
+
"A preprocessor to convert AI magics to markdown"
|
|
177
|
+
if cell.cell_type == 'code' and _aimagics_pattern.search(cell.source):
|
|
178
|
+
cell.cell_type ='markdown'
|
|
179
|
+
cell.source = '\n'.join(cell.source.splitlines()[1:])
|
|
180
|
+
|
|
181
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
171
182
|
_magics_pattern = re.compile(r'^\s*(%%|%).*', re.MULTILINE)
|
|
172
183
|
|
|
173
184
|
def clean_magics(cell):
|
|
174
185
|
"A preprocessor to remove cell magic commands"
|
|
175
186
|
if cell.cell_type == 'code': cell.source = _magics_pattern.sub('', cell.source).strip()
|
|
176
187
|
|
|
177
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
188
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
178
189
|
_re_hdr_dash = re.compile(r'^#+\s+.*\s+-\s*$', re.MULTILINE)
|
|
179
190
|
|
|
180
191
|
def rm_header_dash(cell):
|
|
@@ -183,26 +194,26 @@ def rm_header_dash(cell):
|
|
|
183
194
|
src = cell.source.strip()
|
|
184
195
|
if cell.cell_type == 'markdown' and src.startswith('#') and src.endswith(' -'): del(cell['source'])
|
|
185
196
|
|
|
186
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
197
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
187
198
|
_hide_dirs = {'export','exporti', 'hide','default_exp'}
|
|
188
199
|
|
|
189
200
|
def rm_export(cell):
|
|
190
201
|
"Remove cells that are exported or hidden"
|
|
191
202
|
if cell.directives_ and (cell.directives_.keys() & _hide_dirs): del(cell['source'])
|
|
192
203
|
|
|
193
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
204
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
194
205
|
_re_showdoc = re.compile(r'^show_doc', re.MULTILINE)
|
|
195
206
|
def _is_showdoc(cell): return cell['cell_type'] == 'code' and _re_showdoc.search(cell.source)
|
|
196
207
|
def _add_directives(cell, d):
|
|
197
208
|
for k,v in d.items():
|
|
198
|
-
if not re.findall(
|
|
209
|
+
if not re.findall(fr'#\| *{k}:', cell.source): cell.source = f'#| {k}: {v}\n' + cell.source
|
|
199
210
|
|
|
200
211
|
def clean_show_doc(cell):
|
|
201
212
|
"Remove ShowDoc input cells"
|
|
202
213
|
if not _is_showdoc(cell): return
|
|
203
214
|
_add_directives(cell, {'output':'asis','echo':'false'})
|
|
204
215
|
|
|
205
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
216
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
206
217
|
def _ast_contains(trees, types):
|
|
207
218
|
for tree in trees:
|
|
208
219
|
for node in ast.walk(tree):
|
|
@@ -223,7 +234,7 @@ def _do_eval(cell):
|
|
|
223
234
|
return True
|
|
224
235
|
if _show_docs(trees): return True
|
|
225
236
|
|
|
226
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
237
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
227
238
|
class exec_show_docs(Processor):
|
|
228
239
|
"Execute cells needed for `show_docs` output, including exported cells and imports"
|
|
229
240
|
def begin(self):
|
|
@@ -238,7 +249,7 @@ class exec_show_docs(Processor):
|
|
|
238
249
|
if _do_eval(cell): self.k.cell(cell)
|
|
239
250
|
title = fm.get('title', '')
|
|
240
251
|
if self.k.exc:
|
|
241
|
-
raise Exception(f"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\n{cell.source}") from self.k.exc
|
|
252
|
+
raise Exception(f"Error{' in notebook: '+title if title else ''} in cell {cell.idx_} :\n{cell.source}") from self.k.exc
|
|
242
253
|
|
|
243
254
|
def end(self):
|
|
244
255
|
try: from ipywidgets import Widget
|
|
@@ -250,13 +261,13 @@ class exec_show_docs(Processor):
|
|
|
250
261
|
widgets = {**old, **new, 'state': {**old.get('state', {}), **new['state']}}
|
|
251
262
|
self.nb.metadata['widgets'] = {mimetype: widgets}
|
|
252
263
|
|
|
253
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
264
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
254
265
|
def _import_obj(s):
|
|
255
266
|
mod_nm, obj_nm = s.split(':')
|
|
256
267
|
mod = importlib.import_module(mod_nm)
|
|
257
268
|
return getattr(mod, obj_nm)
|
|
258
269
|
|
|
259
|
-
# %% ../nbs/api/10_processors.ipynb
|
|
270
|
+
# %% ../nbs/api/10_processors.ipynb
|
|
260
271
|
class FilterDefaults:
|
|
261
272
|
"Override `FilterDefaults` to change which notebook processors are used"
|
|
262
273
|
def xtra_procs(self):
|
|
@@ -266,11 +277,13 @@ class FilterDefaults:
|
|
|
266
277
|
def base_procs(self):
|
|
267
278
|
return [FrontmatterProc, populate_language, add_show_docs, insert_warning,
|
|
268
279
|
strip_ansi, hide_line, filter_stream_, rm_header_dash,
|
|
269
|
-
clean_show_doc, exec_show_docs, rm_export, clean_magics, hide_, add_links,
|
|
280
|
+
clean_show_doc, exec_show_docs, rm_export, ai_magics, clean_magics, hide_, add_links,
|
|
281
|
+
add_fold, mv_exports, strip_hidden_metadata]
|
|
270
282
|
|
|
271
283
|
def procs(self):
|
|
272
284
|
"Processors for export"
|
|
273
|
-
|
|
285
|
+
skip_procs = get_config().get('skip_procs', '').split(',')
|
|
286
|
+
return L(self.base_procs()).filter(lambda x: x.__name__ not in skip_procs) + self.xtra_procs()
|
|
274
287
|
|
|
275
288
|
def nb_proc(self, nb):
|
|
276
289
|
"Get an `NBProcessor` with these processors"
|
nbdev/qmd.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Basic qmd generation helpers (experimental)"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/15_qmd.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% ../nbs/api/15_qmd.ipynb 2
|
|
@@ -10,7 +12,7 @@ from fastcore.meta import delegates
|
|
|
10
12
|
# %% auto 0
|
|
11
13
|
__all__ = ['meta', 'div', 'img', 'btn', 'tbl_row', 'tbl_sep']
|
|
12
14
|
|
|
13
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
15
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
14
16
|
def meta(md, # Markdown to add meta to
|
|
15
17
|
classes=None, # List of CSS classes to add
|
|
16
18
|
style=None, # Dict of CSS styles to add
|
|
@@ -25,7 +27,7 @@ def meta(md, # Markdown to add meta to
|
|
|
25
27
|
meta = ' '.join(meta)
|
|
26
28
|
return md + ("{" + meta + "}" if meta else "")
|
|
27
29
|
|
|
28
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
30
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
29
31
|
def div(txt, # Markdown to add meta to
|
|
30
32
|
classes=None, # List of CSS classes to add
|
|
31
33
|
style=None, # Dict of CSS styles to add
|
|
@@ -33,7 +35,7 @@ def div(txt, # Markdown to add meta to
|
|
|
33
35
|
"A qmd div with optional metadata section"
|
|
34
36
|
return meta("::: ", classes=classes, style=style, **kwargs) + f"\n\n{txt}\n\n:::\n\n"
|
|
35
37
|
|
|
36
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
38
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
37
39
|
def img(fname, # Image to link to
|
|
38
40
|
classes=None, # List of CSS classes to add
|
|
39
41
|
style=None, # Dict of CSS styles to add
|
|
@@ -51,7 +53,7 @@ def img(fname, # Image to link to
|
|
|
51
53
|
res = meta(f'', classes=classes, style=style, **kwargs)
|
|
52
54
|
return f'[{res}]({fname})' if link else res
|
|
53
55
|
|
|
54
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
56
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
55
57
|
def btn(txt, # Button text
|
|
56
58
|
link, # Button link URL
|
|
57
59
|
classes=None, # List of CSS classes to add
|
|
@@ -60,20 +62,20 @@ def btn(txt, # Button text
|
|
|
60
62
|
"A qmd button"
|
|
61
63
|
return meta(f'[{txt}]({link})', classes=classes, style=style, role="button")
|
|
62
64
|
|
|
63
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
65
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
64
66
|
def tbl_row(cols:list, # Auto-stringified columns to show in the row
|
|
65
67
|
):
|
|
66
68
|
"Create a markdown table row from `cols`"
|
|
67
69
|
return '|' + '|'.join(str(c or '') for c in cols) + '|'
|
|
68
70
|
|
|
69
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
71
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
70
72
|
def tbl_sep(sizes:int|list=3 # List of column sizes, or single `int` if all sizes the same
|
|
71
73
|
):
|
|
72
74
|
"Create a markdown table separator with relative column size `sizes`"
|
|
73
75
|
if isinstance(sizes,int): sizes = [3]*sizes
|
|
74
76
|
return tbl_row('-'*s for s in sizes)
|
|
75
77
|
|
|
76
|
-
# %% ../nbs/api/15_qmd.ipynb
|
|
78
|
+
# %% ../nbs/api/15_qmd.ipynb
|
|
77
79
|
def _install_nbdev():
|
|
78
80
|
return div('''#### pip
|
|
79
81
|
|
nbdev/quarto.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Install and interact with Quarto from nbdev"""
|
|
2
|
+
|
|
1
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/14_quarto.ipynb.
|
|
2
4
|
|
|
3
5
|
# %% ../nbs/api/14_quarto.ipynb 3
|
|
@@ -19,14 +21,15 @@ import yaml
|
|
|
19
21
|
|
|
20
22
|
# %% auto 0
|
|
21
23
|
__all__ = ['BASE_QUARTO_URL', 'install_quarto', 'install', 'IndentDumper', 'nbdev_sidebar', 'refresh_quarto_yml',
|
|
22
|
-
'nbdev_proc_nbs', 'nbdev_readme', 'nbdev_docs', 'prepare', 'fs_watchdog',
|
|
24
|
+
'nbdev_proc_nbs', 'nbdev_readme', 'nbdev_contributing', 'nbdev_docs', 'prepare', 'fs_watchdog',
|
|
25
|
+
'nbdev_preview']
|
|
23
26
|
|
|
24
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
27
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
25
28
|
def _sprun(cmd):
|
|
26
29
|
try: subprocess.check_output(cmd, shell=True)
|
|
27
30
|
except subprocess.CalledProcessError as cpe: sys.exit(cpe.returncode)
|
|
28
31
|
|
|
29
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
32
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
30
33
|
BASE_QUARTO_URL='https://www.quarto.org/download/latest/'
|
|
31
34
|
|
|
32
35
|
def _install_linux():
|
|
@@ -53,7 +56,7 @@ def install_quarto():
|
|
|
53
56
|
elif 'linux' in sys.platform: _install_linux()
|
|
54
57
|
finally: system('sudo rm -f .installing')
|
|
55
58
|
|
|
56
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
59
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
57
60
|
@call_parse
|
|
58
61
|
def install():
|
|
59
62
|
"Install Quarto and the current library"
|
|
@@ -61,14 +64,14 @@ def install():
|
|
|
61
64
|
d = get_config().lib_path
|
|
62
65
|
if (d/'__init__.py').exists(): system(f'pip install -e "{d.parent}[dev]"')
|
|
63
66
|
|
|
64
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
67
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
65
68
|
def _pre(p,b=True): return ' ' * (len(p.parts)) + ('- ' if b else ' ')
|
|
66
69
|
def _sort(a):
|
|
67
70
|
x,y = a
|
|
68
71
|
if y.startswith('index.'): return x,'00'
|
|
69
72
|
return a
|
|
70
|
-
#|export
|
|
71
|
-
_def_file_re = '\.(?:ipynb|qmd|html)$'
|
|
73
|
+
#| export
|
|
74
|
+
_def_file_re = r'\.(?:ipynb|qmd|html)$'
|
|
72
75
|
|
|
73
76
|
@delegates(nbglob_cli)
|
|
74
77
|
def _nbglob_docs(
|
|
@@ -78,7 +81,7 @@ def _nbglob_docs(
|
|
|
78
81
|
**kwargs):
|
|
79
82
|
return nbglob(path, file_glob=file_glob, file_re=file_re, **kwargs)
|
|
80
83
|
|
|
81
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
84
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
82
85
|
def _recursive_parser(
|
|
83
86
|
dir_dict: dict, # Directory structure as a dict.
|
|
84
87
|
contents: list, # `contents` list from `sidebar.yaml` template dict.
|
|
@@ -87,12 +90,12 @@ def _recursive_parser(
|
|
|
87
90
|
set_index: bool = True): # If `True`, `index` file will be set to href.
|
|
88
91
|
for name, val in dir_dict.items():
|
|
89
92
|
if type(val) is str:
|
|
90
|
-
if re.search('index\..*', re.sub('^\d+_', '', val)) and set_index and section:
|
|
93
|
+
if re.search(r'index\..*', re.sub(r'^\d+_', '', val)) and set_index and section:
|
|
91
94
|
section.update({'href': str(dirpath/val)})
|
|
92
95
|
else:
|
|
93
96
|
contents.append(str(dirpath/val))
|
|
94
97
|
elif type(val) is dict:
|
|
95
|
-
name = re.sub('^\d+_', '', name)
|
|
98
|
+
name = re.sub(r'^\d+_', '', name)
|
|
96
99
|
section = {'section': name, 'contents': []}
|
|
97
100
|
contents.append(section)
|
|
98
101
|
_recursive_parser(val, section['contents'], dirpath/name, section=section)
|
|
@@ -101,14 +104,14 @@ class IndentDumper(yaml.Dumper):
|
|
|
101
104
|
def increase_indent(self, flow=False, indentless=False):
|
|
102
105
|
return super(IndentDumper, self).increase_indent(flow, False)
|
|
103
106
|
|
|
104
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
107
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
105
108
|
@call_parse
|
|
106
109
|
@delegates(_nbglob_docs)
|
|
107
110
|
def nbdev_sidebar(
|
|
108
111
|
path:str=None, # Path to notebooks
|
|
109
112
|
printit:bool=False, # Print YAML for debugging
|
|
110
113
|
force:bool=False, # Create sidebar even if settings.ini custom_sidebar=False
|
|
111
|
-
skip_folder_re:str='(?:^[_.]|^www\$)', # Skip folders matching regex
|
|
114
|
+
skip_folder_re:str=r'(?:^[_.]|^www\$)', # Skip folders matching regex
|
|
112
115
|
**kwargs):
|
|
113
116
|
"Create sidebar.yml"
|
|
114
117
|
if not force and get_config().custom_sidebar: return
|
|
@@ -136,7 +139,7 @@ def nbdev_sidebar(
|
|
|
136
139
|
if printit: return print(yml)
|
|
137
140
|
yml_path.write_text(yml)
|
|
138
141
|
|
|
139
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
142
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
140
143
|
_quarto_yml="""project:
|
|
141
144
|
type: website
|
|
142
145
|
|
|
@@ -145,6 +148,8 @@ format:
|
|
|
145
148
|
theme: cosmo
|
|
146
149
|
css: styles.css
|
|
147
150
|
toc: true
|
|
151
|
+
keep-md: true
|
|
152
|
+
commonmark: default
|
|
148
153
|
|
|
149
154
|
website:
|
|
150
155
|
twitter-card: true
|
|
@@ -158,7 +163,7 @@ website:
|
|
|
158
163
|
|
|
159
164
|
metadata-files: [nbdev.yml, sidebar.yml]"""
|
|
160
165
|
|
|
161
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
166
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
162
167
|
_nbdev_yml="""project:
|
|
163
168
|
output-dir: {doc_path}
|
|
164
169
|
|
|
@@ -170,7 +175,7 @@ website:
|
|
|
170
175
|
repo-url: "{git_url}"
|
|
171
176
|
"""
|
|
172
177
|
|
|
173
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
178
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
174
179
|
def refresh_quarto_yml():
|
|
175
180
|
"Generate `_quarto.yml` from `settings.ini`."
|
|
176
181
|
cfg = get_config()
|
|
@@ -184,13 +189,13 @@ def refresh_quarto_yml():
|
|
|
184
189
|
if qy.exists() and not str2bool(cfg.get('custom_quarto_yml', True)): qy.unlink()
|
|
185
190
|
if not qy.exists(): qy.write_text(_quarto_yml)
|
|
186
191
|
|
|
187
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
192
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
188
193
|
def _ensure_quarto():
|
|
189
194
|
if shutil.which('quarto'): return
|
|
190
195
|
print("Quarto is not installed. We will download and install it for you.")
|
|
191
196
|
install.__wrapped__()
|
|
192
197
|
|
|
193
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
198
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
194
199
|
def _pre_docs(path=None, n_workers:int=defaults.cpus, **kwargs):
|
|
195
200
|
cfg = get_config()
|
|
196
201
|
path = Path(path) if path else cfg.nbs_path
|
|
@@ -202,21 +207,21 @@ def _pre_docs(path=None, n_workers:int=defaults.cpus, **kwargs):
|
|
|
202
207
|
cache = proc_nbs(path, n_workers=n_workers, **kwargs)
|
|
203
208
|
return cache,cfg,path
|
|
204
209
|
|
|
205
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
210
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
206
211
|
@call_parse
|
|
207
212
|
@delegates(proc_nbs)
|
|
208
213
|
def nbdev_proc_nbs(**kwargs):
|
|
209
214
|
"Process notebooks in `path` for docs rendering"
|
|
210
215
|
_pre_docs(**kwargs)[0]
|
|
211
216
|
|
|
212
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
213
|
-
def
|
|
217
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
218
|
+
def _doc_mtime_not_older(readme_path, readme_nb_path):
|
|
214
219
|
if not readme_nb_path.exists():
|
|
215
220
|
print(f"Could not find {readme_nb_path}")
|
|
216
221
|
return True
|
|
217
222
|
return readme_path.exists() and readme_path.stat().st_mtime>=readme_nb_path.stat().st_mtime
|
|
218
223
|
|
|
219
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
224
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
220
225
|
class _SidebarYmlRemoved:
|
|
221
226
|
"Context manager for `nbdev_readme` to avoid rendering whole docs website"
|
|
222
227
|
def __init__(self,path): self._path=path
|
|
@@ -229,14 +234,14 @@ class _SidebarYmlRemoved:
|
|
|
229
234
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
230
235
|
if self._moved: (self._path/'sidebar.yml.bak').rename(self._yml_path)
|
|
231
236
|
|
|
232
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
237
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
233
238
|
def _copytree(a,b):
|
|
234
239
|
if sys.version_info.major >=3 and sys.version_info.minor >=8: copytree(a, b, dirs_exist_ok=True)
|
|
235
240
|
else:
|
|
236
241
|
from distutils.dir_util import copy_tree
|
|
237
242
|
copy_tree(a, b)
|
|
238
243
|
|
|
239
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
244
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
240
245
|
def _save_cached_readme(cache, cfg):
|
|
241
246
|
tmp_doc_path = cache/cfg.doc_path.name
|
|
242
247
|
readme = tmp_doc_path/'README.md'
|
|
@@ -247,7 +252,7 @@ def _save_cached_readme(cache, cfg):
|
|
|
247
252
|
_rdmi = tmp_doc_path/((cache/cfg.readme_nb).stem + '_files') # Supporting files for README
|
|
248
253
|
if _rdmi.exists(): _copytree(_rdmi, cfg.config_path/_rdmi.name)
|
|
249
254
|
|
|
250
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
255
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
251
256
|
@call_parse
|
|
252
257
|
def nbdev_readme(
|
|
253
258
|
path:str=None, # Path to notebooks
|
|
@@ -255,7 +260,7 @@ def nbdev_readme(
|
|
|
255
260
|
"Create README.md from readme_nb (index.ipynb by default)"
|
|
256
261
|
cfg = get_config()
|
|
257
262
|
path = Path(path) if path else cfg.nbs_path
|
|
258
|
-
if chk_time and
|
|
263
|
+
if chk_time and _doc_mtime_not_older(cfg.config_path/'README.md', path/cfg.readme_nb): return
|
|
259
264
|
|
|
260
265
|
with _SidebarYmlRemoved(path): # to avoid rendering whole website
|
|
261
266
|
cache = proc_nbs(path)
|
|
@@ -263,7 +268,39 @@ def nbdev_readme(
|
|
|
263
268
|
|
|
264
269
|
_save_cached_readme(cache, cfg)
|
|
265
270
|
|
|
266
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
271
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
272
|
+
def _save_cached_contributing(cache, cfg, contrib_nb):
|
|
273
|
+
"Move CONTRIBUTING.md (and any `_files` assets) from the Quarto build cache to the repo root."
|
|
274
|
+
tmp_doc_path = cache / cfg.doc_path.name
|
|
275
|
+
contrib_file = tmp_doc_path / 'CONTRIBUTING.md'
|
|
276
|
+
if contrib_file.exists():
|
|
277
|
+
final_path = cfg.config_path / 'CONTRIBUTING.md'
|
|
278
|
+
if final_path.exists(): final_path.unlink() # py37 doesn't have `missing_ok`
|
|
279
|
+
move(contrib_file, final_path)
|
|
280
|
+
assets_folder = tmp_doc_path / (Path(contrib_nb).stem + '_files') # Supporting files for CONTRIBUTING
|
|
281
|
+
if assets_folder.exists(): _copytree(assets_folder, cfg.config_path / assets_folder.name)
|
|
282
|
+
|
|
283
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
284
|
+
@call_parse
|
|
285
|
+
def nbdev_contributing(
|
|
286
|
+
path:str=None, # Path to notebooks
|
|
287
|
+
chk_time:bool=False # Only build if out-of-date
|
|
288
|
+
):
|
|
289
|
+
"""Create CONTRIBUTING.md from contributing_nb (defaults to 'contributing.ipynb' if present). Skips if the file doesn't exist."""
|
|
290
|
+
cfg = get_config()
|
|
291
|
+
path = Path(path) if path else cfg.nbs_path
|
|
292
|
+
contrib_nb_name = cfg.get('contributing_nb', 'contributing.ipynb')
|
|
293
|
+
contrib_nb_path = path / contrib_nb_name
|
|
294
|
+
if not contrib_nb_path.exists(): return
|
|
295
|
+
if chk_time and _doc_mtime_not_older(cfg.config_path / 'CONTRIBUTING.md' , contrib_nb_path): return
|
|
296
|
+
|
|
297
|
+
with _SidebarYmlRemoved(path): # to avoid rendering whole website
|
|
298
|
+
cache = proc_nbs(path)
|
|
299
|
+
_sprun(f'cd "{cache}" && quarto render "{cache/contrib_nb_name}" -o CONTRIBUTING.md -t gfm --no-execute')
|
|
300
|
+
|
|
301
|
+
_save_cached_contributing(cache, cfg, contrib_nb_name)
|
|
302
|
+
|
|
303
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
267
304
|
@call_parse
|
|
268
305
|
@delegates(_nbglob_docs)
|
|
269
306
|
def nbdev_docs(
|
|
@@ -273,11 +310,12 @@ def nbdev_docs(
|
|
|
273
310
|
"Create Quarto docs and README.md"
|
|
274
311
|
cache,cfg,path = _pre_docs(path, n_workers=n_workers, **kwargs)
|
|
275
312
|
nbdev_readme.__wrapped__(path=path, chk_time=True)
|
|
313
|
+
nbdev_contributing.__wrapped__(path=path, chk_time=True)
|
|
276
314
|
_sprun(f'cd "{cache}" && quarto render --no-cache')
|
|
277
315
|
shutil.rmtree(cfg.doc_path, ignore_errors=True)
|
|
278
316
|
move(cache/cfg.doc_path.name, cfg.config_path)
|
|
279
317
|
|
|
280
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
318
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
281
319
|
@call_parse
|
|
282
320
|
def prepare():
|
|
283
321
|
"Export, test, and clean notebooks, and render README if needed"
|
|
@@ -287,8 +325,9 @@ def prepare():
|
|
|
287
325
|
nbdev.clean.nbdev_clean.__wrapped__()
|
|
288
326
|
refresh_quarto_yml()
|
|
289
327
|
nbdev_readme.__wrapped__(chk_time=True)
|
|
328
|
+
nbdev_contributing.__wrapped__(chk_time=True)
|
|
290
329
|
|
|
291
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
330
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
292
331
|
@contextmanager
|
|
293
332
|
def fs_watchdog(func, path, recursive:bool=True):
|
|
294
333
|
"File system watchdog dispatching to `func`"
|
|
@@ -304,7 +343,7 @@ def fs_watchdog(func, path, recursive:bool=True):
|
|
|
304
343
|
observer.stop()
|
|
305
344
|
observer.join()
|
|
306
345
|
|
|
307
|
-
# %% ../nbs/api/14_quarto.ipynb
|
|
346
|
+
# %% ../nbs/api/14_quarto.ipynb
|
|
308
347
|
@call_parse
|
|
309
348
|
@delegates(_nbglob_docs)
|
|
310
349
|
def nbdev_preview(
|