nbdev 3.3.21__tar.gz → 3.3.23__tar.gz
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-3.3.21/nbdev.egg-info → nbdev-3.3.23}/PKG-INFO +6 -4
- {nbdev-3.3.21 → nbdev-3.3.23}/README.md +5 -3
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/__init__.py +2 -2
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/_modidx.py +1 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/maker.py +3 -1
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/quarto.py +13 -10
- nbdev-3.3.23/nbdev/quarto_render.py +71 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/serve.py +14 -6
- nbdev-3.3.23/nbdev/skill.py +82 -0
- {nbdev-3.3.21 → nbdev-3.3.23/nbdev.egg-info}/PKG-INFO +6 -4
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev.egg-info/SOURCES.txt +3 -1
- nbdev-3.3.23/tests/test_quarto_render.py +54 -0
- nbdev-3.3.21/nbdev/skill.py +0 -70
- {nbdev-3.3.21 → nbdev-3.3.23}/CONTRIBUTING.md +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/LICENSE +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/clean.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/cli.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/config.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/diff.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/doclinks.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/export.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/extract_attachments.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/frontmatter.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/imports.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/merge.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/migrate.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/moddocs.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/process.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/processors.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/qmd.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/release.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/scrubmagics.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/serve_drv.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/showdoc.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/sync.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev/test.py +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev.egg-info/dependency_links.txt +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev.egg-info/entry_points.txt +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev.egg-info/requires.txt +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/nbdev.egg-info/top_level.txt +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/pyproject.toml +0 -0
- {nbdev-3.3.21 → nbdev-3.3.23}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nbdev
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.23
|
|
4
4
|
Summary: Create delightful software with Jupyter Notebooks
|
|
5
5
|
Author-email: "Jeremy Howard and the fast.ai community" <j@fast.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -100,7 +100,9 @@ You can also run `nbdev-help` from the terminal to see the full list of availabl
|
|
|
100
100
|
nbdev-conda Create a `meta.yaml` file ready to be built into a package, and optionally build and upload it
|
|
101
101
|
nbdev-contributing Create CONTRIBUTING.md from contributing_nb (defaults to 'contributing.ipynb' if present). Skips if the file doesn't exist.
|
|
102
102
|
nbdev-create-config Create a pyproject.toml config file.
|
|
103
|
-
nbdev-
|
|
103
|
+
nbdev-diff Cell-level diffs for changed notebooks between two git refs
|
|
104
|
+
nbdev-diff-driver Git external diff driver for notebooks; installed by `nbdev-install-hooks`
|
|
105
|
+
nbdev-docs Create Quarto docs
|
|
104
106
|
nbdev-export Export notebooks in `path` to Python modules
|
|
105
107
|
nbdev-filter A notebook filter for Quarto
|
|
106
108
|
nbdev-fix Create working notebook from conflicted notebook `nbname`
|
|
@@ -116,9 +118,9 @@ You can also run `nbdev-help` from the terminal to see the full list of availabl
|
|
|
116
118
|
nbdev-preview Preview docs locally
|
|
117
119
|
nbdev-proc-nbs Process notebooks in `path` for docs rendering
|
|
118
120
|
nbdev-pypi Create and upload Python package to PyPI
|
|
119
|
-
nbdev-readme Create README.md from readme_nb (index.ipynb by default)
|
|
121
|
+
nbdev-readme Create README.md from readme_nb (index.ipynb by default). Skips if the file doesn't exist.
|
|
120
122
|
nbdev-release-both Release both conda and PyPI packages
|
|
121
|
-
nbdev-release-gh
|
|
123
|
+
nbdev-release-gh Create the changelog, optionally edit it, then push and create the GitHub release
|
|
122
124
|
nbdev-release-git Tag and create a release in GitHub for the current version
|
|
123
125
|
nbdev-requirements Writes a `requirements.txt` file to `directory` based on pyproject.toml.
|
|
124
126
|
nbdev-sidebar Create sidebar.yml
|
|
@@ -56,7 +56,9 @@ You can also run `nbdev-help` from the terminal to see the full list of availabl
|
|
|
56
56
|
nbdev-conda Create a `meta.yaml` file ready to be built into a package, and optionally build and upload it
|
|
57
57
|
nbdev-contributing Create CONTRIBUTING.md from contributing_nb (defaults to 'contributing.ipynb' if present). Skips if the file doesn't exist.
|
|
58
58
|
nbdev-create-config Create a pyproject.toml config file.
|
|
59
|
-
nbdev-
|
|
59
|
+
nbdev-diff Cell-level diffs for changed notebooks between two git refs
|
|
60
|
+
nbdev-diff-driver Git external diff driver for notebooks; installed by `nbdev-install-hooks`
|
|
61
|
+
nbdev-docs Create Quarto docs
|
|
60
62
|
nbdev-export Export notebooks in `path` to Python modules
|
|
61
63
|
nbdev-filter A notebook filter for Quarto
|
|
62
64
|
nbdev-fix Create working notebook from conflicted notebook `nbname`
|
|
@@ -72,9 +74,9 @@ You can also run `nbdev-help` from the terminal to see the full list of availabl
|
|
|
72
74
|
nbdev-preview Preview docs locally
|
|
73
75
|
nbdev-proc-nbs Process notebooks in `path` for docs rendering
|
|
74
76
|
nbdev-pypi Create and upload Python package to PyPI
|
|
75
|
-
nbdev-readme Create README.md from readme_nb (index.ipynb by default)
|
|
77
|
+
nbdev-readme Create README.md from readme_nb (index.ipynb by default). Skips if the file doesn't exist.
|
|
76
78
|
nbdev-release-both Release both conda and PyPI packages
|
|
77
|
-
nbdev-release-gh
|
|
79
|
+
nbdev-release-gh Create the changelog, optionally edit it, then push and create the GitHub release
|
|
78
80
|
nbdev-release-git Tag and create a release in GitHub for the current version
|
|
79
81
|
nbdev-requirements Writes a `requirements.txt` file to `directory` based on pyproject.toml.
|
|
80
82
|
nbdev-sidebar Create sidebar.yml
|
|
@@ -4,9 +4,9 @@ Modules:
|
|
|
4
4
|
|
|
5
5
|
- `nbdev.extract_attachments`: A preprocessor that extracts all of the attachments from the notebook file. The extracted attachments are returned in the 'resources' dictionary.
|
|
6
6
|
- `nbdev.moddocs`: # Module docs: creating them from existing notebooks
|
|
7
|
-
- `nbdev.skill`:
|
|
7
|
+
- `nbdev.skill`: Write, read and review nbdev notebooks as source, documentation, examples and tests. You MUST read this before any work on a project with notebooks."""
|
|
8
8
|
|
|
9
|
-
__version__ = "3.3.
|
|
9
|
+
__version__ = "3.3.23"
|
|
10
10
|
|
|
11
11
|
from .doclinks import nbdev_export
|
|
12
12
|
from .showdoc import show_doc
|
|
@@ -364,6 +364,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
364
364
|
'nbdev.serve': { 'nbdev.serve._is_qpy': ('api/serve.html#_is_qpy', 'nbdev/serve.py'),
|
|
365
365
|
'nbdev.serve._keep_file': ('api/serve.html#_keep_file', 'nbdev/serve.py'),
|
|
366
366
|
'nbdev.serve._proc_file': ('api/serve.html#_proc_file', 'nbdev/serve.py'),
|
|
367
|
+
'nbdev.serve._rm_stale': ('api/serve.html#_rm_stale', 'nbdev/serve.py'),
|
|
367
368
|
'nbdev.serve.proc_nbs': ('api/serve.html#proc_nbs', 'nbdev/serve.py')},
|
|
368
369
|
'nbdev.showdoc': { 'nbdev.showdoc.BasicMarkdownRenderer': ('api/showdoc.html#basicmarkdownrenderer', 'nbdev/showdoc.py'),
|
|
369
370
|
'nbdev.showdoc.BasicMarkdownRenderer._repr_markdown_': ( 'api/showdoc.html#basicmarkdownrenderer._repr_markdown_',
|
|
@@ -191,7 +191,9 @@ def _retr_mdoc(cells, nb_path=None):
|
|
|
191
191
|
mdoc = '\n\n'.join(L(summ)+docs).strip()
|
|
192
192
|
url = nbpath2docurl(nb_path) if nb_path else ''
|
|
193
193
|
if url: mdoc = (mdoc + f'\n\nDocs: {url}').strip()
|
|
194
|
-
|
|
194
|
+
if not mdoc: return ''
|
|
195
|
+
r = 'r' if '\\' in mdoc else ''
|
|
196
|
+
return f'{r}"""{mdoc}"""\n\n'
|
|
195
197
|
|
|
196
198
|
# %% ../nbs/api/02_maker.ipynb #cdd205d6
|
|
197
199
|
@patch
|
|
@@ -23,6 +23,7 @@ from fastcore.shutil import rmtree,move,copytree
|
|
|
23
23
|
from fastcore.meta import delegates
|
|
24
24
|
from .serve import proc_nbs,_proc_file
|
|
25
25
|
from . import serve_drv
|
|
26
|
+
from .quarto_render import render_quarto
|
|
26
27
|
import yaml
|
|
27
28
|
|
|
28
29
|
# %% ../nbs/api/14_quarto.ipynb #aae2d2be-ad03-4536-bf70-c4575f39cea3
|
|
@@ -138,7 +139,7 @@ def nbdev_sidebar(
|
|
|
138
139
|
yml = yaml.dump(parsed_struct, Dumper=IndentDumper, sort_keys=False)
|
|
139
140
|
|
|
140
141
|
if printit: return print(yml)
|
|
141
|
-
yml_path.write_text(yml)
|
|
142
|
+
if not yml_path.exists() or yml_path.read_text() != yml: yml_path.write_text(yml)
|
|
142
143
|
|
|
143
144
|
# %% ../nbs/api/14_quarto.ipynb #aabf2f15
|
|
144
145
|
_quarto_yml="""project:
|
|
@@ -189,7 +190,8 @@ def refresh_quarto_yml():
|
|
|
189
190
|
vals = {k:cfg[k] for k in ['title', 'description', 'branch', 'git_url', 'doc_host', 'doc_baseurl']}
|
|
190
191
|
vals['doc_path'] = cfg.doc_path.name
|
|
191
192
|
if 'title' not in vals: vals['title'] = vals['lib_name']
|
|
192
|
-
|
|
193
|
+
text = _nbdev_yml.format(**vals)
|
|
194
|
+
if not ny.exists() or ny.read_text() != text: ny.write_text(text)
|
|
193
195
|
qy = cfg.nbs_path/'_quarto.yml'
|
|
194
196
|
if 'custom_quarto_yml' in cfg: print("NB: `_quarto.yml` is no longer auto-updated. Remove `custom_quarto_yml` from `pyproject.toml`")
|
|
195
197
|
if qy.exists() and not str2bool(cfg.get('custom_quarto_yml', True)): qy.unlink()
|
|
@@ -335,13 +337,11 @@ def _fix_quarto_nav(doc_path):
|
|
|
335
337
|
@delegates(_nbglob_docs)
|
|
336
338
|
def nbdev_docs(
|
|
337
339
|
path:str=None, # Path to notebooks
|
|
338
|
-
n_workers:int=defaults.cpus, #
|
|
340
|
+
n_workers:int=defaults.cpus, # Preprocessing and Quarto workers (0 or 1: serial)
|
|
339
341
|
**kwargs):
|
|
340
|
-
"Create Quarto docs
|
|
341
|
-
cache,cfg,
|
|
342
|
-
|
|
343
|
-
nbdev_readme.__wrapped__(path=path, chk_time=True)
|
|
344
|
-
nbdev_contributing.__wrapped__(path=path, chk_time=True)
|
|
342
|
+
"Create Quarto docs"
|
|
343
|
+
cache,cfg,_ = _pre_docs(path, n_workers=n_workers, **kwargs)
|
|
344
|
+
render_quarto(cache, cache/cfg.doc_path.name, n_workers=n_workers)
|
|
345
345
|
shutil.rmtree(cfg.doc_path, ignore_errors=True)
|
|
346
346
|
move(cache/cfg.doc_path.name, cfg.config_path)
|
|
347
347
|
_fix_quarto_nav(cfg.doc_path)
|
|
@@ -382,11 +382,15 @@ def nbdev_preview(
|
|
|
382
382
|
port:int=None, # The port on which to run preview
|
|
383
383
|
host:str=None, # The host on which to run preview
|
|
384
384
|
no_browser:bool=False, # Do not open a browser
|
|
385
|
-
|
|
385
|
+
render:bool=False, # Render all pages before previewing
|
|
386
|
+
n_workers:int=defaults.cpus, # Preprocessing and Quarto workers (0 or 1: serial)
|
|
386
387
|
**kwargs):
|
|
387
388
|
"Preview docs locally"
|
|
388
389
|
os.environ['QUARTO_PREVIEW']='1'
|
|
389
390
|
cache,cfg,path = _pre_docs(path, n_workers=n_workers, **kwargs)
|
|
391
|
+
site = cache/cfg.doc_path.name
|
|
392
|
+
if not (render or site.exists()) and cfg.doc_path.exists(): copytree(cfg.doc_path, site)
|
|
393
|
+
if render or not site.exists(): render_quarto(cache, site, n_workers=n_workers)
|
|
390
394
|
xtra = []
|
|
391
395
|
if port: xtra += ['--port', str(port)]
|
|
392
396
|
if host: xtra += ['--host', host]
|
|
@@ -399,5 +403,4 @@ def nbdev_preview(
|
|
|
399
403
|
except: traceback.print_exc()
|
|
400
404
|
|
|
401
405
|
os.chdir(cache)
|
|
402
|
-
xtra = xtra or []
|
|
403
406
|
with fs_watchdog(_f, path): subprocess.run(['quarto','preview']+xtra)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"Parallel Quarto website rendering in isolated project copies."
|
|
2
|
+
|
|
3
|
+
import json, shutil, subprocess, tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from xml.etree import ElementTree as ET
|
|
6
|
+
from fastcore.parallel import parallel
|
|
7
|
+
from fastcore.utils import defaults
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _quarto(path, *args):
|
|
11
|
+
res = subprocess.run(['quarto', *map(str, args)], cwd=path, capture_output=True, text=True)
|
|
12
|
+
if res.returncode: raise RuntimeError(res.stderr or res.stdout or f'Quarto exited with status {res.returncode}')
|
|
13
|
+
return res.stdout
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _render_batch(job):
|
|
17
|
+
path, inputs, output = job
|
|
18
|
+
_quarto(path, 'render', *inputs, '--no-cache', '--no-clean', '--output-dir', output)
|
|
19
|
+
return output
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _merge_site(outputs, dest):
|
|
23
|
+
search, urls, homepage = {}, {}, None
|
|
24
|
+
sitemap = None
|
|
25
|
+
for output in outputs:
|
|
26
|
+
idx = output/'index.html'
|
|
27
|
+
if idx.exists() and (homepage is None or 'http-equiv="refresh"' not in idx.read_text()): homepage = idx
|
|
28
|
+
if (output/'search.json').exists():
|
|
29
|
+
for entry in json.loads((output/'search.json').read_text()): search[entry['href']] = entry
|
|
30
|
+
if (output/'sitemap.xml').exists():
|
|
31
|
+
sitemap = ET.parse(output/'sitemap.xml')
|
|
32
|
+
for entry in sitemap.getroot(): urls[entry.find('{*}loc').text] = entry
|
|
33
|
+
shutil.copytree(output, dest, dirs_exist_ok=True)
|
|
34
|
+
if homepage: shutil.copy2(homepage, dest/'index.html')
|
|
35
|
+
if search: (dest/'search.json').write_text(json.dumps(list(search.values())))
|
|
36
|
+
if sitemap is not None:
|
|
37
|
+
sitemap.getroot()[:] = urls.values()
|
|
38
|
+
ET.register_namespace('', 'http://www.sitemaps.org/schemas/sitemap/0.9')
|
|
39
|
+
sitemap.write(dest/'sitemap.xml', encoding='utf-8', xml_declaration=True)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def render_quarto(path, output_dir, n_workers=defaults.cpus):
|
|
43
|
+
"Render a project, parallelizing website pages with isolated Quarto workers."
|
|
44
|
+
path, output_dir = Path(path).resolve(), Path(output_dir).resolve()
|
|
45
|
+
if output_dir == path or output_dir in path.parents: raise ValueError('Output directory must not contain the project')
|
|
46
|
+
if n_workers <= 1:
|
|
47
|
+
_quarto(path, 'render', '--no-cache', '--output-dir', output_dir)
|
|
48
|
+
return
|
|
49
|
+
info = json.loads(_quarto(path, 'inspect'))
|
|
50
|
+
project = info['config']['project']
|
|
51
|
+
website = info['config'].get('website', {})
|
|
52
|
+
if project['type'] != 'website' or project.get('pre-render') or project.get('post-render') or website.get('llms-txt'):
|
|
53
|
+
return render_quarto(path, output_dir, n_workers=1)
|
|
54
|
+
inputs = [str(Path(p).relative_to(path)) for p in info['files']['input']]
|
|
55
|
+
if len(inputs) < 2: return render_quarto(path, output_dir, n_workers=1)
|
|
56
|
+
n_workers = min(n_workers, len(inputs))
|
|
57
|
+
print(f'Rendering {len(inputs)} pages with {n_workers} Quarto workers')
|
|
58
|
+
excluded = {path/'.quarto', (path/project.get('output-dir', '_site')).resolve(), output_dir}
|
|
59
|
+
def ignore(src, names): return [n for n in names if Path(src)/n in excluded]
|
|
60
|
+
with tempfile.TemporaryDirectory(prefix='nbdev-quarto-') as tmp:
|
|
61
|
+
jobs = []
|
|
62
|
+
for i in range(n_workers):
|
|
63
|
+
worker = Path(tmp)/str(i)
|
|
64
|
+
shutil.copytree(path, worker, ignore=ignore)
|
|
65
|
+
jobs.append((worker, inputs[i::n_workers], Path(tmp)/f'output-{i}'))
|
|
66
|
+
outputs = parallel(_render_batch, jobs, n_workers=n_workers, threadpool=True)
|
|
67
|
+
merged = Path(tmp)/'merged'
|
|
68
|
+
_merge_site(outputs, merged)
|
|
69
|
+
if output_dir.exists(): shutil.rmtree(output_dir)
|
|
70
|
+
output_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
71
|
+
shutil.move(merged, output_dir)
|
|
@@ -38,15 +38,12 @@ def _is_qpy(path:Path):
|
|
|
38
38
|
if vl[0]=='---' and vl[-1]=='---': return '\n'.join(vl[1:-1])
|
|
39
39
|
|
|
40
40
|
# %% ../nbs/api/17_serve.ipynb #abc3835a
|
|
41
|
-
def _proc_file(s, cache, path
|
|
41
|
+
def _proc_file(s, cache, path):
|
|
42
42
|
skips = ('_proc', '_docs', '_site', 'pyproject.toml')
|
|
43
43
|
if not s.is_file() or any(o[0]=='.' or o in skips for o in s.parts): return
|
|
44
44
|
d = cache/s.relative_to(path)
|
|
45
45
|
if s.suffix=='.py': d = d.with_suffix('')
|
|
46
|
-
if d.exists():
|
|
47
|
-
dtime = d.stat().st_mtime
|
|
48
|
-
if mtime: dtime = max(dtime, mtime)
|
|
49
|
-
if s.stat().st_mtime<=dtime: return
|
|
46
|
+
if d.exists() and s.stat().st_mtime<=d.stat().st_mtime: return
|
|
50
47
|
|
|
51
48
|
d.parent.mkdir(parents=True, exist_ok=True)
|
|
52
49
|
if s.suffix=='.ipynb': return s,d,FilterDefaults
|
|
@@ -60,6 +57,15 @@ def _keep_file(f:Path, file_glob='', file_re=''):
|
|
|
60
57
|
if f.suffix!='.ipynb': return True
|
|
61
58
|
return (not file_glob or fnmatch(f.name, file_glob)) and (not file_re or bool(re.search(file_re, f.name)))
|
|
62
59
|
|
|
60
|
+
# %% ../nbs/api/17_serve.ipynb #ff47fad5
|
|
61
|
+
def _rm_stale(cache:Path, path:Path):
|
|
62
|
+
"Remove pages in `cache` whose source in `path` no longer exists"
|
|
63
|
+
for d in list(cache.rglob('*')):
|
|
64
|
+
rel = d.relative_to(cache)
|
|
65
|
+
if d.suffix not in ('.ipynb','.qmd','.md') or any(o[0]=='.' for o in rel.parts): continue
|
|
66
|
+
s = path/rel
|
|
67
|
+
if not (s.exists() or s.with_name(s.name+'.py').exists()): d.unlink()
|
|
68
|
+
|
|
63
69
|
# %% ../nbs/api/17_serve.ipynb #14463227
|
|
64
70
|
@delegates(nbglob_cli)
|
|
65
71
|
def proc_nbs(
|
|
@@ -77,6 +83,7 @@ def proc_nbs(
|
|
|
77
83
|
if file_glob or file_re: files = files.filter(_keep_file, file_glob=file_glob, file_re=file_re)
|
|
78
84
|
if (path/'_quarto.yml').exists(): files.append(path/'_quarto.yml')
|
|
79
85
|
if (path/'_brand.yml').exists(): files.append(path/'_brand.yml')
|
|
86
|
+
files.extend(nbglob(path, func=Path, file_glob='_metadata.y*ml', skip_file_re='^[.]'))
|
|
80
87
|
if (path/'_extensions').exists(): files.extend(nbglob(path/'_extensions', func=Path, file_glob='', file_re='', skip_file_re='^[.]'))
|
|
81
88
|
|
|
82
89
|
# If pyproject.toml or filter script newer than cache folder modified, delete cache
|
|
@@ -85,8 +92,9 @@ def proc_nbs(
|
|
|
85
92
|
cache_mtime = cache.stat().st_mtime
|
|
86
93
|
if force or (cache.exists() and cache_mtime<chk_mtime): rmtree(cache)
|
|
87
94
|
(cache/'_quarto.yml').unlink(missing_ok=True) # single-doc renders strip the staged copy in place (`_strip_sidebar`), so always restage it
|
|
95
|
+
_rm_stale(cache, path)
|
|
88
96
|
|
|
89
|
-
files = files.map(_proc_file,
|
|
97
|
+
files = files.map(_proc_file, cache=cache, path=path).filter()
|
|
90
98
|
kw = {} if in_notebook() else {'method':'spawn'}
|
|
91
99
|
parallel(nbdev.serve_drv.main, files, n_workers=n_workers, pause=0.01, **kw)
|
|
92
100
|
if cache.exists(): cache.touch()
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Write, read and review nbdev notebooks as source, documentation, examples and tests. You MUST read this before any work on a project with notebooks.
|
|
2
|
+
|
|
3
|
+
# Source
|
|
4
|
+
|
|
5
|
+
Write notebooks to be read top-to-bottom, interleaving implementation, explanation, examples, outputs, and failure demonstrations. Follow these conventions in published libraries. In internal projects, match the surrounding notebooks.
|
|
6
|
+
|
|
7
|
+
Check the file, not the repo: `# AUTOGENERATED! DO NOT EDIT!` names the source notebook; `# %% ../nbs/04_usage.ipynb #a45f753a` identifies the source cell. Edit that notebook, never generated modules, `__all__`, `_modidx.py`, or module docstrings; `nbdev-export` regenerates them. Unmarked modules are hand-written. Use notebooks to teach the API through narrative and examples, and plain `.py` for dense mechanism iterated under pytest (rarely; never add pytests to a project without them unless asked). Lessons test notebook code. Test plain modules in `tests/*.py`.
|
|
8
|
+
|
|
9
|
+
Understand and review work through the notebooks and their `git diff`, never generated modules. Those lack the prose, examples and tests.
|
|
10
|
+
|
|
11
|
+
For project-level questions, read `nbs/index.ipynb`, the source of README.md; regenerate with `nbdev-readme` after changes.
|
|
12
|
+
|
|
13
|
+
# Structure and authoring
|
|
14
|
+
|
|
15
|
+
Choose tutorial, how-to, explanation, or reference. Open with an H1 title cell and `>` subtitle, then a brief introduction. Use H2s for major concepts without fragmenting short narratives. Each heading belongs in its own markdown cell so Jupyter can collapse sections.
|
|
16
|
+
|
|
17
|
+
Before changing a notebook, read the whole notebook or a summary of every cell. For a new notebook, read a role model such as claudette's `00_core.ipynb` to learn the literate form.
|
|
18
|
+
|
|
19
|
+
Develop one idea at a time: a small implementation, its explanation, and a lesson if it's a substantive API. Introduce each lesson in markdown; split it when the introduction becomes complicated. Extend existing examples rather than adding near-duplicates. Build classes with `@patch` beside each method's explanation unless that obscures the API.
|
|
20
|
+
|
|
21
|
+
When new behavior needs a lesson, choose its narrative position, write the lesson first, run preceding cells as needed, and see it fail. Add its markdown introduction and implementation above it, then run both to see it pass. For bugs, revise an existing example to exercise the failing path; if no revision fits, add an assertion to it. If no documented behavior changes, change no lesson cells. Never add a cell merely to witness a fix.
|
|
22
|
+
|
|
23
|
+
Read existing examples before experimenting. Explore in the notebook, not in a separate kernel check; keep useful explorations as lessons and delete the rest. Always try to leave a notebook better than you found it.
|
|
24
|
+
|
|
25
|
+
Reserve underscore names for machinery without a coherent independent contract, not for everything used only internally. If a helper is only useful internally, the design is often awkward.
|
|
26
|
+
|
|
27
|
+
# Prose and module documentation
|
|
28
|
+
|
|
29
|
+
In markdown, explain rationale, distinctions and guarantees. Don't narrate code line by line or write filler transitions. Place markdown where it reads correctly on the generated page, normally after the definition. Use short symbol docstrings, parameter/return docments beside signatures, and backticked symbol names for nbdev links. Extended explanation, examples, and warnings belong in markdown.
|
|
30
|
+
|
|
31
|
+
`nbdev-export` builds the module docstring from the title's `>` description and exported markdown after the H1. Use one exported markdown cell per major section for long docstrings. `doc(module)` lists the functions. In module prose, explain only shared constraints and workflows.
|
|
32
|
+
|
|
33
|
+
`#| exportd` includes a code cell as fenced source in the module docstring, not module code. Use compact runnable demos after their required definitions; end the preceding exported summary with a colon. Unexported definitions between the summary and demo don't appear in the docstring.
|
|
34
|
+
|
|
35
|
+
Check claims by running code before documenting them; preview assembly with `nbdev.export.nb_mdoc`. `nbdev-export` lists a module in package documentation and `llms.txt` if its docstring has more than the default summary. Only extend docstrings of modules read on their own. Export the opening paragraphs of `index.ipynb` for the package intro.
|
|
36
|
+
|
|
37
|
+
# Lesson cells
|
|
38
|
+
|
|
39
|
+
A notebook guides a reader through what the code does. Readability comes first.
|
|
40
|
+
|
|
41
|
+
Every substantive API needs a lesson. Basic functionality outside the API, with little logic, generally doesn't.
|
|
42
|
+
|
|
43
|
+
Explain with code and its displayed output. When a displayed value is also worth testing, assign it, test it, then show it. Test results that are the point of the code, not incidentals such as formatting.
|
|
44
|
+
|
|
45
|
+
Show an error only when readers are likely to hit it.
|
|
46
|
+
|
|
47
|
+
A cell that serves a test instead of a reader doesn't belong on the page. Signs include setup that exists only so a check can run safely, and checks on glue code. Usually, remove such a test. If it checks substantive logic, move it to a `#| hide` cell or to pytest. Discuss such changes carefully.
|
|
48
|
+
|
|
49
|
+
Write page content: realistic values, minimal setup, reused objects, and direct assertions that support the lesson. Show evidence with compact `_repr_markdown_` or structured summaries, plots, tables, images or HTML. Explain what matters in large outputs and keep stored results focused.
|
|
50
|
+
|
|
51
|
+
Use `fastcore.test` assertions and `expect_fail` in ordinary code cells. Keep check plumbing, mocks, dense comprehensions, and long setup out of reader-facing examples: extract a small helper or hide the check. Use `nbdev-test --file-re` to run several named notebooks in parallel.
|
|
52
|
+
|
|
53
|
+
Consider a rewrite when you see these:
|
|
54
|
+
|
|
55
|
+
- Split commented examples into prose and code. Split numbered steps into markdown/code pairs. Keep comments that state constraints code cannot show.
|
|
56
|
+
- Replace a sentence-wrapped `print` with prose and a displayed value, and a final `print(x)` with bare `x`.
|
|
57
|
+
- Split reassignment stages and blank-line groups into cells, each with its own introduction.
|
|
58
|
+
- In near-duplicate examples, explain the argument that differs. Don't repeat the setup.
|
|
59
|
+
- Show errors with `expect_fail`, not `try`/`except`. Move long docstrings to markdown.
|
|
60
|
+
- Resolve TODO/FIXME example comments or move them to issues.
|
|
61
|
+
|
|
62
|
+
# State and execution
|
|
63
|
+
|
|
64
|
+
Define values near first use, introduce shared setup, reuse established objects, and don't reassign names later cells depend on. End exploratory cells with the expression recording what was learned when useful.
|
|
65
|
+
|
|
66
|
+
Keep imports in dedicated cells, including in lessons: docs builds execute import-containing cells in fresh namespaces, so mixing imports with state-dependent code can fail or run code at documentation time.
|
|
67
|
+
|
|
68
|
+
Stored outputs are generated and may be stale during editing. Don't stop work, clear them by hand, or edit notebook JSON because they no longer match the source. At PR time, regenerate them with `nbdev-test --save` if the project keeps saved outputs.
|
|
69
|
+
|
|
70
|
+
# Directives and nbdev v3
|
|
71
|
+
|
|
72
|
+
- `#| default_exp` names the generated module; `#| export` includes a cell there. Exported underscore helpers need not be public.
|
|
73
|
+
- `#| hide` hides distracting necessities from the page.
|
|
74
|
+
- `#| eval: false` is for code that must not run, not broken code. Later cells can't use its state.
|
|
75
|
+
- Directives may instead be metadata: `{"nbdev": {"export": "true"}}`. nbdev reads both; `fastcore.nbio.mk_cell(...).directives` merges them with content winning conflicts.
|
|
76
|
+
|
|
77
|
+
nbdev v3 (Jan 2026) uses `pyproject.toml`, not `settings.ini`: metadata in `[project]`, nbdev settings in `[tool.nbdev]` (default `nbs_path='nbs'`, `doc_path='_docs'`), version in `__init__.py` via `dynamic = ["version"]`, and `_modidx` in `[project.entry-points.nbdev]`. CLIs use hyphens (`nbdev-export`, `nbdev-test`); Python names retain underscores. GitHub actions are `fastai/workflows/nbdev3-ci` and `quarto-ghp3`.
|
|
78
|
+
|
|
79
|
+
# Finish
|
|
80
|
+
|
|
81
|
+
Revise affected prose, displays, and assertions together; changed assertions must fail on old code and pass on new. Re-read the touched narrative for readability. Run `nbdev-test <nb>`.
|
|
82
|
+
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nbdev
|
|
3
|
-
Version: 3.3.
|
|
3
|
+
Version: 3.3.23
|
|
4
4
|
Summary: Create delightful software with Jupyter Notebooks
|
|
5
5
|
Author-email: "Jeremy Howard and the fast.ai community" <j@fast.ai>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -100,7 +100,9 @@ You can also run `nbdev-help` from the terminal to see the full list of availabl
|
|
|
100
100
|
nbdev-conda Create a `meta.yaml` file ready to be built into a package, and optionally build and upload it
|
|
101
101
|
nbdev-contributing Create CONTRIBUTING.md from contributing_nb (defaults to 'contributing.ipynb' if present). Skips if the file doesn't exist.
|
|
102
102
|
nbdev-create-config Create a pyproject.toml config file.
|
|
103
|
-
nbdev-
|
|
103
|
+
nbdev-diff Cell-level diffs for changed notebooks between two git refs
|
|
104
|
+
nbdev-diff-driver Git external diff driver for notebooks; installed by `nbdev-install-hooks`
|
|
105
|
+
nbdev-docs Create Quarto docs
|
|
104
106
|
nbdev-export Export notebooks in `path` to Python modules
|
|
105
107
|
nbdev-filter A notebook filter for Quarto
|
|
106
108
|
nbdev-fix Create working notebook from conflicted notebook `nbname`
|
|
@@ -116,9 +118,9 @@ You can also run `nbdev-help` from the terminal to see the full list of availabl
|
|
|
116
118
|
nbdev-preview Preview docs locally
|
|
117
119
|
nbdev-proc-nbs Process notebooks in `path` for docs rendering
|
|
118
120
|
nbdev-pypi Create and upload Python package to PyPI
|
|
119
|
-
nbdev-readme Create README.md from readme_nb (index.ipynb by default)
|
|
121
|
+
nbdev-readme Create README.md from readme_nb (index.ipynb by default). Skips if the file doesn't exist.
|
|
120
122
|
nbdev-release-both Release both conda and PyPI packages
|
|
121
|
-
nbdev-release-gh
|
|
123
|
+
nbdev-release-gh Create the changelog, optionally edit it, then push and create the GitHub release
|
|
122
124
|
nbdev-release-git Tag and create a release in GitHub for the current version
|
|
123
125
|
nbdev-requirements Writes a `requirements.txt` file to `directory` based on pyproject.toml.
|
|
124
126
|
nbdev-sidebar Create sidebar.yml
|
|
@@ -21,6 +21,7 @@ nbdev/process.py
|
|
|
21
21
|
nbdev/processors.py
|
|
22
22
|
nbdev/qmd.py
|
|
23
23
|
nbdev/quarto.py
|
|
24
|
+
nbdev/quarto_render.py
|
|
24
25
|
nbdev/release.py
|
|
25
26
|
nbdev/scrubmagics.py
|
|
26
27
|
nbdev/serve.py
|
|
@@ -34,4 +35,5 @@ nbdev.egg-info/SOURCES.txt
|
|
|
34
35
|
nbdev.egg-info/dependency_links.txt
|
|
35
36
|
nbdev.egg-info/entry_points.txt
|
|
36
37
|
nbdev.egg-info/requires.txt
|
|
37
|
-
nbdev.egg-info/top_level.txt
|
|
38
|
+
nbdev.egg-info/top_level.txt
|
|
39
|
+
tests/test_quarto_render.py
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import json, shutil, subprocess, pytest
|
|
2
|
+
from xml.etree import ElementTree as ET
|
|
3
|
+
from fastcore.test import test_eq as teq
|
|
4
|
+
from nbdev.quarto_render import render_quarto
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.skipif(not shutil.which('quarto'), reason='requires Quarto')
|
|
8
|
+
def test_website(tmp_path):
|
|
9
|
+
root = tmp_path/'source'
|
|
10
|
+
root.mkdir()
|
|
11
|
+
(root/'_quarto.yml').write_text(r'''project:
|
|
12
|
+
type: website
|
|
13
|
+
website:
|
|
14
|
+
title: Parallel render
|
|
15
|
+
site-url: https://example.com/
|
|
16
|
+
sidebar:
|
|
17
|
+
contents: [index.qmd, alpha.qmd, beta.qmd]
|
|
18
|
+
format:
|
|
19
|
+
html: default
|
|
20
|
+
commonmark: default
|
|
21
|
+
execute:
|
|
22
|
+
enabled: false
|
|
23
|
+
''')
|
|
24
|
+
for name in ('index', 'alpha', 'beta'):
|
|
25
|
+
(root/f'{name}.qmd').write_text(fr'''---
|
|
26
|
+
title: {name}
|
|
27
|
+
---
|
|
28
|
+
## Detail
|
|
29
|
+
|
|
30
|
+
Page {name}. [Alpha](alpha.qmd#detail). [Download](data.txt).
|
|
31
|
+
''')
|
|
32
|
+
(root/'data.txt').write_text('shared resource')
|
|
33
|
+
serial, parallel = tmp_path/'serial', tmp_path/'parallel'
|
|
34
|
+
render_quarto(root, serial, n_workers=1)
|
|
35
|
+
render_quarto(root, parallel, n_workers=2)
|
|
36
|
+
search = lambda d: {e['href']: e['text'] for e in json.loads((d/'search.json').read_text())}
|
|
37
|
+
urls = lambda d: {e.text for e in ET.parse(d/'sitemap.xml').findall('.//{*}loc')}
|
|
38
|
+
teq(search(serial), search(parallel))
|
|
39
|
+
teq(urls(serial), urls(parallel))
|
|
40
|
+
teq(len(urls(parallel)), 3)
|
|
41
|
+
for name in ('index', 'alpha', 'beta'):
|
|
42
|
+
html = (parallel/f'{name}.html').read_text()
|
|
43
|
+
assert 'alpha.html#detail' in html and 'data.txt' in html
|
|
44
|
+
assert (parallel/f'{name}.md').exists()
|
|
45
|
+
teq((parallel/'data.txt').read_text(), 'shared resource')
|
|
46
|
+
assert (parallel/'site_libs').is_dir()
|
|
47
|
+
assert 'http-equiv="refresh"' not in (parallel/'index.html').read_text()
|
|
48
|
+
(root/'beta.qmd').write_text('---\ntitle: beta\n---\n## Detail\n\nUpdated beta.')
|
|
49
|
+
subprocess.run(['quarto', 'render', 'beta.qmd', '--quiet', '--output-dir', str(parallel)], cwd=root, check=True)
|
|
50
|
+
teq(set(search(serial)), set(search(parallel)))
|
|
51
|
+
assert 'Updated beta.' in search(parallel)['beta.html#detail']
|
|
52
|
+
(root/'beta.qmd').write_text('---\nfilters: [missing.lua]\n---\nBroken.')
|
|
53
|
+
with pytest.raises(RuntimeError, match='missing.lua'): render_quarto(root, parallel, n_workers=2)
|
|
54
|
+
assert 'Updated beta.' in search(parallel)['beta.html#detail']
|
nbdev-3.3.21/nbdev/skill.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"""Author nbdev notebooks as source, documentation, examples, and tests. Ensure this guidance is in context before any notebook edit.
|
|
2
|
-
|
|
3
|
-
# Source
|
|
4
|
-
|
|
5
|
-
Write notebooks to be read top-to-bottom, interleaving implementation, explanation, examples, outputs, and failure demonstrations. These conventions target published libraries; match surrounding notebooks in internal projects.
|
|
6
|
-
|
|
7
|
-
Check the file, not the repo: `# AUTOGENERATED! DO NOT EDIT!` names the source notebook; `# %% ../nbs/04_usage.ipynb #a45f753a` identifies the source cell. Edit that notebook, never generated modules, `__all__`, `_modidx.py`, or module docstrings; `nbdev-export` regenerates them. Unmarked modules are hand-written. Use notebooks where narrative/examples teach the API, plain `.py` for dense mechanism iterated under pytest (which should be very rare; never add pytests to a project that doesn't have any, unless explicitly requested). Tests follow source form: lesson cells for notebooks, `tests/*.py` for plain modules.
|
|
8
|
-
|
|
9
|
-
For project-level questions, read `nbs/index.ipynb`, the source of README.md; regenerate with `nbdev-readme` after changes.
|
|
10
|
-
|
|
11
|
-
# Structure and authoring
|
|
12
|
-
|
|
13
|
-
Choose tutorial, how-to, explanation, or reference. Open with an H1 title cell and `>` subtitle, then a brief introduction. Use H2s for major concepts without fragmenting short narratives. Each heading belongs in its own markdown cell so Jupyter can collapse sections.
|
|
14
|
-
|
|
15
|
-
Before changing a notebook, read the whole notebook or a summary of every cell. For a new notebook, read a role model such as claudette's `00_core.ipynb` to learn the literate form.
|
|
16
|
-
|
|
17
|
-
Develop one idea at a time: small implementation, explanation, executable lesson, assertions where useful. Introduce each lesson in markdown; split it when the introduction becomes complicated. Extend existing examples rather than adding near-duplicates. Build classes with `@patch` beside each method's explanation unless that obscures the API.
|
|
18
|
-
|
|
19
|
-
For new behavior, choose its narrative position, write the lesson first, run preceding cells as needed, and see it fail. Add its markdown introduction and implementation above it, then run implementation and lesson to see it pass. For bugs, revise an existing example to exercise the failing path; if no revision fits, add an assertion to it. If no documented behavior changes, change no lesson cells. Never add a cell merely to witness a fix.
|
|
20
|
-
|
|
21
|
-
Read existing examples before experimenting. Explore in the notebook, not in a separate kernel check; retain useful explorations as lessons and delete those that teach nothing worth keeping. Always try to leave a notebook better than you found it.
|
|
22
|
-
|
|
23
|
-
Reserve underscore names for machinery without a coherent independent contract. Internal use can justify exposing an abstraction; helpers useful only internally may indicate awkward design.
|
|
24
|
-
|
|
25
|
-
# Prose and module documentation
|
|
26
|
-
|
|
27
|
-
Markdown explains rationale, distinctions, and guarantees, not code line-by-line or empty transitions. Place it where it reads correctly on the generated page, normally after the definition. Use short symbol docstrings, parameter/return docments beside signatures, and backticked symbol names for nbdev links. Extended explanation, examples, and warnings belong in markdown.
|
|
28
|
-
|
|
29
|
-
The module docstring combines the title's `>` description with exported markdown after H1. Use one exported markdown cell per major section for long docstrings. Since `doc(module)` also lists functions, module prose should teach shared constraints and workflows, not repeat individual API documentation.
|
|
30
|
-
|
|
31
|
-
`#| exportd` includes a code cell as fenced source in the module docstring, not module code. Use compact runnable demos after their required definitions; end the preceding exported summary with a colon. Only exported cells join the docstring, so definitions between that summary and demo do not separate them there.
|
|
32
|
-
|
|
33
|
-
Check claims by running code before documenting them; preview assembly with `nbdev.export.nb_mdoc`. A docstring beyond the default summary causes `nbdev-export` to include the module in package documentation and `llms.txt`; write these summaries for modules meant to be read independently. Export the opening paragraphs of `index.ipynb` for the package intro.
|
|
34
|
-
|
|
35
|
-
# Lesson cells
|
|
36
|
-
|
|
37
|
-
Write page content, not standalone test cells (except rare hidden checks): realistic values, minimal setup, reused objects, and direct assertions that support the lesson. End lesson cells with an informative displayed value when there is something useful to show. Compact `_repr_markdown_` or structured summaries can make later examples readable; plots, tables, images, and HTML also provide evidence. Explain what matters in large outputs and keep stored results focused.
|
|
38
|
-
|
|
39
|
-
Use `fastcore.test` assertions and `expect_fail` in ordinary code cells, with one focused error example per contract. Keep check plumbing, mocks, dense comprehensions, and long setup out of reader-facing examples: extract a small helper or hide the check. Use `nbdev-test --file-re` for running >1 named notebook, since they'll run in parallel.
|
|
40
|
-
|
|
41
|
-
Review these as rewrite signals, not absolute rules:
|
|
42
|
-
|
|
43
|
-
- Comments often mark a split into prose and code; numbered steps suggest separate markdown/code pairs. Keep comments that state constraints code cannot show.
|
|
44
|
-
- A sentence-wrapped `print` belongs in prose plus a displayed value; final `print(x)` loses rich repr compared with bare `x`.
|
|
45
|
-
- Reassignments through stages can hide useful intermediate displays; blank-line groups suggest cell boundaries with their own introductions.
|
|
46
|
-
- Near-duplicate examples should make the differing argument the narrative, rather than repeat setup.
|
|
47
|
-
- Error-demonstration `try`/`except` usually wants `expect_fail`; long docstrings want markdown.
|
|
48
|
-
- Resolve TODO/FIXME example comments or move them to issues.
|
|
49
|
-
|
|
50
|
-
# State and execution
|
|
51
|
-
|
|
52
|
-
Define values near first use, introduce shared setup, reuse established objects, and don't reassign names later cells depend on. End exploratory cells with the expression recording what was learned when useful.
|
|
53
|
-
|
|
54
|
-
Keep imports in dedicated cells, including in lessons: docs builds execute import-containing cells in fresh namespaces, so mixing imports with state-dependent code can fail or run code at documentation time.
|
|
55
|
-
|
|
56
|
-
Stored outputs are generated artifacts and may be stale during editing. Do not stop, clear them manually, or manipulate notebook JSON because output no longer matches source. At PR time, regenerate with `nbdev-test --save` when the project needs saved outputs updated.
|
|
57
|
-
|
|
58
|
-
# Directives and nbdev v3
|
|
59
|
-
|
|
60
|
-
- `#| default_exp` names the generated module; `#| export` includes a cell there. Exported underscore helpers need not join the public API.
|
|
61
|
-
- `#| hide` hides distracting necessities from the page.
|
|
62
|
-
- `#| eval: false` is for code that must not run, not broken code; it cannot supply state to evaluated cells.
|
|
63
|
-
- Directives may instead be metadata: `{"nbdev": {"export": "true"}}`. The toolchain honors both; `mk_cell(...).directives` merges them with content winning conflicts.
|
|
64
|
-
|
|
65
|
-
nbdev v3 (Jan 2026) uses `pyproject.toml`, not `settings.ini`: metadata in `[project]`, nbdev settings in `[tool.nbdev]` (default `nbs_path='nbs'`, `doc_path='_docs'`), version in `__init__.py` via `dynamic = ["version"]`, and `_modidx` in `[project.entry-points.nbdev]`. CLIs use hyphens (`nbdev-export`, `nbdev-test`); Python names retain underscores. GitHub actions are `fastai/workflows/nbdev3-ci` and `quarto-ghp3`.
|
|
66
|
-
|
|
67
|
-
# Finish
|
|
68
|
-
|
|
69
|
-
Revise affected prose, displays, and assertions together; changed assertions must fail on old code and pass on new. Re-read the touched narrative for readability: tests cannot catch documentation damage. Run `nbdev-test <nb>`.
|
|
70
|
-
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|