diff --git a/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb b/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb index d14cb4f50..e3f8bead0 100644 --- a/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb +++ b/docs/source/examples/Notebook/Distributing Jupyter Extensions as Python Packages.ipynb @@ -21,7 +21,9 @@ " [AMD modules](https://en.wikipedia.org/wiki/Asynchronous_module_definition)\n", " that exports a function `load_ipython_extension`\n", "- server extension: an importable Python module\n", - " - that implements `load_jupyter_server_extension`" + " - that implements `load_jupyter_server_extension`\n", + "- bundler extension: an importable Python module with generated File -> Download as / Deploy as menu item trigger\n", + " - that implements `bundle`" ] }, { @@ -105,11 +107,12 @@ "metadata": {}, "source": [ "## Did it work? Check by listing Jupyter Extensions.\n", - "After running one or more extension installation steps, you can list what is presently known about nbextensions or server extension. The following commands will list which extensions are available, whether they are enabled, and other extension details:\n", + "After running one or more extension installation steps, you can list what is presently known about nbextensions, server extensions, or bundler extensions. The following commands will list which extensions are available, whether they are enabled, and other extension details:\n", "\n", "```shell\n", "jupyter nbextension list\n", "jupyter serverextension list\n", + "jupyter bundlerextension list\n", "```" ] }, @@ -255,6 +258,98 @@ "jupyter serverextension enable --py my_fancy_module [--sys-prefix|--system]\n", "```" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example - Bundler extension" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating a Python package with a bundlerextension\n", + "\n", + "Here is a bundler extension that adds a *Download as -> Notebook Tarball (tar.gz)* option to the notebook *File* menu. It assumes this directory structure:\n", + "\n", + "```\n", + "- setup.py\n", + "- MANIFEST.in\n", + "- my_tarball_bundler/\n", + " - __init__.py\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Defining the bundler extension\n", + "\n", + "This example shows that the bundler extension and its `bundle` function are defined in the `__init__.py` file.\n", + "\n", + "#### `my_tarball_bundler/__init__.py`\n", + "\n", + "```python\n", + "import tarfile\n", + "import io\n", + "import os\n", + "import nbformat\n", + "\n", + "def _jupyter_bundlerextension_paths():\n", + " \"\"\"Declare bundler extensions provided by this package.\"\"\"\n", + " return [{\n", + " # unique bundler name\n", + " \"name\": \"tarball_bundler\",\n", + " # module containing bundle function\n", + " \"module_name\": \"my_tarball_bundler\",\n", + " # human-redable menu item label\n", + " \"label\" : \"Notebook Tarball (tar.gz)\",\n", + " # group under 'deploy' or 'download' menu\n", + " \"group\" : \"download\",\n", + " }]\n", + "\n", + "\n", + "def bundle(handler, model):\n", + " \"\"\"Create a compressed tarball containing the notebook document.\n", + " \n", + " Parameters\n", + " ----------\n", + " handler : tornado.web.RequestHandler\n", + " Handler that serviced the bundle request\n", + " model : dict\n", + " Notebook model from the configured ContentManager\n", + " \"\"\"\n", + " notebook_filename = model['name']\n", + " notebook_content = nbformat.writes(model['content']).encode('utf-8')\n", + " notebook_name = os.path.splitext(notebook_filename)[0]\n", + " tar_filename = '{}.tar.gz'.format(notebook_name)\n", + " \n", + " info = tarfile.TarInfo(notebook_filename)\n", + " info.size = len(notebook_content)\n", + "\n", + " with io.BytesIO() as tar_buffer:\n", + " with tarfile.open(tar_filename, \"w:gz\", fileobj=tar_buffer) as tar:\n", + " tar.addfile(info, io.BytesIO(notebook_content))\n", + " \n", + " # Set headers to trigger browser download\n", + " handler.set_header('Content-Disposition',\n", + " 'attachment; filename=\"{}\"'.format(tar_filename))\n", + " handler.set_header('Content-Type', 'application/gzip')\n", + " \n", + " # Return the buffer value as the response\n", + " handler.finish(tar_buffer.getvalue())\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "See [Extending the Notebook](../../extending) for more documentation about writing nbextensions, server extensions, and bundler extensions." + ] } ], "metadata": { @@ -277,5 +372,5 @@ } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/docs/source/extending/bundler_extensions.rst b/docs/source/extending/bundler_extensions.rst index 3136ff889..f4dc9f948 100644 --- a/docs/source/extending/bundler_extensions.rst +++ b/docs/source/extending/bundler_extensions.rst @@ -118,11 +118,12 @@ The output describes options for listing enabled bundlers, configuring bundlers Example: IPython Notebook bundle (.zip) --------------------------------------- -The `hello_bundler` example in this documentation is simplisitic in the name of brevity. For a more meaningful example, see the source in `notebook/bundler/zip_bundler.py`. It parses Markdown cells in the active notebook for gitignore-syntax file references, and bundles those files alongside the notebook in a zip download. You can enable it like so: +The `hello_bundler` example in this documentation is simplisitic in the name of brevity. For more meaningful examples, see `notebook/bundler/zip_bundler.py` and `notebook/bundler/tarball_bundler.py`. You can enable them to try them like so: .. code:: bash jupyter bundlerextension enable --py notebook.bundler.zip_bundler --sys-prefix + jupyter bundlerextension enable --py notebook.bundler.tarball_bundler --sys-prefix .. _bundler-details: diff --git a/notebook/bundler/tarball_bundler.py b/notebook/bundler/tarball_bundler.py new file mode 100644 index 000000000..e513dbf3b --- /dev/null +++ b/notebook/bundler/tarball_bundler.py @@ -0,0 +1,48 @@ +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. +import os +import io +import tarfile +import nbformat + +def _jupyter_bundlerextension_paths(): + """Metadata for notebook bundlerextension""" + return [{ + # unique bundler name + "name": "tarball_bundler", + # module containing bundle function + "module_name": "notebook.bundler.tarball_bundler", + # human-redable menu item label + "label" : "Notebook Tarball (tar.gz)", + # group under 'deploy' or 'download' menu + "group" : "download", + }] + +def bundle(handler, model): + """Create a compressed tarball containing the notebook document. + + Parameters + ---------- + handler : tornado.web.RequestHandler + Handler that serviced the bundle request + model : dict + Notebook model from the configured ContentManager + """ + notebook_filename = model['name'] + notebook_content = nbformat.writes(model['content']).encode('utf-8') + notebook_name = os.path.splitext(notebook_filename)[0] + tar_filename = '{}.tar.gz'.format(notebook_name) + + info = tarfile.TarInfo(notebook_filename) + info.size = len(notebook_content) + + with io.BytesIO() as tar_buffer: + with tarfile.open(tar_filename, "w:gz", fileobj=tar_buffer) as tar: + tar.addfile(info, io.BytesIO(notebook_content)) + + handler.set_header('Content-Disposition', + 'attachment; filename="{}"'.format(tar_filename)) + handler.set_header('Content-Type', 'application/gzip') + + # Return the buffer value as the response + handler.finish(tar_buffer.getvalue())