template.py
261 lines
| 8.9 KiB
| text/x-python
|
PythonLexer
/ converters / template.py
|
r9578 | """Base classes for the notebook conversion pipeline. | ||
|
r9665 | 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 | ||||
|
r9578 | """ | ||
#----------------------------------------------------------------------------- | ||||
|
r9665 | # Copyright (c) 2013, the IPython Development Team. | ||
|
r9578 | # | ||
# 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 | ||||
|
r9665 | |||
# 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 | ||||
|
r9701 | |||
|
r9665 | # other libs/dependencies | ||
from jinja2 import Environment, FileSystemLoader | ||||
# local import (pre-transformers) | ||||
|
r9621 | import converters.transformers as trans | ||
|
r9665 | |||
# some jinja filters | ||||
|
r9622 | from converters.jinja_filters import (python_comment, indent, | ||
rm_fake, remove_ansi, markdown, highlight, | ||||
|
r9650 | ansi2html, markdown2latex, escape_tex, FilterDataType) | ||
|
r9621 | |||
|
r9641 | from converters.utils import markdown2rst | ||
|
r9578 | |||
|
r9701 | import textwrap | ||
def wrap(text, width=100): | ||||
""" try to detect and wrap paragraph""" | ||||
splitt = text.split('\n') | ||||
wrp = map(lambda x:textwrap.wrap(x,width),splitt) | ||||
wrpd = map('\n'.join, wrp) | ||||
return '\n'.join(wrpd) | ||||
|
r9624 | |||
|
r9654 | |||
|
r9665 | # define differents environemnt with different | ||
# delimiters not to conflict with languages inside | ||||
|
r9578 | |||
|
r9599 | env = Environment( | ||
|
r9616 | loader=FileSystemLoader([ | ||
'./templates/', | ||||
'./templates/skeleton/', | ||||
]), | ||||
|
r9599 | extensions=['jinja2.ext.loopcontrols'] | ||
) | ||||
|
r9578 | |||
|
r9609 | texenv = Environment( | ||
|
r9616 | loader=FileSystemLoader([ | ||
'./templates/tex/', | ||||
'./templates/skeleton/tex/', | ||||
]), | ||||
|
r9609 | extensions=['jinja2.ext.loopcontrols'] | ||
) | ||||
texenv.block_start_string = '((*' | ||||
texenv.block_end_string = '*))' | ||||
|
r9650 | |||
|
r9609 | texenv.variable_start_string = '(((' | ||
texenv.variable_end_string = ')))' | ||||
|
r9650 | |||
|
r9609 | texenv.comment_start_string = '((=' | ||
texenv.comment_end_string = '=))' | ||||
|
r9650 | |||
|
r9609 | texenv.filters['escape_tex'] = escape_tex | ||
|
r9665 | #----------------------------------------------------------------------------- | ||
# Class declarations | ||||
#----------------------------------------------------------------------------- | ||||
class ConversionException(Exception): | ||||
pass | ||||
|
r9614 | |||
|
r9578 | class ConverterTemplate(Configurable): | ||
|
r9640 | """ 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 | ||||
""" | ||||
|
r9578 | |||
|
r9768 | display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'], | ||
config=True, | ||||
help= """ | ||||
An ordered list of prefered output type, the first | ||||
encounterd will usually be used when converting discarding | ||||
the others. | ||||
""" | ||||
) | ||||
|
r9640 | pre_transformer_order = List(['haspyout_transformer'], | ||
|
r9619 | config=True, | ||
|
r9623 | help= """ | ||
An ordered list of pre transformer to apply to the ipynb | ||||
file befor running through templates | ||||
|
r9611 | """ | ||
) | ||||
|
r9614 | |||
|
r9768 | extract_figures = Bool(False, | ||
config=True, | ||||
help= """ | ||||
wether to remove figure data from ipynb and store them in auxiliary | ||||
dictionnary | ||||
""" | ||||
) | ||||
|
r9618 | tex_environement = Bool(False, | ||
config=True, | ||||
help=""" is this a tex environment or not """) | ||||
template_file = Unicode('', | ||||
config=True, | ||||
|
r9640 | help=""" Name of the template file to use """ ) | ||
|
r9578 | #------------------------------------------------------------------------- | ||
# Instance-level attributes that are set in the constructor for this | ||||
# class. | ||||
#------------------------------------------------------------------------- | ||||
|
r9768 | infile = Any() | ||
|
r9611 | |||
|
r9578 | |||
|
r9768 | infile_dir = Unicode() | ||
#todo: move to filter | ||||
def filter_data_type(self, output): | ||||
""" return the first availlable format in priority """ | ||||
for fmt in self.display_data_priority: | ||||
if fmt in output: | ||||
return [fmt] | ||||
raise Exception("did not found any format I can extract in output, shoudl at lest have one") | ||||
|
r9640 | preprocessors = [] | ||
def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw): | ||||
|
r9768 | """ | ||
config: the Configurable config object to pass around. | ||||
|
r9603 | |||
|
r9650 | preprocessors: dict of **availlable** key/value function to run on | ||
|
r9665 | ipynb json data before conversion to extract/inline file. | ||
See `transformer.py` and `ConfigurableTransformers` | ||||
|
r9650 | |||
|
r9665 | set the order in which the transformers should apply | ||
with the `pre_transformer_order` trait of this class | ||||
|
r9603 | |||
|
r9665 | 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. | ||||
|
r9603 | """ | ||
|
r9615 | super(ConverterTemplate, self).__init__(config=config, **kw) | ||
|
r9665 | |||
# variable parameters depending on the pype of jinja environement | ||||
|
r9618 | self.env = texenv if self.tex_environement else env | ||
self.ext = '.tplx' if self.tex_environement else '.tpl' | ||||
|
r9624 | |||
|
r9621 | for name in self.pre_transformer_order: | ||
|
r9665 | # get the user-defined transformer first | ||
|
r9640 | transformer = getattr(preprocessors, name, getattr(trans, name, None)) | ||
if isinstance(transformer, MetaHasTraits): | ||||
transformer = transformer(config=config) | ||||
self.preprocessors.append(transformer) | ||||
## for compat, remove later | ||||
|
r9656 | self.preprocessors.append(trans.coalesce_streams) | ||
|
r9650 | self.preprocessors.append(trans.ExtractFigureTransformer(config=config)) | ||
|
r9644 | self.preprocessors.append(trans.RevealHelpTransformer(config=config)) | ||
|
r9654 | self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config)) | ||
|
r9615 | |||
|
r9640 | ## | ||
|
r9650 | self.env.filters['filter_data_type'] = FilterDataType(config=config) | ||
|
r9615 | 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 | ||||
|
r9641 | self.env.filters['markdown2rst'] = markdown2rst | ||
|
r9701 | self.env.filters['wrap'] = wrap | ||
|
r9665 | |||
## user filter will overwrite | ||||
|
r9650 | for key, filtr in jinja_filters.iteritems(): | ||
if isinstance(filtr, MetaHasTraits): | ||||
self.env.filters[key] = filtr(config=config) | ||||
else : | ||||
self.env.filters[key] = filtr | ||||
|
r9615 | |||
|
r9618 | self.template = self.env.get_template(self.template_file+self.ext) | ||
|
r9611 | |||
|
r9578 | |||
|
r9640 | def process(self, nb): | ||
|
r9607 | """ | ||
|
r9614 | preprocess the notebook json for easier use with the templates. | ||
will call all the `preprocessor`s in order before returning it. | ||||
|
r9607 | """ | ||
|
r9603 | |||
|
r9615 | # dict of 'resources' that could be made by the preprocessors | ||
# like key/value data to extract files from ipynb like in latex conversion | ||||
resources = {} | ||||
|
r9603 | for preprocessor in self.preprocessors: | ||
|
r9622 | nb, resources = preprocessor(nb, resources) | ||
|
r9603 | |||
|
r9615 | return nb, resources | ||
|
r9578 | |||
|
r9640 | def convert(self, nb): | ||
|
r9601 | """ convert the ipynb file | ||
return both the converted ipynb file and a dict containing potential | ||||
other resources | ||||
""" | ||||
|
r9640 | nb, resources = self.process(nb) | ||
|
r9654 | return self.template.render(nb=nb, resources=resources), resources | ||
|
r9578 | |||
|
r9640 | def from_filename(self, filename): | ||
|
r9666 | """read and convert a notebook from a file name""" | ||
|
r9589 | with io.open(filename) as f: | ||
|
r9640 | return self.convert(nbformat.read(f, 'json')) | ||
|
r9624 | |||
|
r9666 | def from_file(self, filelike): | ||
"""read and convert a notebook from a filelike object | ||||
|
r9650 | |||
|
r9666 | filelike object will just be "read" and should be json format.. | ||
""" | ||||
return self.convert(nbformat.read(filelike, 'json')) | ||||
def from_json(self, json): | ||||
""" not implemented | ||||
Should convert from a json object | ||||
""" | ||||
raise NotImplementedError('not implemented (yet?)') | ||||
|
r9654 | |||