Auto export python code from jupyter notebooks

because it is easier to search from .py files in text format. Using file save hooks from Jupyter, using nbconvert
jupyter
Published

January 5, 2021

This hack comes from https://github.com/jupyter/notebook/blob/master/docs/source/extending/savehooks.rst.

jupyter_notebook_config.py

Here is the code:

# Based off of https://github.com/jupyter/notebook/blob/master/docs/source/extending/savehooks.rst

import io
import os
from notebook.utils import to_api_path

_script_exporter = None
_html_exporter = None

def script_post_save(model, os_path, contents_manager, **kwargs):
    """convert notebooks to Python script after save with nbconvert

    replaces `ipython notebook --script`
    """
    from nbconvert.exporters.script import ScriptExporter
    from nbconvert.exporters.html import HTMLExporter

    if model['type'] != 'notebook':
        return

    global _script_exporter
    if _script_exporter is None:
        _script_exporter = ScriptExporter(parent=contents_manager)
    log = contents_manager.log

    global _html_exporter
    if _html_exporter is None:
        _html_exporter = HTMLExporter(parent=contents_manager)
    log = contents_manager.log

    # save .py file
    base, ext = os.path.splitext(os_path)
    script, resources = _script_exporter.from_filename(os_path)
    # si le sous rep eports_py existe, on ecrit dedans, sinon on ecrit à la racine
    sous_rep=''
    repertoire=os.path.dirname(base)
    if os.path.exists(repertoire+'/exports_py'):
        sous_rep='/exports_py'
    basename = os.path.basename(base)
    script_fname = repertoire+ sous_rep+'/'+basename+resources.get('output_extension', '.txt')
    log.info("base: {}, basename: {}, sous_rep: {}, repertoire: {}".format(base, basename, sous_rep, repertoire))
    log.info("script_fname: {}".format(script_fname))
    #script_fname = base + resources.get('output_extension', '.txt')
    log.info("Saving script /%s", to_api_path(script_fname, contents_manager.root_dir))
    with io.open(script_fname, 'w', encoding='utf-8') as f:
        f.write(script)

"""
    # save html
    base, ext = os.path.splitext(os_path)
    script, resources = _html_exporter.from_filename(os_path)
    script_fname = base + resources.get('output_extension', '.txt')
    log.info("Saving html /%s", to_api_path(script_fname, contents_manager.root_dir))
    with io.open(script_fname, 'w', encoding='utf-8') as f:
        f.write(script)
        
"""

c.FileContentsManager.post_save_hook = script_post_save

In this version, if a subfolder exports_py exists, .py version will be exported in it. Oherwise it will be exported in the notebook folder.

Maybe in a later version it would be good to export only of this subfolder exists. (for example I don’t need these py files when creating such a blog entry, even if my .gitignore won’t publish .py files)

And to remove the creation of Untitled.txt files when notebooks are just being created (and not yet named).

deployment

Just save/merge this jupyter_notebook_config.py file (download) to your jupyter home directory.

According to Config file and command line options in jupyter documentation, it is located at ~/.jupyter

And in windows it is at C:\Users\<yourID>\.jupyter

This will be valid for all your conda environments.

test

~/.jupyter$ cp ~/git/guillaume/blog/files/jupyter_notebook_config.py .

Restart Jupyter notebook server and click save on any notebook:

voila