金魚亭日常

読書,ガジェット,競技プログラミング

jupyter notebook の カスタム Exporter を作る

jupyter の公式ドキュメント の例のように,post save hook で nbconvert --to=scriptnbconvert --to=html が走るようにしている.

で,スクリプトのファイルは,Python の場合は Markdown のセルがコメントとして保存されるが,R (IRKernel) の場合だと,Markdown のセルは保存されない. 考えてみれば当たり前のことで,各言語によってコメントの書式は異なるからだ.

nbconvert --to=script での処理順序

nbconvert --to=script したときにどういう処理が行われるかというと,

  1. notebook の metadata の language_info を見る
  2. language_infonbconvert_exporter を見て,これに該当するExporter があるかどうかを確認する
    • entrypoints の nbconvert.exporters から探す
    • あったらそれを使う(Pythonの場合はこちら)
    • 最初 nbconvert_exporterの値で探して,そのあと to_lower() した値で探す
  3. nbconvert.exporters が無い場合は name を見る
    • entrypoints の nbconvert.exporters.script から探す
    • あったらそれを使う
    • to_lower() せず,そのままの値で探すのみ
  4. 汎用の Exporter を使う
    • R の場合はこちら

という感じになる

カスタム Exporter の作成

nbconvert の公式ドキュメント にカスタムExporter の書き方が載っているので,基本的にこれの通りにやる

entrypoints というのは,インストールされているパッケージを全部見て,setup.py に Exporter があれば登録する,ということをしているらしい.

なので,カスタム Exporter をPythonのパッケージとして作る

まず,こういう感じのディレクトリを作る

nbconvert_exporter_r
|   setup.py
+---nbconvert_exporter_r
|   |   __init__.py
|   \---templates
|           r.tpl

次に,ファイルの中身を書く.

setup.py

from setuptools import setup, find_packages

setup(
    name='nbconvert_exporter_r',
    version='0.0.3',
    description='Custom exporter of nbconvert for R language',
    author='whatalnk',
    packages=find_packages(),
    package_data={
        '': ['templates/*.tpl'],
    },
    entry_points={
        'nbconvert.exporters.script': [
            'R = nbconvert_exporter_r:RExporter'
        ],
    }
)

重要なのは,

  • package に template が含まれるように,package_data を書く
  • entry_points の キーはnbconvert.exporters.script にする
    • IRKernel の metadata には nbconvert_exporters がない

というところ.

__init__.py

import os
import os.path

from traitlets.config import Config
from nbconvert.exporters.templateexporter import TemplateExporter

class RExporter(TemplateExporter):
    export_from_notebook = "R format"

    def _file_extension_default(self):
        return '.R'

    @property
    def template_path(self):
        return super().template_path + [os.path.join(os.path.dirname(__file__), "templates")]

    def _template_file_default(self):
        return 'r.tpl' 

基本的に python の exporter と同じ.

r.tpl

{%- extends 'script.tpl' -%}

{% block in_prompt %}
{% if resources.global_content_filter.include_input_prompt -%}
    # In[{{ cell.execution_count if cell.execution_count else ' ' }}]:
{% endif %}
{% endblock in_prompt %}

{% block input %}
{{ cell.source }}
{% endblock input %}

{% block markdowncell scoped %}
{{ cell.source | comment_lines }}
{% endblock markdowncell %}

こちらも,基本的に python の template と同じ

パッケージング

# pip install wheel
python setup.py bdist_wheel

インストール

pip install --force-reinstall dist\nbconvert_exporter_r-0.0.3-py3-none-any.whl

できたやつ

github.com