import sys
import nbformat
from nbdev.imports import *
from nbdev.process import *
from nbdev.processors import NBProcessor, mk_cell, populate_language, add_show_docs, insert_warning,\
strip_ansi, hide_line, filter_stream_, rm_header_dash,\
clean_show_doc, exec_show_docs, rm_export, clean_magics, hide_, add_links, strip_hidden_metadata
from pathlib import Path
import yaml
import re
from execnb.nbio import dict2nb,loadsWe need to import required nbdev processors for notebook for minimal rendering.
A few changes required
Nbdev has this notion of path_ of notebook which it adds as metadata on each notebook in FrontmatterProc. Since we work with stdout, it is difficult to know the same when adding an ipynb-filter. So we need to overwrite FrontMatterProc
Not all nbdev processors are required for pure quarto site , so we need to add only the once we use
Imports
FrontMatterProc
_RE_FM_BASE=r'''^---\s*
(.*?\S+.*?)
---\s*'''
_re_fm_nb = re.compile(_RE_FM_BASE+'$', flags=re.DOTALL)
_re_fm_md = re.compile(_RE_FM_BASE, flags=re.DOTALL)
def _fm2dict(s:str, nb=True):
"Load YAML frontmatter into a `dict`"
re_fm = _re_fm_nb if nb else _re_fm_md
match = re_fm.search(s.strip())
return yaml.safe_load(match.group(1)) if match else {}
def _md2dict(s:str):
"Convert H1 formatted markdown cell to frontmatter dict"
if '#' not in s: return {}
m = re.search(r'^#\s+(\S.*?)\s*$', s, flags=re.MULTILINE)
if not m: return {}
res = {'title': m.group(1)}
m = re.search(r'^>\s+(\S.*?)\s*$', s, flags=re.MULTILINE)
if m: res['description'] = m.group(1)
r = re.findall(r'^-\s+(\S.*:.*\S)\s*$', s, flags=re.MULTILINE)
if r:
try: res.update(yaml.safe_load('\n'.join(r)))
except Exception as e: warn(f'Failed to create YAML dict for:\n{r}\n\n{e}\n')
return resdef _dict2fm(d): return f'---\n{yaml.dump(d)}\n---\n\n'
def _insertfm(nb, fm): nb.cells.insert(0, mk_cell(_dict2fm(fm), 'raw'))
class MyFrontmatterProc(Processor):
"A YAML and formatted-markdown frontmatter processor"
def begin(self): self.fm = getattr(self.nb, 'frontmatter_', {})
def _update(self, f, cell):
s = cell.get('source')
if not s: return
d = f(s)
if not d: return
self.fm.update(d)
cell.source = None
def cell(self, cell):
if cell.cell_type=='raw': self._update(_fm2dict, cell)
elif cell.cell_type=='markdown' and 'title' not in self.fm: self._update(_md2dict, cell)
def end(self):
self.nb.frontmatter_ = self.fm
if not self.fm: return
_insertfm(self.nb, self.fm)Filter
def filter_nb():
procs = [MyFrontmatterProc, populate_language, insert_warning,
strip_ansi, hide_line, filter_stream_, rm_header_dash,
clean_magics, hide_, strip_hidden_metadata]
nb_txt = sys.stdin.read()
nb = dict2nb(loads(nb_txt))
nbp = NBProcessor(nb=nb, procs=procs)
nbp.process()
nbformat.write(nbp.nb, sys.stdout)
filter_nb()Add filter to _quarto.yml
ipynb-filters:
- nbdev_filter.py
project:
type: website
pre-render: custom2qmd.py
# post-render:
# - notes2site.py
# - app2site.py
preview:
host: '0.0.0.0'
port: 3000
browser: false
# twitter-card: true
Modifying Github Action
For making quarto work with libraries outside python STL we will need to update python environment with additional dependencies. For reasons unknown to me, this modification only works after quarto setup action. Relevant snippet from my publish file
- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2
- name: Install Python and Dependencies
uses: actions/setup-python@v4
with:
python-version: '3.10'
cache: 'pip'
- run: pip install jupyter
- run: pip install -r requirements.txt
- name: Render and Publish
uses: quarto-dev/quarto-actions/publish@v2
with:
target: netlify
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}