template.py
212 lines
| 7.3 KiB
| text/x-python
|
PythonLexer
/ converters / template.py
Matthias BUSSONNIER
|
r8994 | """Base classes for the notebook conversion pipeline. | ||
Matthias BUSSONNIER
|
r9526 | This module defines ConverterTemplate, a highly configurable converter | ||
that uses Jinja2 to convert notebook files into different format. | ||||
You can register both pre-transformers that will act on the notebook format | ||||
befor conversion and jinja filter that would then be availlable in the templates | ||||
Matthias BUSSONNIER
|
r8994 | """ | ||
#----------------------------------------------------------------------------- | ||||
Matthias BUSSONNIER
|
r9526 | # Copyright (c) 2013, the IPython Development Team. | ||
Matthias BUSSONNIER
|
r8994 | # | ||
# Distributed under the terms of the Modified BSD License. | ||||
# | ||||
# The full license is in the file COPYING.txt, distributed with this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
from __future__ import print_function, absolute_import | ||||
Matthias BUSSONNIER
|
r9526 | |||
# Stdlib imports | ||||
import io | ||||
# IPython imports | ||||
from IPython.utils.traitlets import MetaHasTraits | ||||
from IPython.utils.traitlets import (Unicode, List, Bool) | ||||
from IPython.config.configurable import Configurable | ||||
from IPython.nbformat import current as nbformat | ||||
# other libs/dependencies | ||||
from jinja2 import Environment, FileSystemLoader | ||||
# local import (pre-transformers) | ||||
Matthias BUSSONNIER
|
r9302 | import converters.transformers as trans | ||
Matthias BUSSONNIER
|
r9526 | |||
# some jinja filters | ||||
Matthias BUSSONNIER
|
r9303 | from converters.jinja_filters import (python_comment, indent, | ||
rm_fake, remove_ansi, markdown, highlight, | ||||
Matthias BUSSONNIER
|
r9424 | ansi2html, markdown2latex, escape_tex, FilterDataType) | ||
Matthias BUSSONNIER
|
r9302 | |||
Matthias BUSSONNIER
|
r9333 | from converters.utils import markdown2rst | ||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9307 | |||
Matthias BUSSONNIER
|
r9490 | |||
Matthias BUSSONNIER
|
r9526 | # define differents environemnt with different | ||
# delimiters not to conflict with languages inside | ||||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9049 | env = Environment( | ||
Matthias BUSSONNIER
|
r9230 | loader=FileSystemLoader([ | ||
'./templates/', | ||||
'./templates/skeleton/', | ||||
]), | ||||
Matthias BUSSONNIER
|
r9049 | extensions=['jinja2.ext.loopcontrols'] | ||
) | ||||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9212 | texenv = Environment( | ||
Matthias BUSSONNIER
|
r9230 | loader=FileSystemLoader([ | ||
'./templates/tex/', | ||||
'./templates/skeleton/tex/', | ||||
]), | ||||
Matthias BUSSONNIER
|
r9212 | extensions=['jinja2.ext.loopcontrols'] | ||
) | ||||
texenv.block_start_string = '((*' | ||||
texenv.block_end_string = '*))' | ||||
Matthias BUSSONNIER
|
r9424 | |||
Matthias BUSSONNIER
|
r9212 | texenv.variable_start_string = '(((' | ||
texenv.variable_end_string = ')))' | ||||
Matthias BUSSONNIER
|
r9424 | |||
Matthias BUSSONNIER
|
r9212 | texenv.comment_start_string = '((=' | ||
texenv.comment_end_string = '=))' | ||||
Matthias BUSSONNIER
|
r9424 | |||
Matthias BUSSONNIER
|
r9212 | texenv.filters['escape_tex'] = escape_tex | ||
Matthias BUSSONNIER
|
r9526 | #----------------------------------------------------------------------------- | ||
# Class declarations | ||||
#----------------------------------------------------------------------------- | ||||
class ConversionException(Exception): | ||||
pass | ||||
Matthias BUSSONNIER
|
r9218 | |||
Matthias BUSSONNIER
|
r8994 | class ConverterTemplate(Configurable): | ||
Matthias BUSSONNIER
|
r9332 | """ A Jinja2 base converter templates | ||
Preprocess the ipynb files, feed it throug jinja templates, | ||||
and spit an converted files and a data object with other data | ||||
shoudl be mostly configurable | ||||
""" | ||||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9332 | pre_transformer_order = List(['haspyout_transformer'], | ||
Matthias BUSSONNIER
|
r9237 | config=True, | ||
Matthias BUSSONNIER
|
r9304 | help= """ | ||
An ordered list of pre transformer to apply to the ipynb | ||||
file befor running through templates | ||||
Matthias BUSSONNIER
|
r9214 | """ | ||
) | ||||
Matthias BUSSONNIER
|
r9218 | |||
Matthias BUSSONNIER
|
r9234 | tex_environement = Bool(False, | ||
config=True, | ||||
help=""" is this a tex environment or not """) | ||||
template_file = Unicode('', | ||||
config=True, | ||||
Matthias BUSSONNIER
|
r9332 | help=""" Name of the template file to use """ ) | ||
Matthias BUSSONNIER
|
r8994 | #------------------------------------------------------------------------- | ||
# Instance-level attributes that are set in the constructor for this | ||||
# class. | ||||
#------------------------------------------------------------------------- | ||||
Matthias BUSSONNIER
|
r9214 | |||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9332 | preprocessors = [] | ||
def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw): | ||||
Matthias BUSSONNIER
|
r9424 | """ Init a new converter. | ||
Matthias BUSSONNIER
|
r9526 | config: the Configurable config object to pass around. | ||
Matthias BUSSONNIER
|
r9184 | |||
Matthias BUSSONNIER
|
r9424 | preprocessors: dict of **availlable** key/value function to run on | ||
Matthias BUSSONNIER
|
r9526 | ipynb json data before conversion to extract/inline file. | ||
See `transformer.py` and `ConfigurableTransformers` | ||||
Matthias BUSSONNIER
|
r9424 | |||
Matthias BUSSONNIER
|
r9526 | set the order in which the transformers should apply | ||
with the `pre_transformer_order` trait of this class | ||||
Matthias BUSSONNIER
|
r9184 | |||
Matthias BUSSONNIER
|
r9526 | transformers registerd by this key will take precedence on | ||
default one. | ||||
jinja_filters: dict of supplementary jinja filter that should be made | ||||
availlable in template. If those are of Configurable Class type, | ||||
they will be instanciated with the config object as argument. | ||||
user defined filter will overwrite the one availlable by default. | ||||
Matthias BUSSONNIER
|
r9184 | """ | ||
Matthias BUSSONNIER
|
r9229 | super(ConverterTemplate, self).__init__(config=config, **kw) | ||
Matthias BUSSONNIER
|
r9526 | |||
# variable parameters depending on the pype of jinja environement | ||||
Matthias BUSSONNIER
|
r9234 | self.env = texenv if self.tex_environement else env | ||
self.ext = '.tplx' if self.tex_environement else '.tpl' | ||||
Matthias BUSSONNIER
|
r9307 | |||
Matthias BUSSONNIER
|
r9302 | for name in self.pre_transformer_order: | ||
Matthias BUSSONNIER
|
r9526 | # get the user-defined transformer first | ||
Matthias BUSSONNIER
|
r9332 | transformer = getattr(preprocessors, name, getattr(trans, name, None)) | ||
if isinstance(transformer, MetaHasTraits): | ||||
transformer = transformer(config=config) | ||||
self.preprocessors.append(transformer) | ||||
## for compat, remove later | ||||
Matthias BUSSONNIER
|
r9492 | self.preprocessors.append(trans.coalesce_streams) | ||
Matthias BUSSONNIER
|
r9424 | self.preprocessors.append(trans.ExtractFigureTransformer(config=config)) | ||
Matthias BUSSONNIER
|
r9401 | self.preprocessors.append(trans.RevealHelpTransformer(config=config)) | ||
Matthias BUSSONNIER
|
r9490 | self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config)) | ||
Matthias BUSSONNIER
|
r9229 | |||
Matthias BUSSONNIER
|
r9332 | ## | ||
Matthias BUSSONNIER
|
r9424 | self.env.filters['filter_data_type'] = FilterDataType(config=config) | ||
Matthias BUSSONNIER
|
r9229 | self.env.filters['pycomment'] = python_comment | ||
self.env.filters['indent'] = indent | ||||
self.env.filters['rm_fake'] = rm_fake | ||||
self.env.filters['rm_ansi'] = remove_ansi | ||||
self.env.filters['markdown'] = markdown | ||||
self.env.filters['highlight'] = highlight | ||||
self.env.filters['ansi2html'] = ansi2html | ||||
self.env.filters['markdown2latex'] = markdown2latex | ||||
Matthias BUSSONNIER
|
r9333 | self.env.filters['markdown2rst'] = markdown2rst | ||
Matthias BUSSONNIER
|
r9526 | |||
## user filter will overwrite | ||||
Matthias BUSSONNIER
|
r9424 | for key, filtr in jinja_filters.iteritems(): | ||
if isinstance(filtr, MetaHasTraits): | ||||
self.env.filters[key] = filtr(config=config) | ||||
else : | ||||
self.env.filters[key] = filtr | ||||
Matthias BUSSONNIER
|
r9229 | |||
Matthias BUSSONNIER
|
r9234 | self.template = self.env.get_template(self.template_file+self.ext) | ||
Matthias BUSSONNIER
|
r9214 | |||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9332 | def process(self, nb): | ||
Matthias BUSSONNIER
|
r9209 | """ | ||
Matthias BUSSONNIER
|
r9218 | preprocess the notebook json for easier use with the templates. | ||
will call all the `preprocessor`s in order before returning it. | ||||
Matthias BUSSONNIER
|
r9209 | """ | ||
Matthias BUSSONNIER
|
r9184 | |||
Matthias BUSSONNIER
|
r9229 | # dict of 'resources' that could be made by the preprocessors | ||
# like key/value data to extract files from ipynb like in latex conversion | ||||
resources = {} | ||||
Matthias BUSSONNIER
|
r9184 | for preprocessor in self.preprocessors: | ||
Matthias BUSSONNIER
|
r9303 | nb, resources = preprocessor(nb, resources) | ||
Matthias BUSSONNIER
|
r9184 | |||
Matthias BUSSONNIER
|
r9229 | return nb, resources | ||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9332 | def convert(self, nb): | ||
Matthias BUSSONNIER
|
r9182 | """ convert the ipynb file | ||
return both the converted ipynb file and a dict containing potential | ||||
other resources | ||||
""" | ||||
Matthias BUSSONNIER
|
r9332 | nb, resources = self.process(nb) | ||
Matthias BUSSONNIER
|
r9490 | return self.template.render(nb=nb, resources=resources), resources | ||
Matthias BUSSONNIER
|
r8994 | |||
Matthias BUSSONNIER
|
r9332 | def from_filename(self, filename): | ||
Matthias BUSSONNIER
|
r8994 | "read and parse notebook into NotebookNode called self.nb" | ||
Matthias BUSSONNIER
|
r9014 | with io.open(filename) as f: | ||
Matthias BUSSONNIER
|
r9332 | return self.convert(nbformat.read(f, 'json')) | ||
Matthias BUSSONNIER
|
r9307 | |||
Matthias BUSSONNIER
|
r9424 | |||
Matthias BUSSONNIER
|
r9490 | |||