a template for exporting markdownish notebooks¤
we need a combination of markdown and html templates to operate optimally with mkdocs. this document frankensteins templates and gives us some nice pixels.
get_template
creates our hybrid template from the initial TEMPLATE
and goes on to append blocks from nbconvert
templates.
def get_template() -> str:
blocks = __import__("collections").ChainMap(*map(get_blocks, "classic/base.html.j2 classic/index.html.j2".split()))
template = TEMPLATE
for k in KEEP:
if k in blocks: template += blocks[k].group("block") + "\n" * 3
return template
we KEEP
some blocks from the classic templates
KEEP = "notebook_css execute_result stream_stdout stream_stderr \
data_svg data_html data_png data_jpg error traceback_line data_widget_state data_widget_view".split()
get_blocks
loads a template and returns a dictionary of its blocks.
def get_blocks(alias) -> dict[str, (T := __import__("typing")).Pattern]:
template = get_exporter().environment.get_template(alias)
with open(template.filename) as file: body = file.read()
return dict(yield_blocks(body))
yield
all the blocks recursing into the found matches
def yield_blocks(string) -> T.Iterator[tuple[str, T.Pattern]]:
from tonyfast.regexs import jinja_block
for m in jinja_block.finditer(string):
yield m.group("name"), m
yield from yield_blocks(m.group("inner"))
initialize our exporter and jinja environment
i want to hide the input code, but no remove it; a custom exporter felt like a better way.
import nbconvert, re
class PidgyExporter(nbconvert.exporters.HTMLExporter):
def from_notebook_node(self, nb, resources=None, **kw):
resources = self._init_resources(dict(is_pidgy=is_pidgy(nb)))
return super().from_notebook_node(nb, resources, **kw)
we moved is_pidgy
and PIDGY
from the mkdocs plugin because them make more self here.
def is_pidgy(nb):
for cell in nb["cells"]:
if cell["cell_type"] == "code":
if PIDGY.match("".join(cell["source"])):
return True
return False
PIDGY = re.compile(r"\s*%(re)?load_ext pidgy")
def replace_attachments(cell):
source = "".join(cell["source"])
if cell.get("attachments"):
for k, v in cell["attachments"].items():
for t, v in v.items():
source = source.replace("attachment:" + k, "data:" + t + ";base64," + v)
return source
@(cache := __import__("functools").lru_cache)
def get_exporter() -> "nbconvert.TemplateExporter":
exporter = PidgyExporter()
exporter.environment.filters.setdefault("highlight_code", lambda x: x)
exporter.environment.filters.setdefault("attachment", replace_attachments)
return exporter
generating the template¤
this template holds so of our own logic.
TEMPLATE = """{%- extends 'display_priority.j2' -%}
{% block body_footer %}
{{super()}}
{% set mimetype = 'application/vnd.jupyter.widget-state+json'%}
{% if mimetype in nb.metadata.get("widgets",{})%}
<script type="{{ mimetype }}">
{{ nb.metadata.widgets[mimetype] | json_dumps }}
</script>
{% endif %}
{% endblock %}
{% block data_markdown scoped %}
{{output.data['text/markdown']}}
{% endblock data_markdown %}
{% block markdowncell scoped %}
{{cell | attachment}}
{% endblock markdowncell %}
{% block input %}
{% if not resources.is_pidgy %}
``````````````````````````````````````````````````````````````python
{{cell.source}}
``````````````````````````````````````````````````````````````
{% endif %}
{% endblock %}
"""
HEAD = """{% from 'base/jupyter_widgets.html.j2' import jupyter_widgets %}
<script src="{{ resources.require_js_url }}"></script>
{{ jupyter_widgets(resources.jupyter_widgets_base_url, resources.html_manager_semver_range, resources.widget_renderer_url) }}
"""
we can then combine our base template with existing nbconvert
ones to compute the final template.
template = get_template()
the generated template¤
if "__file__" not in locals():
display({"text/markdown": F"``````````````````````````html+jinja\n{template}\n``````````````````````````"}, raw=True)
{%- extends 'display_priority.j2' -%}
{% block body_footer %}
{{super()}}
{% set mimetype = 'application/vnd.jupyter.widget-state+json'%}
{% if mimetype in nb.metadata.get("widgets",{})%}
<script type="{{ mimetype }}">
{{ nb.metadata.widgets[mimetype] | json_dumps }}
</script>
{% endif %}
{% endblock %}
{% block input %}
{% if resources.is_pidgy %}<div class="cell source" hidden="">
{% endif %}
````````````````````````python
{{cell.source}}
````````````````````````
{% if resources.is_pidgy %}</div>
{% endif %}
{% endblock input %}
{% block data_markdown scoped %}
{{output.data['text/markdown']}}
{% endblock data_markdown %}
{% block markdowncell scoped %}
{{cell | attachment}}
{% endblock markdowncell %}
{% block notebook_css %}
{{ resources.include_css("static/style.css") }}
<style type="text/css">
/* Overrides of notebook CSS for static HTML export */
body {
overflow: visible;
padding: 8px;
}
div#notebook {
overflow: visible;
border-top: none;
}
{%- if resources.global_content_filter.no_prompt-%}
div#notebook-container{
padding: 6ex 12ex 8ex 12ex;
}
{%- endif -%}
@media print {
body {
margin: 0;
}
div.cell {
display: block;
page-break-inside: avoid;
}
div.output_wrapper {
display: block;
page-break-inside: avoid;
}
div.output {
display: block;
page-break-inside: avoid;
}
}
</style>
{% endblock notebook_css %}
{% block execute_result -%}
{%- set extra_class="output_execute_result" -%}
{% block data_priority scoped %}
{{ super() }}
{% endblock data_priority %}
{%- set extra_class="" -%}
{%- endblock execute_result %}
{% block stream_stdout -%}
<div class="output_subarea output_stream output_stdout output_text">
<pre>
{{- output.text | ansi2html -}}
</pre>
</div>
{%- endblock stream_stdout %}
{% block stream_stderr -%}
<div class="output_subarea output_stream output_stderr output_text">
<pre>
{{- output.text | ansi2html -}}
</pre>
</div>
{%- endblock stream_stderr %}
{% block data_svg scoped -%}
<div class="output_svg output_subarea {{ extra_class }}">
{%- if output.svg_filename %}
<img alt="No description has been provided for this image" src="{{ output.svg_filename | posix_path }}"/>
{%- else %}
{{ output.data['image/svg+xml'] }}
{%- endif %}
</div>
{%- endblock data_svg %}
{% block data_html scoped -%}
<div class="output_html rendered_html output_subarea {{ extra_class }}">
{%- if output.get('metadata', {}).get('text/html', {}).get('isolated') -%}
<iframe class="isolated-iframe" frameborder="0" scrolling="auto" src="data:text/html;base64,{{output.data['text/html'] | text_base64}}" style="height:520px; width:100%; margin:0; padding: 0">
</iframe>
{%- else -%}
{{ output.data['text/html'] }}
{%- endif -%}
</div>
{%- endblock data_html %}
{% block data_png scoped %}
<div class="output_png output_subarea {{ extra_class }}">
{%- if 'image/png' in output.metadata.get('filenames', {}) %}
<img %}="" 'image="" (cell="" -%}="" <img="" alt="{{ alttext }}" alttext="" class="unconfined" else="" endif="" get_metadata('alt'))="" get_metadata('alt',="" get_metadata('height',="" get_metadata('unconfined',="" get_metadata('width',="" height="" if="" is="" none="" not="" or="" output="" png')="" png'))="" set="" src="data:image/png;base64,{{ output.data['image/png'] }}" width="" {%-="" |="" }}=""/>
</div>
{%- endblock data_png %}
{% block data_jpg scoped %}
<div class="output_jpeg output_subarea {{ extra_class }}">
{%- if 'image/jpeg' in output.metadata.get('filenames', {}) %}
<img %}="" 'image="" (cell="" -%}="" <img="" alt="{{ alttext }}" alttext="" class="unconfined" else="" endif="" get_metadata('alt'))="" get_metadata('alt',="" get_metadata('height',="" get_metadata('unconfined',="" get_metadata('width',="" height="" if="" is="" jpeg')="" jpeg'))="" none="" not="" or="" output="" set="" src="data:image/jpeg;base64,{{ output.data['image/jpeg'] }}" width="" {%-="" |="" }}=""/>
</div>
{%- endblock data_jpg %}
{% block error -%}
<div class="output_subarea output_text output_error">
<pre>
{{- super() -}}
</pre>
</div>
{%- endblock error %}
{%- block traceback_line %}
{{ line | ansi2html }}
{%- endblock traceback_line %}
{%- block data_widget_state scoped %}
{% set div_id = uuid4() %}
{% set datatype_list = output.data | filter_data_type %}
{% set datatype = datatype_list[0]%}
<div class="output_subarea output_widget_state {{ extra_class }}" id="{{ div_id }}">
<script type="text/javascript">
var element = $('#{{ div_id }}');
</script>
<script type="{{ datatype }}">
{{ output.data[datatype] | json_dumps }}
</script>
</div>
{%- endblock data_widget_state -%}
{%- block data_widget_view scoped %}
{% set div_id = uuid4() %}
{% set datatype_list = output.data | filter_data_type %}
{% set datatype = datatype_list[0]%}
<div class="output_subarea output_widget_view {{ extra_class }}" id="{{ div_id }}">
<script type="text/javascript">
var element = $('#{{ div_id }}');
</script>
<script type="{{ datatype }}">
{{ output.data[datatype] | json_dumps }}
</script>
</div>
{%- endblock data_widget_view -%}