A few weeks ago we discussed using Markdown in code cells. The implementation injected a function into the input transform manager in a running kernel.
This notebook takes an alternate approach where we create a custom kernel that permits markdown. Most of this work was made possible by the demonstration in creating a simple kernel for Jupyter.
Literate
transformer¶The Literate transformer will extract the indented code using CommonMark.
from deathbeds.__Markdown_code_cells import Literate
from deathbeds.__whereami import *
transformer = Literate()
The Kernel
subclasses the existing IPythonKernel
and strips the indented code before executing the kernel as normal.
from ipykernel.ipkernel import IPythonKernel
class LiterateKernel(IPythonKernel):
implementation = 'Literate'
implementation_version = '0.0.1'
banner = "Literate"
def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
code = transformer(code)
return super().do_execute(
code,
silent, store_history=store_history, user_expressions=user_expressions, allow_stdin=allow_stdin)
Kernel
assets¶ import json; from pathlib import Path
The python script imports this post and creates the module that will run the kernel.
kernel.json
¶ with __import__('importlib_resources').path('deathbeds', 'literate_kernel.py') as file:
kernel_json = Path(file).parent / 'literate' / 'kernel.json'
kernel_json.parent.mkdir(parents=True, exist_ok=True)
not kernel_json.exists() and kernel_json.write_text(json.dumps({
"argv": "python -m deathbeds.literate_kernel -f {connection_file}".split(),
"display_name": "literate", "name": "literate", "language": "python"}))
not Path(file).exists() and Path(file).write_text("""
with __import__('importnb').Notebook():
from deathbeds.__Literate_Markdown_Kernel import LiterateKernel
if __name__ == '__main__':
from ipykernel.kernelapp import IPKernelApp
IPKernelApp.launch_instance(kernel_class=LiterateKernel)""");
Originally we tried running the new kernel with IPython, but that is not permitted.
python
must invoke thekernelspec
.
In short, this notebook defines our literate kernel and deathbeds.literate_kernel
runs it.
Run these commands in the deathbeds
module directory.
from IPython import get_ipython; import click
@click.group()
def main(): ...
@main.command()
@click.option('--env', '-e', help='The environment.')
def install(env=None):
prefix = f"source activate {prefix} && " if env else ""
!$prefix jupyter kernelspec install literate
!$prefix jupyter kernelspec list
Nick said that installing with the
--user
flag is less reproducible so we removed it.
@main.command()
@click.option('--env', '-e', help='The environment.')
def uninstall(env=None):
prefix = f"source activate {prefix} && " if env else ""
!$prefix jupyter kernelspec uninstall literate
_file_ = '2018-07-20-Literate-Markdown-Kernel.ipynb'
@main.command()
def tests(): __import__('pytest').main(['-s', globals().get('__file__', _file_)])
CLI and main()
if INTERACTIVE:
!ipython -m deathbeds.2018-07-20-Literate-Markdown-Kernel -- tests
def test_import_kernel():
from deathbeds import literate_kernel
def test_commands():
# uninstall requires an input otherwise it hangs.
!ipython -m deathbeds.2018-07-20-Literate-Markdown-Kernel -- install