Skip to content

Commit 423ef63

Browse files
authored
Merge pull request #1286 from hugetim/readme-refactor
Refactor quarto.nbdev_readme; add docs
2 parents b2678cf + 05d2bed commit 423ef63

File tree

3 files changed

+144
-69
lines changed

3 files changed

+144
-69
lines changed

nbdev/_modidx.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,22 @@
231231
'nbdev.qmd.meta': ('api/qmd.html#meta', 'nbdev/qmd.py'),
232232
'nbdev.qmd.tbl_row': ('api/qmd.html#tbl_row', 'nbdev/qmd.py'),
233233
'nbdev.qmd.tbl_sep': ('api/qmd.html#tbl_sep', 'nbdev/qmd.py')},
234-
'nbdev.quarto': { 'nbdev.quarto._copytree': ('api/quarto.html#_copytree', 'nbdev/quarto.py'),
234+
'nbdev.quarto': { 'nbdev.quarto._SidebarYmlRemoved': ('api/quarto.html#_sidebarymlremoved', 'nbdev/quarto.py'),
235+
'nbdev.quarto._SidebarYmlRemoved.__enter__': ( 'api/quarto.html#_sidebarymlremoved.__enter__',
236+
'nbdev/quarto.py'),
237+
'nbdev.quarto._SidebarYmlRemoved.__exit__': ( 'api/quarto.html#_sidebarymlremoved.__exit__',
238+
'nbdev/quarto.py'),
239+
'nbdev.quarto._SidebarYmlRemoved.__init__': ( 'api/quarto.html#_sidebarymlremoved.__init__',
240+
'nbdev/quarto.py'),
241+
'nbdev.quarto._copytree': ('api/quarto.html#_copytree', 'nbdev/quarto.py'),
235242
'nbdev.quarto._ensure_quarto': ('api/quarto.html#_ensure_quarto', 'nbdev/quarto.py'),
236243
'nbdev.quarto._install_linux': ('api/quarto.html#_install_linux', 'nbdev/quarto.py'),
237244
'nbdev.quarto._install_mac': ('api/quarto.html#_install_mac', 'nbdev/quarto.py'),
238245
'nbdev.quarto._nbglob_docs': ('api/quarto.html#_nbglob_docs', 'nbdev/quarto.py'),
239246
'nbdev.quarto._pre': ('api/quarto.html#_pre', 'nbdev/quarto.py'),
240247
'nbdev.quarto._pre_docs': ('api/quarto.html#_pre_docs', 'nbdev/quarto.py'),
248+
'nbdev.quarto._readme_mtime_not_older': ('api/quarto.html#_readme_mtime_not_older', 'nbdev/quarto.py'),
249+
'nbdev.quarto._save_cached_readme': ('api/quarto.html#_save_cached_readme', 'nbdev/quarto.py'),
241250
'nbdev.quarto._sort': ('api/quarto.html#_sort', 'nbdev/quarto.py'),
242251
'nbdev.quarto._sprun': ('api/quarto.html#_sprun', 'nbdev/quarto.py'),
243252
'nbdev.quarto.fs_watchdog': ('api/quarto.html#fs_watchdog', 'nbdev/quarto.py'),

nbdev/quarto.py

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/14_quarto.ipynb.
22

3-
# %% ../nbs/api/14_quarto.ipynb 2
3+
# %% ../nbs/api/14_quarto.ipynb 3
44
from __future__ import annotations
55
import subprocess,sys,shutil,ast,warnings,traceback
66
from os import system
@@ -20,12 +20,12 @@
2020
__all__ = ['BASE_QUARTO_URL', 'install_quarto', 'install', 'nbdev_sidebar', 'refresh_quarto_yml', 'nbdev_proc_nbs',
2121
'nbdev_readme', 'nbdev_docs', 'prepare', 'fs_watchdog', 'nbdev_preview']
2222

23-
# %% ../nbs/api/14_quarto.ipynb 4
23+
# %% ../nbs/api/14_quarto.ipynb 5
2424
def _sprun(cmd):
2525
try: subprocess.check_output(cmd, shell=True)
2626
except subprocess.CalledProcessError as cpe: sys.exit(cpe.returncode)
2727

28-
# %% ../nbs/api/14_quarto.ipynb 6
28+
# %% ../nbs/api/14_quarto.ipynb 7
2929
BASE_QUARTO_URL='https://www.quarto.org/download/latest/'
3030

3131
def _install_linux():
@@ -50,15 +50,15 @@ def install_quarto():
5050
elif 'linux' in sys.platform: _install_linux()
5151
finally: system('sudo rm -f .installing')
5252

53-
# %% ../nbs/api/14_quarto.ipynb 7
53+
# %% ../nbs/api/14_quarto.ipynb 8
5454
@call_parse
5555
def install():
5656
"Install Quarto and the current library"
5757
install_quarto.__wrapped__()
5858
d = get_config().lib_path
5959
if (d/'__init__.py').exists(): system(f'pip install -e "{d.parent}[dev]"')
6060

61-
# %% ../nbs/api/14_quarto.ipynb 9
61+
# %% ../nbs/api/14_quarto.ipynb 10
6262
def _pre(p,b=True): return ' ' * (len(p.parts)) + ('- ' if b else ' ')
6363
def _sort(a):
6464
x,y = a
@@ -75,7 +75,7 @@ def _nbglob_docs(
7575
**kwargs):
7676
return nbglob(path, file_glob=file_glob, file_re=file_re, **kwargs)
7777

78-
# %% ../nbs/api/14_quarto.ipynb 10
78+
# %% ../nbs/api/14_quarto.ipynb 11
7979
@call_parse
8080
@delegates(_nbglob_docs)
8181
def nbdev_sidebar(
@@ -108,7 +108,7 @@ def _f(a,b): return Path(a),b
108108
if printit: return print(yml)
109109
yml_path.write_text(yml)
110110

111-
# %% ../nbs/api/14_quarto.ipynb 13
111+
# %% ../nbs/api/14_quarto.ipynb 14
112112
_quarto_yml="""project:
113113
type: website
114114
@@ -130,7 +130,7 @@ def _f(a,b): return Path(a),b
130130
131131
metadata-files: [nbdev.yml, sidebar.yml]"""
132132

133-
# %% ../nbs/api/14_quarto.ipynb 14
133+
# %% ../nbs/api/14_quarto.ipynb 15
134134
_nbdev_yml="""project:
135135
output-dir: {doc_path}
136136
@@ -142,7 +142,7 @@ def _f(a,b): return Path(a),b
142142
repo-url: "{git_url}"
143143
"""
144144

145-
# %% ../nbs/api/14_quarto.ipynb 15
145+
# %% ../nbs/api/14_quarto.ipynb 16
146146
def refresh_quarto_yml():
147147
"Generate `_quarto.yml` from `settings.ini`."
148148
cfg = get_config()
@@ -156,13 +156,13 @@ def refresh_quarto_yml():
156156
if qy.exists() and not str2bool(cfg.get('custom_quarto_yml', True)): qy.unlink()
157157
if not qy.exists(): qy.write_text(_quarto_yml)
158158

159-
# %% ../nbs/api/14_quarto.ipynb 16
159+
# %% ../nbs/api/14_quarto.ipynb 17
160160
def _ensure_quarto():
161161
if shutil.which('quarto'): return
162162
print("Quarto is not installed. We will download and install it for you.")
163163
install.__wrapped__()
164164

165-
# %% ../nbs/api/14_quarto.ipynb 17
165+
# %% ../nbs/api/14_quarto.ipynb 18
166166
def _pre_docs(path=None, n_workers:int=defaults.cpus, **kwargs):
167167
cfg = get_config()
168168
path = Path(path) if path else cfg.nbs_path
@@ -174,55 +174,67 @@ def _pre_docs(path=None, n_workers:int=defaults.cpus, **kwargs):
174174
cache = proc_nbs(path, n_workers=n_workers, **kwargs)
175175
return cache,cfg,path
176176

177-
# %% ../nbs/api/14_quarto.ipynb 18
177+
# %% ../nbs/api/14_quarto.ipynb 19
178178
@call_parse
179179
@delegates(proc_nbs)
180180
def nbdev_proc_nbs(**kwargs):
181181
"Process notebooks in `path` for docs rendering"
182182
_pre_docs(**kwargs)[0]
183183

184-
# %% ../nbs/api/14_quarto.ipynb 20
184+
# %% ../nbs/api/14_quarto.ipynb 21
185+
def _readme_mtime_not_older(readme_path, readme_nb_path):
186+
if not readme_nb_path.exists():
187+
print(f"Could not find {readme_nb_path}")
188+
return True
189+
return readme_path.exists() and readme_path.stat().st_mtime>=readme_nb_path.stat().st_mtime
190+
191+
# %% ../nbs/api/14_quarto.ipynb 22
192+
class _SidebarYmlRemoved:
193+
"Context manager for `nbdev_readme` to avoid rendering whole docs website"
194+
def __init__(self,path): self._path=path
195+
def __enter__(self):
196+
self._yml_path = self._path/'sidebar.yml'
197+
self._moved=False
198+
if self._yml_path.exists():
199+
self._yml_path.rename(self._path/'sidebar.yml.bak')
200+
self._moved=True
201+
def __exit__(self, exc_type, exc_value, exc_tb):
202+
if self._moved: (self._path/'sidebar.yml.bak').rename(self._yml_path)
203+
204+
# %% ../nbs/api/14_quarto.ipynb 23
185205
def _copytree(a,b):
186206
if sys.version_info.major >=3 and sys.version_info.minor >=8: copytree(a, b, dirs_exist_ok=True)
187207
else:
188208
from distutils.dir_util import copy_tree
189209
copy_tree(a, b)
190210

191-
# %% ../nbs/api/14_quarto.ipynb 21
211+
# %% ../nbs/api/14_quarto.ipynb 24
212+
def _save_cached_readme(cache, cfg):
213+
tmp_doc_path = cache/cfg.doc_path.name
214+
readme = tmp_doc_path/'README.md'
215+
if readme.exists():
216+
readme_path = cfg.config_path/'README.md'
217+
if readme_path.exists(): readme_path.unlink() # py37 doesn't have `missing_ok`
218+
move(readme, cfg.config_path)
219+
_rdmi = tmp_doc_path/((cache/cfg.readme_nb).stem + '_files') # Supporting files for README
220+
if _rdmi.exists(): _copytree(_rdmi, cfg.config_path/_rdmi.name)
221+
222+
# %% ../nbs/api/14_quarto.ipynb 25
192223
@call_parse
193224
def nbdev_readme(
194225
path:str=None, # Path to notebooks
195226
chk_time:bool=False): # Only build if out of date
196227
cfg = get_config()
197-
cfg_path = cfg.config_path
198228
path = Path(path) if path else cfg.nbs_path
199-
idx_path = path/cfg.readme_nb
200-
if not idx_path.exists(): return print(f"Could not find {idx_path}")
201-
readme_path = cfg_path/'README.md'
202-
if chk_time and readme_path.exists() and readme_path.stat().st_mtime>=idx_path.stat().st_mtime: return
203-
204-
yml_path = path/'sidebar.yml'
205-
moved=False
206-
if yml_path.exists():
207-
# move out of the way to avoid rendering whole website
208-
yml_path.rename(path/'sidebar.yml.bak')
209-
moved=True
229+
if chk_time and _readme_mtime_not_older(cfg.config_path/'README.md', path/cfg.readme_nb): return
210230

211-
try:
231+
with _SidebarYmlRemoved(path): # to avoid rendering whole website
212232
cache = proc_nbs(path)
213-
idx_cache = cache/cfg.readme_nb
214-
_sprun(f'cd "{cache}" && quarto render "{idx_cache}" -o README.md -t gfm --no-execute')
215-
finally:
216-
if moved: (path/'sidebar.yml.bak').rename(yml_path)
217-
tmp_doc_path = cache/cfg.doc_path.name
218-
readme = tmp_doc_path/'README.md'
219-
if readme.exists():
220-
_rdmi = tmp_doc_path/(idx_cache.stem + '_files')
221-
if readme_path.exists(): readme_path.unlink() # py37 doesn't have `missing_ok`
222-
move(readme, cfg_path)
223-
if _rdmi.exists(): _copytree(_rdmi, cfg_path/_rdmi.name) # Supporting files for README
233+
_sprun(f'cd "{cache}" && quarto render "{cache/cfg.readme_nb}" -o README.md -t gfm --no-execute')
234+
235+
_save_cached_readme(cache, cfg)
224236

225-
# %% ../nbs/api/14_quarto.ipynb 23
237+
# %% ../nbs/api/14_quarto.ipynb 28
226238
@call_parse
227239
@delegates(_nbglob_docs)
228240
def nbdev_docs(
@@ -236,7 +248,7 @@ def nbdev_docs(
236248
shutil.rmtree(cfg.doc_path, ignore_errors=True)
237249
move(cache/cfg.doc_path.name, cfg.config_path)
238250

239-
# %% ../nbs/api/14_quarto.ipynb 25
251+
# %% ../nbs/api/14_quarto.ipynb 30
240252
@call_parse
241253
def prepare():
242254
"Export, test, and clean notebooks, and render README if needed"
@@ -247,7 +259,7 @@ def prepare():
247259
refresh_quarto_yml()
248260
nbdev_readme.__wrapped__(chk_time=True)
249261

250-
# %% ../nbs/api/14_quarto.ipynb 27
262+
# %% ../nbs/api/14_quarto.ipynb 32
251263
@contextmanager
252264
def fs_watchdog(func, path, recursive:bool=True):
253265
"File system watchdog dispatching to `func`"
@@ -263,7 +275,7 @@ class _ProcessHandler(FileSystemEventHandler): dispatch=func
263275
observer.stop()
264276
observer.join()
265277

266-
# %% ../nbs/api/14_quarto.ipynb 28
278+
# %% ../nbs/api/14_quarto.ipynb 33
267279
@call_parse
268280
@delegates(_nbglob_docs)
269281
def nbdev_preview(

nbs/api/14_quarto.ipynb

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
"- order: 14"
1212
]
1313
},
14+
{
15+
"cell_type": "markdown",
16+
"id": "5d6a70e3",
17+
"metadata": {},
18+
"source": [
19+
"Helpful background on how Quarto fits in here: [https://nbdev.fast.ai/explanations/docs.html](https://nbdev.fast.ai/explanations/docs.html)"
20+
]
21+
},
1422
{
1523
"cell_type": "code",
1624
"execution_count": null,
@@ -346,9 +354,47 @@
346354
"metadata": {},
347355
"outputs": [],
348356
"source": [
357+
"#|hide\n",
349358
"# nbdev_proc_nbs.__wrapped__()"
350359
]
351360
},
361+
{
362+
"cell_type": "code",
363+
"execution_count": null,
364+
"id": "c5c7bb0e",
365+
"metadata": {},
366+
"outputs": [],
367+
"source": [
368+
"#|export\n",
369+
"def _readme_mtime_not_older(readme_path, readme_nb_path):\n",
370+
" if not readme_nb_path.exists():\n",
371+
" print(f\"Could not find {readme_nb_path}\")\n",
372+
" return True\n",
373+
" return readme_path.exists() and readme_path.stat().st_mtime>=readme_nb_path.stat().st_mtime"
374+
]
375+
},
376+
{
377+
"cell_type": "code",
378+
"execution_count": null,
379+
"id": "7b70b5c8",
380+
"metadata": {},
381+
"outputs": [],
382+
"source": [
383+
"#|hide\n",
384+
"#|export\n",
385+
"class _SidebarYmlRemoved:\n",
386+
" \"Context manager for `nbdev_readme` to avoid rendering whole docs website\"\n",
387+
" def __init__(self,path): self._path=path\n",
388+
" def __enter__(self):\n",
389+
" self._yml_path = self._path/'sidebar.yml'\n",
390+
" self._moved=False\n",
391+
" if self._yml_path.exists():\n",
392+
" self._yml_path.rename(self._path/'sidebar.yml.bak')\n",
393+
" self._moved=True\n",
394+
" def __exit__(self, exc_type, exc_value, exc_tb):\n",
395+
" if self._moved: (self._path/'sidebar.yml.bak').rename(self._yml_path)"
396+
]
397+
},
352398
{
353399
"cell_type": "code",
354400
"execution_count": null,
@@ -364,6 +410,25 @@
364410
" copy_tree(a, b)"
365411
]
366412
},
413+
{
414+
"cell_type": "code",
415+
"execution_count": null,
416+
"id": "caeaa153",
417+
"metadata": {},
418+
"outputs": [],
419+
"source": [
420+
"#|export\n",
421+
"def _save_cached_readme(cache, cfg):\n",
422+
" tmp_doc_path = cache/cfg.doc_path.name\n",
423+
" readme = tmp_doc_path/'README.md'\n",
424+
" if readme.exists():\n",
425+
" readme_path = cfg.config_path/'README.md'\n",
426+
" if readme_path.exists(): readme_path.unlink() # py37 doesn't have `missing_ok`\n",
427+
" move(readme, cfg.config_path)\n",
428+
" _rdmi = tmp_doc_path/((cache/cfg.readme_nb).stem + '_files') # Supporting files for README\n",
429+
" if _rdmi.exists(): _copytree(_rdmi, cfg.config_path/_rdmi.name)"
430+
]
431+
},
367432
{
368433
"cell_type": "code",
369434
"execution_count": null,
@@ -377,33 +442,22 @@
377442
" path:str=None, # Path to notebooks\n",
378443
" chk_time:bool=False): # Only build if out of date\n",
379444
" cfg = get_config()\n",
380-
" cfg_path = cfg.config_path\n",
381445
" path = Path(path) if path else cfg.nbs_path\n",
382-
" idx_path = path/cfg.readme_nb\n",
383-
" if not idx_path.exists(): return print(f\"Could not find {idx_path}\")\n",
384-
" readme_path = cfg_path/'README.md'\n",
385-
" if chk_time and readme_path.exists() and readme_path.stat().st_mtime>=idx_path.stat().st_mtime: return\n",
386-
"\n",
387-
" yml_path = path/'sidebar.yml'\n",
388-
" moved=False\n",
389-
" if yml_path.exists():\n",
390-
" # move out of the way to avoid rendering whole website\n",
391-
" yml_path.rename(path/'sidebar.yml.bak')\n",
392-
" moved=True\n",
446+
" if chk_time and _readme_mtime_not_older(cfg.config_path/'README.md', path/cfg.readme_nb): return\n",
393447
"\n",
394-
" try:\n",
448+
" with _SidebarYmlRemoved(path): # to avoid rendering whole website\n",
395449
" cache = proc_nbs(path)\n",
396-
" idx_cache = cache/cfg.readme_nb\n",
397-
" _sprun(f'cd \"{cache}\" && quarto render \"{idx_cache}\" -o README.md -t gfm --no-execute')\n",
398-
" finally:\n",
399-
" if moved: (path/'sidebar.yml.bak').rename(yml_path)\n",
400-
" tmp_doc_path = cache/cfg.doc_path.name\n",
401-
" readme = tmp_doc_path/'README.md'\n",
402-
" if readme.exists():\n",
403-
" _rdmi = tmp_doc_path/(idx_cache.stem + '_files')\n",
404-
" if readme_path.exists(): readme_path.unlink() # py37 doesn't have `missing_ok`\n",
405-
" move(readme, cfg_path)\n",
406-
" if _rdmi.exists(): _copytree(_rdmi, cfg_path/_rdmi.name) # Supporting files for README"
450+
" _sprun(f'cd \"{cache}\" && quarto render \"{cache/cfg.readme_nb}\" -o README.md -t gfm --no-execute')\n",
451+
" \n",
452+
" _save_cached_readme(cache, cfg)"
453+
]
454+
},
455+
{
456+
"cell_type": "markdown",
457+
"id": "a6f1a4ce",
458+
"metadata": {},
459+
"source": [
460+
"`nbdev_readme` calls \"quarto render,\" which is explained in the Quarto guide [here](https://quarto.org/docs/projects/code-execution.html)."
407461
]
408462
},
409463
{
@@ -414,7 +468,7 @@
414468
"outputs": [],
415469
"source": [
416470
"#|hide\n",
417-
"# nbdev_readme.__wrapped__(chk_time=True)"
471+
"# nbdev_readme.__wrapped__(chk_time=False)"
418472
]
419473
},
420474
{
@@ -576,7 +630,7 @@
576630
],
577631
"metadata": {
578632
"kernelspec": {
579-
"display_name": "torch",
633+
"display_name": "Python 3 (ipykernel)",
580634
"language": "python",
581635
"name": "python3"
582636
}

0 commit comments

Comments
 (0)