{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Customizing nbconvert" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Under the hood, nbconvert uses [Jinja templates](https://jinja2.readthedocs.io/en/latest/intro.html) to specify how the notebooks should be formatted. These templates can be fully customized, allowing you to use nbconvert to create notebooks in different formats with different styles as well." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Converting a notebook to an (I)Python script and printing to stdout\n", "\n", "Out of the box, nbconvert can be used to convert notebooks to plain Python files. For example, the following command converts the example.ipynb notebook to Python and prints out the result:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[NbConvertApp] Converting notebook example.ipynb to python\n", "\n", "# coding: utf-8\n", "\n", "# # Example notebook\n", "\n", "# ### Markdown cells\n", "# \n", "# This is an example notebook that can be converted with nbconvert to different formats. This is an example of a markdown cell.\n", "\n", "# ### LaTeX Equations\n", "# \n", "# Here is an equation:\n", "# \n", "# $$\n", "# y = \\sin(x)\n", "#$$\n", "\n", "# ### Code cells\n", "\n", "# In[1]:\n", "\n", "\n", "print(\"This is a code cell that produces some output\")\n", "\n", "\n", "# ### Inline figures\n", "\n", "# In[1]:\n", "\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "plt.ion()\n", "\n", "x = np.linspace(0, 2 * np.pi, 100)\n", "y = np.sin(x)\n", "plt.plot(x, y)\n", "\n" ] } ], "source": [ "!jupyter nbconvert --to python 'example.ipynb' --stdout" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the code, you can see that non-code cells are also exported. If you wanted to change that behaviour, you would first look to nbconvert [configuration options page](./config_options.rst) to see if there is an option available that can give you your desired behaviour. \n", "\n", "In this case, if you wanted to remove code cells from the output, you could use the TemplateExporter.exclude_markdown traitlet directly, as below. " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[NbConvertApp] Converting notebook example.ipynb to python\n", "\n", "# coding: utf-8\n", "\n", "# In[1]:\n", "\n", "\n", "print(\"This is a code cell that produces some output\")\n", "\n", "\n", "# In[1]:\n", "\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "plt.ion()\n", "\n", "x = np.linspace(0, 2 * np.pi, 100)\n", "y = np.sin(x)\n", "plt.plot(x, y)\n", "\n" ] } ], "source": [ "!jupyter nbconvert --to python 'example.ipynb' --stdout --TemplateExporter.exclude_markdown=True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Custom Templates \n", "\n", "As mentioned above, if you want to change this behavior, you can use a custom template. The custom template inherits from the Python template and overwrites the markdown blocks so that they are empty. \n", "\n", "Below is an example of a custom template, which we write to a file called simplepython.tpl. This template removes markdown cells from the output, and also changes how the execution count numbers are formatted:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting simplepython.tpl\n" ] } ], "source": [ "%%writefile simplepython.tpl\n", "\n", "{% extends 'python.tpl'%}\n", "\n", "## remove markdown cells\n", "{% block markdowncell -%}\n", "{% endblock markdowncell %}\n", "\n", "## change the appearance of execution count\n", "{% block in_prompt %}\n", "# [{{ cell.execution_count if cell.execution_count else ' ' }}]:\n", "{% endblock in_prompt %}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using this template, we see that the resulting Python code does not contain anything that was previously in a markdown cell, and only displays execution counts (i.e., [#]: not In[#]:):" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[NbConvertApp] Converting notebook example.ipynb to python\n", "\n", "\n", "# coding: utf-8\n", "\n", "# [1]:\n", "\n", "print(\"This is a code cell that produces some output\")\n", "\n", "\n", "# [1]:\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "plt.ion()\n", "\n", "x = np.linspace(0, 2 * np.pi, 100)\n", "y = np.sin(x)\n", "plt.plot(x, y)\n", "\n" ] } ], "source": [ "!jupyter nbconvert --to python 'example.ipynb' --stdout --template=simplepython.tpl" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Template structure\n", "\n", "Nbconvert templates consist of a set of nested blocks. When defining a new\n", "template, you extend an existing template by overriding some of the blocks.\n", "\n", "All the templates shipped in nbconvert have the basic structure described here,\n", "though some may define additional blocks." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "
\n", "

### Main page

\n", "
\n", "\n", "
body\n", "
any_cell\n", "
codecell\n", "
input_group\n", "
in_prompt
\n", "
input
\n", "
\n", "
output_group\n", "
output_prompt
\n", "
outputs (see below)
\n", "
\n", "
\n", "
\n", "
any_cell\n", "
markdowncell
\n", "
\n", "
any_cell\n", "
rawcell
\n", "
\n", "
any_cell\n", "
unknowncell
\n", "
\n", "
\n", "
\n", "\n", "
footer
\n", "\n", "

### Outputs

\n", "\n", "
outputs\n", "
output\n", "
execute_result
\n", "
\n", "
output\n", "
stream_stdout
\n", "
\n", "
output\n", "
stream_stderr
\n", "
\n", "
output\n", "
display_data\n", "
data_priority\n", "
data_pdf / data_svg / data_png /\n", " data_html / data_markdown / data_jpg / data_text /\n", " data_latex / data_javascript / data_other\n", "
\n", "
\n", "
\n", "
\n", "
output\n", "
error\n", "
traceback_line
\n", "
\n", "
\n", "
\n", "
\n", "
\n", "\n", "

### Extra HTML blocks

\n", "

#### basic.tpl

\n", "
output\n", "
output_area_prompt
\n", "
output (as above)
\n", "
\n", "

#### full.tpl

\n", "
<head>
\n", "
\n", "
</head>
\n", "
\n", "\n", "

### Extra Latex blocks

\n", "
docclass
\n", "
packages
\n", "
definitions\n", "
title
\n", "
date
\n", "
author
\n", "
\n", "
commands\n", "
margins
\n", "
\n", "
\n", "
body\n", "
predoc\n", "
maketitle
\n", "
abstract
\n", "
\n", " ... other fields as above ...\n", "
postdoc\n", "
bibliography
\n", "
\n", "
\n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from IPython.display import HTML, display\n", "with open('template_structure.html') as f:\n", " display(HTML(f.read()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A few gotchas\n", "\n", "Jinja blocks use {% %} by default which does not play nicely with LaTeX, so those are replaced by ((* *)) in LaTeX templates." ] }, { "cell_type": "markdown", "metadata": { "tags": [ "Hard" ] }, "source": [ "## Templates using cell tags\n", "\n", "The notebook file format supports attaching arbitrary JSON metadata to each cell. In addition, every cell has a special tags metadata field that accepts a list of strings that indicate the cell's tags. To apply these, go to the View → CellToolbar → Tags option which will create a Tag editor at the top of every cell. \n", "\n", "First choose a notebook you want to convert to html, and apply the tags: \"Easy\", \"Medium\", or \n", "\"Hard\". \n", "\n", "With this in place, the notebook can be converted using a custom template.\n", "\n", "Design your template in the cells provided below.\n", "\n", "Hint: tags are located at cell.metadata.tags, the following Python code collects the value of the tag: \n", "\n", "python\n", "cell['metadata'].get('tags', [])\n", "\n", "\n", "Which you can then use inside a Jinja template as in the following:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting mytemplate.tpl\n" ] } ], "source": [ "%%writefile mytemplate.tpl\n", "\n", "{% extends 'full.tpl'%}\n", "{% block any_cell %}\n", "{% if 'Hard' in cell['metadata'].get('tags', []) %}\n", "
\n", " {{ super() }}\n", "
\n", "{% elif 'Medium' in cell['metadata'].get('tags', []) %}\n", "
\n", " {{ super() }}\n", "
\n", "{% elif 'Easy' in cell['metadata'].get('tags', []) %}\n", "
\n", " {{ super() }}\n", "
\n", "{% else %}\n", " {{ super() }}\n", "{% endif %}\n", "{% endblock any_cell %}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, if we collect the result of using nbconvert with this template, and display the resulting html, we see the following:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "example\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", "\n", "
\n", "
\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "

# Example notebook¶

\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "

### Markdown cells¶

This is an example notebook that can be converted with nbconvert to different formats. This is an example of a markdown cell.

\n", "\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", "

### LaTeX Equations¶

Here is an equation:

\n", "$$\n", "y = \\sin(x)\n", "$$\n", "
\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "

### Code cells¶

\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", "
\n", " \n", "
\n", "
\n", "
In [1]:
\n", "
\n", "
\n", "
print("This is a code cell that produces some output")\n",
"
\n", "\n", "
\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "
\n", "\n", "
\n", "\n", "\n", "
\n", "
This is a code cell that produces some output\n",
"
\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "

### Inline figures¶

\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", "
\n", " \n", "
\n", "
\n", "
In [1]:
\n", "
\n", "
\n", "
import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"plt.ion()\n",
"\n",
"x = np.linspace(0, 2 * np.pi, 100)\n",
"y = np.sin(x)\n",
"plt.plot(x, y)\n",
"
\n", "\n", "
\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "
\n", "\n", "
Out[1]:
\n", "\n", "\n", "\n", "\n", "
\n", "
[<matplotlib.lines.Line2D at 0x1111b2160>]
\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\n", "\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "
\n", "
\n", "\n", "\n", " \n", "\n", "\n", "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "example = !jupyter nbconvert --to html 'example.ipynb' --template=mytemplate.tpl --stdout\n", "example = example[3:] # have to remove the first three lines which are not proper html\n", "from IPython.display import HTML, display\n", "display(HTML('\\n'.join(example))) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Templates using custom cell metadata \n", "\n", "We demonstrated [above](#Templates-using-cell-tags) how to use cell tags in a template to apply custom styling to a notebook. But remember, the notebook file format supports attaching _arbitrary_ JSON metadata to each cell, not only cell tags. \n", "Here, we describe an exercise for using an example.difficulty metadata field (rather than cell tags) to do the same as before (to mark up different cells as being \"Easy\", \"Medium\" or \"Hard\").\n", "\n", "### How to edit cell metadata\n", "\n", "To edit the cell metadata from within the notebook, go to the menu item: View → Cell Toolbar → Edit Metadata. This will bring up a toolbar above each cell with a button that says \"Edit Metadata\". Click this button, and a field will pop up in which you will directly edit the cell metadata JSON. \n", "\n", "**NB**: Because it is JSON, you will need to ensure that what you write is valid JSON. \n", "\n", "### Template challenges: dealing with missing custom metadata fields\n", "\n", "One of the challenges of dealing with custom metadata is to handle the case where the metadata is not present on every cell. This can get somewhat tricky because of JSON objects tendency to be deeply nested coupled with Python's (and therefore Jinja's) approach to calling into dictionaries. Specifically, the following code will error:\n", "\n", "python\n", "foo = {}\n", "foo[\"bar\"]\n", "\n", "\n", "Accordingly, it is better to use the [{}.get method](https://docs.python.org/3.6/library/stdtypes.html#dict.get) which allows you to set a default value to return if no key is found as the second argument. \n", "\n", "Hint: if your metadata items are located at cell.metadata.example.difficulty, the following Python code would get the value defaulting to an empty string ('') if nothing is found:\n", "\n", "python\n", "cell['metadata'].get('example', {}).get('difficulty', '')\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercise: Write a template for handling custom metadata\n", "Now, write a template that will look for Easy, Medium and Hard metadata values for the cell.metadata.example.difficulty field and wrap them in a div with a green, orange, or red thin solid border (respectively). \n", "\n", "**NB**: This is the same design and logic as used in the previous cell tag example.\n", "\n", "#### How to get example.ipynb\n", "\n", "We have provided an example file in example.ipynb in the nbconvert documentation that has already been marked up with both tags and the above metadata for you to test with. You can get it from [this link to the raw file]( https://raw.githubusercontent.com/jupyter/nbconvert/master/docs/source/example.ipynb) or by cloning the repository [from GitHub](https://github.com/jupyter/nbconvert) and navingating to nbconvert/docs/source/example.ipynb. \n", "\n", "#### Convert example.ipynb using cell tags \n", "\n", "First, make sure that you can reproduce the previous result using the cell tags template that we have provided above. \n", "\n", "**Easy**: If you want to make it easy on yourself, create a new file my_template.tpl in the same directory as example.ipynb and copy the contents of the cell we use to write mytemplate.tpl to the file system. \n", "\n", "Then run jupyter nbconvert --to html 'example.ipynb' --template=mytemplate.tpl and see if your \n", "\n", "**Moderate**: If you want more of a challenge, try recreating the jinja template by modifying the following jinja template file:\n", "\n", "python\n", "{% extends 'full.tpl'%}\n", "{% block any_cell %}\n", "
\n", " {{ super() }}\n", "
\n", "{% endblock any_cell %}\n", "\n", "\n", "**Hard**: If you want even more of a challenge, try recreating the jinja template from scratch. \n", "\n", "#### Write your template\n", "\n", "Once you've done at least the **Easy** version of the previous step, try modifying your template to use cell.metadata.example.difficulty fields rather than cell tags. \n", "\n", "#### Convert example.ipynb with formatting from custom metadata\n", "\n", "Once you've written your template, try converting example.ipynb using the following command (making sure that your_template.tpl is in your local directory where you are running the command):\n", "\n", "bash\n", "jupyter nbconvert --to html 'example.ipynb' --template=your_template.tpl --stdout\n", "\n", "\n", "The resulting display should pick out different cells to be bordered with green, orange, or red.\n", "\n", "If you do that successfullly, the resulting html document should look like the following cell's contents: " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "example\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", "\n", "
\n", "
\n", "\n", "\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", "
\n", "

# Example notebook¶

\n", "
\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "
\n", "

### Markdown cells¶

This is an example notebook that can be converted with nbconvert to different formats. This is an example of a markdown cell.

\n", "\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "
\n", "

### LaTeX Equations¶

Here is an equation:

\n", "$$\n", "y = \\sin(x)\n", "$$\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", "
\n", " \n", "
\n", "
\n", "
\n", "
\n", "
\n", "

### Code cells¶

\n", "
\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
In [1]:
\n", "
\n", "
\n", "
print("This is a code cell that produces some output")\n",
"
\n", "\n", "
\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "
\n", "\n", "
\n", "\n", "\n", "
\n", "
This is a code cell that produces some output\n",
"
\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n", "
\n", "
\n", "

### Inline figures¶

\n", "
\n", "
\n", "
\n", "\n", "\n", "\n", "
\n", " \n", "
\n", "
\n", "
In [1]:
\n", "
\n", "
\n", "
import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"plt.ion()\n",
"\n",
"x = np.linspace(0, 2 * np.pi, 100)\n",
"y = np.sin(x)\n",
"plt.plot(x, y)\n",
"
\n", "\n", "
\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "
\n", "\n", "
Out[1]:
\n", "\n", "\n", "\n", "\n", "
\n", "
[<matplotlib.lines.Line2D at 0x1111b2160>]
\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "\n", "\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "\n", "
\n", "
\n", "\n", "
\n", "
\n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
In [ ]:
\n", "
\n", "
\n", "
 \n",
"
\n", "\n", "
\n", "
\n", "
\n", "\n", "
\n", "\n", "\n", "
\n", "
\n", "\n", "\n", " \n", "\n", "\n", "" ] } ], "metadata": { "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", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 1 }