Skip to content

Instantly share code, notes, and snippets.

@bj0
Created April 9, 2013 05:55
Show Gist options
  • Save bj0/5343292 to your computer and use it in GitHub Desktop.
Save bj0/5343292 to your computer and use it in GitHub Desktop.
IPython Custom Cell Magic for Rendering Jinja2 Templates
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "jinja magic"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"IPython Custom Cell Magic for Rendering Jinja2 Templates"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After watching some of the [PyCon 2013 videos](http://pyvideo.org/category/33/pycon-us-2013) on IPython, I felt inspired, as always, to play once more with IPython. Since I had just recently learned to use Jinja2, I thought it would be cool if I could test some jinja2 template rendering in the IPython notebook.\n",
"\n",
"According to a post on the IPython mailing list, unfortunately, jinja2 rendering is not supported in the markdown cells (which would be really neat) and probably will not be, because it is too Python specific. This means I am restricted rendering input cells and displaying the result in output cells.\n",
"\n",
"It would be simple enough to just import jinja2 and render a string, but I wanted to make it a little nicer, so I looked up the documentation on defining your own magic functions. Turns out that it's pretty simple. There's an example in the IPython docs that I used as a starting point to create the following class."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from IPython import display\n",
"from IPython.core.magic import register_cell_magic, Magics, magics_class, cell_magic\n",
"import jinja2\n",
"\n",
"@magics_class\n",
"class JinjaMagics(Magics):\n",
" '''Magics class containing the jinja2 magic and state'''\n",
" \n",
" def __init__(self, shell):\n",
" super(JinjaMagics, self).__init__(shell)\n",
" \n",
" # create a jinja2 environment to use for rendering\n",
" # this can be modified for desired effects (ie: using different variable syntax)\n",
" self.env = jinja2.Environment(loader=jinja2.FileSystemLoader('.'))\n",
" \n",
" # possible output types\n",
" self.display_functions = dict(html=display.HTML, \n",
" latex=display.Latex,\n",
" json=display.JSON,\n",
" pretty=display.Pretty,\n",
" display=display.display)\n",
"\n",
" \n",
" @cell_magic\n",
" def jinja(self, line, cell):\n",
" '''\n",
" jinja2 cell magic function. Contents of cell are rendered by jinja2, and \n",
" the line can be used to specify output type.\n",
"\n",
" ie: \"%%jinja html\" will return the rendered cell wrapped in an HTML object.\n",
" '''\n",
" f = self.display_functions.get(line.lower().strip(), display.display)\n",
" \n",
" tmp = self.env.from_string(cell)\n",
" rend = tmp.render(dict((k,v) for (k,v) in self.shell.user_ns.items() \n",
" if not k.startswith('_') and k not in self.shell.user_ns_hidden))\n",
" \n",
" return f(rend)\n",
" \n",
" \n",
"ip = get_ipython()\n",
"ip.register_magics(JinjaMagics)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The class creates a simple jinja2 environment with the FileSystemLoader, so template files can be imported/extended, and defines a function used to register a cell tagged with the cellmagic \"%% jinja <output>\". The output specifier is optional, and will return the rendered text wrapped in one of IPython's rich display objects\n",
"\n",
"The local (non-hidden) namespace is used for rendering, so any variables or functions defined in the IPython notebook can be accessed."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"names = ['alice','bob']"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here is an example of rendering a simple HTML template and displaying it with the HTML object:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%jinja html\n",
"<html>\n",
"<head>\n",
"<title>{{ title }}</title>\n",
"</head>\n",
"<body>\n",
"{% for name in names %}\n",
"\n",
"Hello {{ name }} <br/>\n",
"\n",
"{% endfor %} \n",
"</body>\n",
"</html>"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<html>\n",
"<head>\n",
"<title></title>\n",
"</head>\n",
"<body>\n",
"\n",
"\n",
"Hello alice <br/>\n",
"\n",
"\n",
"\n",
"Hello bob <br/>\n",
"\n",
" \n",
"</body>\n",
"</html>"
],
"output_type": "pyout",
"prompt_number": 7,
"text": [
"<IPython.core.display.HTML at 0x38e1210>"
]
}
],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A string with no template synax will simply be passed through as-is to the display object. The following will produce the same as using the %%latex cellmagic, but there are no built-in cellmagics for the other display objects."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%jinja latex\n",
"\n",
"\\begin{eqnarray}\n",
"\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\\n",
"\\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
"\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
"\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
"\\end{eqnarray}"
],
"language": "python",
"metadata": {},
"outputs": [
{
"latex": [
"\n",
"\\begin{eqnarray}\n",
"\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\\n",
"\\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n",
"\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n",
"\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n",
"\\end{eqnarray}"
],
"output_type": "pyout",
"prompt_number": 8,
"text": [
"<IPython.core.display.Latex at 0x38e1f50>"
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following is an example of using the %%jinja magic to generate and render some latex:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"vars = {'rho':5,'alpha':6,'pi':3.14,'phi':1.618,'hbar':'6.582121 \\cdot 10^{-16} eV\\cdot s'}"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 9
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%jinja latex\n",
"\n",
"\\begin{eqnarray}\n",
"\n",
"{% for k,v in vars.iteritems() %}\n",
"\\{{ k }} & = {{ v }} \\\\\n",
"{% endfor %}\n",
"\n",
"\\end{eqnarray}"
],
"language": "python",
"metadata": {},
"outputs": [
{
"latex": [
"\n",
"\\begin{eqnarray}\n",
"\n",
"\n",
"\\alpha & = 6 \\\\\n",
"\n",
"\\phi & = 1.618 \\\\\n",
"\n",
"\\pi & = 3.14 \\\\\n",
"\n",
"\\rho & = 5 \\\\\n",
"\n",
"\\hbar & = 6.582121 \\cdot 10^{-16} eV\\cdot s \\\\\n",
"\n",
"\n",
"\\end{eqnarray}"
],
"output_type": "pyout",
"prompt_number": 10,
"text": [
"<IPython.core.display.Latex at 0x38e1b90>"
]
}
],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I honestly thought it would be more difficult to extend IPython with my own magic function, but the IPython devs really know what they're doing. It has come a long long way since the first time I used it back around version 0.9, when it was simply an enhanced, interactive python terminal. What it has become now is pretty amazing."
]
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment