Last active
July 26, 2026 18:51
-
-
Save bryan-lunt/681a2467d5fff9a87748f34fde031bdc to your computer and use it in GitHub Desktop.
Jupyter / IPython notebook loader for Python >= 3.4 (PEP 451)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #http://jupyter-notebook.readthedocs.io/en/stable/examples/Notebook/Importing%20Notebooks.html | |
| import io, os, sys, types | |
| from typing import override | |
| import importlib, importlib.abc, importlib.util | |
| from IPython import get_ipython | |
| from nbformat import read | |
| from IPython.core.interactiveshell import InteractiveShell | |
| def find_notebook(fullname, path=None): | |
| """find a notebook, given its fully qualified name and an optional path | |
| This turns "foo.bar" into "foo/bar.ipynb" | |
| and tries turning "Foo_Bar" into "Foo Bar" if Foo_Bar | |
| does not exist. | |
| """ | |
| name = fullname.rsplit('.', 1)[-1] | |
| if not path: | |
| path = [''] | |
| for d in path: | |
| nb_path = os.path.join(d, name + ".ipynb") | |
| if os.path.isfile(nb_path): | |
| return nb_path | |
| # let import Notebook_Name find "Notebook Name.ipynb" | |
| nb_path = nb_path.replace("_", " ") | |
| if os.path.isfile(nb_path): | |
| return nb_path | |
| # | |
| class NotebookLoader(importlib.abc.Loader): | |
| """Module Loader for Jupyter Notebooks""" | |
| def __init__(self, fullname, path=None): | |
| self.shell = InteractiveShell.instance() | |
| self.fullname = fullname | |
| self.path = path | |
| def create_module(self, spec): | |
| mod = types.ModuleType(spec.name) | |
| mod.__file__ = spec.loader.path | |
| mod.__loader__ = self | |
| mod.__dict__['get_ipython'] = get_ipython | |
| return mod | |
| def exec_module(self, module): | |
| """import a notebook as a module""" | |
| path = find_notebook(self.fullname, self.path) | |
| print ("importing Jupyter notebook from %s" % path) | |
| # load the notebook object | |
| with io.open(path, 'r', encoding='utf-8') as f: | |
| nb = read(f, 4) | |
| # extra work to ensure that magics that would affect the user_ns | |
| # actually affect the notebook module's ns | |
| save_user_ns = self.shell.user_ns | |
| self.shell.user_ns = module.__dict__ | |
| try: | |
| for cell in nb.cells: | |
| if cell.cell_type == 'code': | |
| # transform the input to executable Python | |
| code = self.shell.input_transformer_manager.transform_cell(cell.source) | |
| # run the code in themodule | |
| exec(code, module.__dict__) | |
| finally: | |
| self.shell.user_ns = save_user_ns | |
| # | |
| class NotebookFinder(importlib.abc.MetaPathFinder): | |
| """Module finder that locates Jupyter Notebooks""" | |
| def __init__(self): | |
| self.loaders = {} | |
| def find_spec(self, fullname, path=None, target=None): | |
| nb_path = find_notebook(fullname, path) | |
| if not nb_path: | |
| return None | |
| key = path | |
| if path: | |
| # lists aren't hashable | |
| key = os.path.sep.join(path) | |
| else: | |
| key = fullname | |
| if key not in self.loaders: | |
| self.loaders[key] = importlib.util.spec_from_loader(fullname, NotebookLoader(fullname, path)) | |
| return self.loaders[key] | |
| # | |
| #happens when this is imported | |
| #Still not a proper jupyter extension though | |
| sys.meta_path.append(NotebookFinder()) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The code that creates the key is not completely trustworthy yet.
Eventually it needs to be submit back to the Jupyter project.
The code at https://jupyter-notebook.readthedocs.io/en/stable/examples/Notebook/Importing%20Notebooks.html no longer works.