Importing Notebooks.ipynb
523 lines
| 13.1 KiB
| text/plain
|
TextLexer
MinRK
|
r11699 | { | ||
Min RK
|
r18669 | "cells": [ | ||
MinRK
|
r11699 | { | ||
Min RK
|
r18669 | "cell_type": "markdown", | ||
"metadata": {}, | ||||
"source": [ | ||||
"# Importing IPython Notebooks as Modules" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"It is a common problem that people want to import code from IPython Notebooks.\n", | ||||
"This is made difficult by the fact that Notebooks are not plain Python files,\n", | ||||
"and thus cannot be imported by the regular Python machinery.\n", | ||||
"\n", | ||||
"Fortunately, Python provides some fairly sophisticated [hooks](http://www.python.org/dev/peps/pep-0302/) into the import machinery,\n", | ||||
"so we can actually make IPython notebooks importable without much difficulty,\n", | ||||
"and only using public APIs." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
"import io, os, sys, types" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
Pietro Battiston
|
r21008 | "from IPython import get_ipython\n", | ||
Min RK
|
r18669 | "from IPython.nbformat import current\n", | ||
"from IPython.core.interactiveshell import InteractiveShell" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Import hooks typically take the form of two objects:\n", | ||||
"\n", | ||||
"1. a Module **Loader**, which takes a module name (e.g. `'IPython.display'`), and returns a Module\n", | ||||
"2. a Module **Finder**, which figures out whether a module might exist, and tells Python what **Loader** to use" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
"def find_notebook(fullname, path=None):\n", | ||||
" \"\"\"find a notebook, given its fully qualified name and an optional path\n", | ||||
" \n", | ||||
" This turns \"foo.bar\" into \"foo/bar.ipynb\"\n", | ||||
" and tries turning \"Foo_Bar\" into \"Foo Bar\" if Foo_Bar\n", | ||||
" does not exist.\n", | ||||
" \"\"\"\n", | ||||
" name = fullname.rsplit('.', 1)[-1]\n", | ||||
" if not path:\n", | ||||
" path = ['']\n", | ||||
" for d in path:\n", | ||||
" nb_path = os.path.join(d, name + \".ipynb\")\n", | ||||
" if os.path.isfile(nb_path):\n", | ||||
" return nb_path\n", | ||||
" # let import Notebook_Name find \"Notebook Name.ipynb\"\n", | ||||
" nb_path = nb_path.replace(\"_\", \" \")\n", | ||||
" if os.path.isfile(nb_path):\n", | ||||
" return nb_path\n", | ||||
" " | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"## Notebook Loader" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Here we have our Notebook Loader.\n", | ||||
"It's actually quite simple - once we figure out the filename of the module,\n", | ||||
"all it does is:\n", | ||||
"\n", | ||||
"1. load the notebook document into memory\n", | ||||
"2. create an empty Module\n", | ||||
"3. execute every cell in the Module namespace\n", | ||||
"\n", | ||||
"Since IPython cells can have extended syntax,\n", | ||||
"the IPython transform is applied to turn each of these cells into their pure-Python counterparts before executing them.\n", | ||||
"If all of your notebook cells are pure-Python,\n", | ||||
"this step is unnecessary." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
"class NotebookLoader(object):\n", | ||||
" \"\"\"Module Loader for IPython Notebooks\"\"\"\n", | ||||
" def __init__(self, path=None):\n", | ||||
" self.shell = InteractiveShell.instance()\n", | ||||
" self.path = path\n", | ||||
" \n", | ||||
" def load_module(self, fullname):\n", | ||||
" \"\"\"import a notebook as a module\"\"\"\n", | ||||
" path = find_notebook(fullname, self.path)\n", | ||||
" \n", | ||||
" print (\"importing IPython notebook from %s\" % path)\n", | ||||
" \n", | ||||
" # load the notebook object\n", | ||||
" with io.open(path, 'r', encoding='utf-8') as f:\n", | ||||
" nb = current.read(f, 'json')\n", | ||||
" \n", | ||||
" \n", | ||||
" # create the module and add it to sys.modules\n", | ||||
" # if name in sys.modules:\n", | ||||
" # return sys.modules[name]\n", | ||||
" mod = types.ModuleType(fullname)\n", | ||||
" mod.__file__ = path\n", | ||||
" mod.__loader__ = self\n", | ||||
Pietro Battiston
|
r21008 | " mod.__dict__['get_ipython'] = get_ipython\n", | ||
Min RK
|
r18669 | " sys.modules[fullname] = mod\n", | ||
" \n", | ||||
" # extra work to ensure that magics that would affect the user_ns\n", | ||||
" # actually affect the notebook module's ns\n", | ||||
" save_user_ns = self.shell.user_ns\n", | ||||
" self.shell.user_ns = mod.__dict__\n", | ||||
" \n", | ||||
" try:\n", | ||||
" for cell in nb.worksheets[0].cells:\n", | ||||
" if cell.cell_type == 'code' and cell.language == 'python':\n", | ||||
" # transform the input to executable Python\n", | ||||
" code = self.shell.input_transformer_manager.transform_cell(cell.input)\n", | ||||
" # run the code in themodule\n", | ||||
" exec(code, mod.__dict__)\n", | ||||
" finally:\n", | ||||
" self.shell.user_ns = save_user_ns\n", | ||||
" return mod\n" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"## The Module Finder" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"The finder is a simple object that tells you whether a name can be imported,\n", | ||||
"and returns the appropriate loader.\n", | ||||
"All this one does is check, when you do:\n", | ||||
"\n", | ||||
"```python\n", | ||||
"import mynotebook\n", | ||||
"```\n", | ||||
"\n", | ||||
"it checks whether `mynotebook.ipynb` exists.\n", | ||||
"If a notebook is found, then it returns a NotebookLoader.\n", | ||||
"\n", | ||||
"Any extra logic is just for resolving paths within packages." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
"class NotebookFinder(object):\n", | ||||
" \"\"\"Module finder that locates IPython Notebooks\"\"\"\n", | ||||
" def __init__(self):\n", | ||||
" self.loaders = {}\n", | ||||
" \n", | ||||
" def find_module(self, fullname, path=None):\n", | ||||
" nb_path = find_notebook(fullname, path)\n", | ||||
" if not nb_path:\n", | ||||
" return\n", | ||||
" \n", | ||||
" key = path\n", | ||||
" if path:\n", | ||||
" # lists aren't hashable\n", | ||||
" key = os.path.sep.join(path)\n", | ||||
" \n", | ||||
" if key not in self.loaders:\n", | ||||
" self.loaders[key] = NotebookLoader(path)\n", | ||||
" return self.loaders[key]\n" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"## Register the hook" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Now we register the `NotebookFinder` with `sys.meta_path`" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
"sys.meta_path.append(NotebookFinder())" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"After this point, my notebooks should be importable.\n", | ||||
"\n", | ||||
"Let's look at what we have in the CWD:" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"ls nbpackage" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"So I should be able to `import nbimp.mynotebook`.\n" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"### Aside: displaying notebooks" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Here is some simple code to display the contents of a notebook\n", | ||||
"with syntax highlighting, etc." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"from pygments import highlight\n", | ||||
"from pygments.lexers import PythonLexer\n", | ||||
"from pygments.formatters import HtmlFormatter\n", | ||||
"\n", | ||||
"from IPython.display import display, HTML\n", | ||||
"\n", | ||||
"formatter = HtmlFormatter()\n", | ||||
"lexer = PythonLexer()\n", | ||||
"\n", | ||||
"# publish the CSS for pygments highlighting\n", | ||||
"display(HTML(\"\"\"\n", | ||||
"<style type='text/css'>\n", | ||||
"%s\n", | ||||
"</style>\n", | ||||
"\"\"\" % formatter.get_style_defs()\n", | ||||
"))" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"def show_notebook(fname):\n", | ||||
" \"\"\"display a short summary of the cells of a notebook\"\"\"\n", | ||||
" with io.open(fname, 'r', encoding='utf-8') as f:\n", | ||||
" nb = current.read(f, 'json')\n", | ||||
" html = []\n", | ||||
" for cell in nb.worksheets[0].cells:\n", | ||||
" html.append(\"<h4>%s cell</h4>\" % cell.cell_type)\n", | ||||
" if cell.cell_type == 'code':\n", | ||||
" html.append(highlight(cell.input, lexer, formatter))\n", | ||||
" else:\n", | ||||
" html.append(\"<pre>%s</pre>\" % cell.source)\n", | ||||
" display(HTML('\\n'.join(html)))\n", | ||||
"\n", | ||||
"show_notebook(os.path.join(\"nbpackage\", \"mynotebook.ipynb\"))" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"So my notebook has a heading cell and some code cells,\n", | ||||
"one of which contains some IPython syntax.\n", | ||||
"\n", | ||||
"Let's see what happens when we import it" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"from nbpackage import mynotebook" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Hooray, it imported! Does it work?" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"mynotebook.foo()" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Hooray again!\n", | ||||
"\n", | ||||
"Even the function that contains IPython syntax works:" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"mynotebook.has_ip_syntax()" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"## Notebooks in packages" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"We also have a notebook inside the `nb` package,\n", | ||||
"so let's make sure that works as well." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"ls nbpackage/nbs" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Note that the `__init__.py` is necessary for `nb` to be considered a package,\n", | ||||
"just like usual." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"show_notebook(os.path.join(\"nbpackage\", \"nbs\", \"other.ipynb\"))" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"from nbpackage.nbs import other\n", | ||||
"other.bar(5)" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"So now we have importable notebooks, from both the local directory and inside packages.\n", | ||||
"\n", | ||||
"I can even put a notebook inside IPython, to further demonstrate that this is working properly:" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
"outputs": [], | ||||
"source": [ | ||||
"import shutil\n", | ||||
"from IPython.utils.path import get_ipython_package_dir\n", | ||||
"\n", | ||||
"utils = os.path.join(get_ipython_package_dir(), 'utils')\n", | ||||
"shutil.copy(os.path.join(\"nbpackage\", \"mynotebook.ipynb\"),\n", | ||||
" os.path.join(utils, \"inside_ipython.ipynb\")\n", | ||||
")" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"and import the notebook from `IPython.utils`" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
Jonathan Frederic
|
r20536 | "execution_count": null, | ||
Min RK
|
r18669 | "metadata": { | ||
"collapsed": false | ||||
}, | ||||
Jonathan Frederic
|
r20536 | "outputs": [], | ||
Min RK
|
r18669 | "source": [ | ||
"from IPython.utils import inside_ipython\n", | ||||
"inside_ipython.whatsmyname()" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"This approach can even import functions and classes that are defined in a notebook using the `%%cython` magic." | ||||
] | ||||
MinRK
|
r11699 | } | ||
Min RK
|
r18669 | ], | ||
"metadata": { | ||||
"gist_id": "6011986", | ||||
Min RK
|
r20278 | "kernelspec": { | ||
"display_name": "Python 3", | ||||
"language": "python", | ||||
"name": "python3" | ||||
}, | ||||
"language_info": { | ||||
"codemirror_mode": { | ||||
"name": "ipython", | ||||
"version": 3 | ||||
}, | ||||
"file_extension": ".py", | ||||
"mimetype": "text/x-python", | ||||
"name": "python", | ||||
"nbconvert_exporter": "python", | ||||
"pygments_lexer": "ipython3", | ||||
Jonathan Frederic
|
r20536 | "version": "3.4.3" | ||
Min RK
|
r20278 | } | ||
Min RK
|
r18669 | }, | ||
"nbformat": 4, | ||||
"nbformat_minor": 0 | ||||
Min RK
|
r20278 | } | ||