1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
# Copied from the yt_project, commit e8fb57e
# yt/doc/extensions/notebook_sphinxext.py
# https://bitbucket.org/yt_analysis/yt/src/e8fb57e66ca42e26052dadf054a5c782740abec9/doc/extensions/notebook_sphinxext.py?at=yt
# Almost completely re-written by Matthew Harrigan to use nbconvert v4
import os
import shutil
import nbformat
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from nbconvert import HTMLExporter, PythonExporter
def _read(wd, name):
with open(f"{wd}/{name}.ipynb") as f:
notebook = nbformat.read(f, as_version=4)
return notebook
def export_html(wd, name):
nb = _read(wd, name)
config = {
"Exporter": {
"template_file": "embed",
"template_path": ["./sphinxext/"],
},
"ExecutePreprocessor": {"enabled": True},
"ExtractOutputPreprocessor": {"enabled": True},
"CSSHTMLHeaderPreprocessor": {"enabled": True},
}
exporter = HTMLExporter(config)
try:
body, resources = exporter.from_notebook_node(nb)
for fn, data in resources["outputs"].items():
with open(f"{wd}/{fn}", "wb") as f:
f.write(data)
return body
except Exception as e:
return str(e)
def export_python(wd, name):
nb = _read(wd, name)
exporter = PythonExporter()
body, resources = exporter.from_notebook_node(nb)
with open(f"{wd}/{name}.py", "w") as f:
f.write(body)
class NotebookDirective(Directive):
"""Insert an evaluated notebook into a document"""
required_arguments = 1
optional_arguments = 1
option_spec = {"skip_exceptions": directives.flag}
final_argument_whitespace = True
def run(self):
# check if raw html is supported
if not self.state.document.settings.raw_enabled:
raise self.warning('"%s" directive disabled.' % self.name)
# get path to notebook
nb_rel_path = self.arguments[0]
nb_abs_path = f"{setup.confdir}/../{nb_rel_path}"
nb_abs_path = os.path.abspath(nb_abs_path)
nb_name = os.path.basename(nb_rel_path).split(".")[0]
dest_dir = f"{setup.app.builder.outdir}/{os.path.dirname(nb_rel_path)}/{nb_name}"
fmt = {"wd": dest_dir, "name": nb_name}
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
shutil.copyfile(nb_abs_path, "{wd}/{name}.ipynb".format(**fmt))
# TODO: Actually save evaluated notebook
shutil.copyfile(nb_abs_path, "{wd}/{name}_eval.ipynb".format(**fmt))
html = export_html(**fmt)
export_python(**fmt)
# Create link to notebook and script files
link_rst = "({uneval}; {eval}; {py})".format(
uneval=formatted_link("{wd}/{name}.ipynb".format(**fmt)),
eval=formatted_link("{wd}/{name}_eval.ipynb".format(**fmt)),
py=formatted_link("{wd}/{name}.py".format(**fmt)),
)
rst_file = self.state_machine.document.attributes["source"]
self.state_machine.insert_input([link_rst], rst_file)
# create notebook node
attributes = {"format": "html", "source": "nb_path"}
nb_node = notebook_node("", html, **attributes)
nb_node.source, nb_node.line = self.state_machine.get_source_and_line(
self.lineno,
)
# add dependency
self.state.document.settings.record_dependencies.add(nb_abs_path)
return [nb_node]
class notebook_node(nodes.raw):
pass
def formatted_link(path):
return f"`{os.path.basename(path)} <{path}>`__"
def visit_notebook_node(self, node):
self.visit_raw(node)
def depart_notebook_node(self, node):
self.depart_raw(node)
def setup(app):
setup.app = app
setup.config = app.config
setup.confdir = app.confdir
app.add_node(
notebook_node,
html=(visit_notebook_node, depart_notebook_node),
)
app.add_directive("notebook", NotebookDirective)
|