##// END OF EJS Templates
Added datetime access to Jinja
Jonathan Frederic -
Show More
@@ -1,234 +1,261 b''
1 """Base classes for the notebook conversion pipeline.
1 """Base classes for the notebook conversion pipeline.
2
2
3 This module defines ConverterTemplate, a highly configurable converter
3 This module defines ConverterTemplate, a highly configurable converter
4 that uses Jinja2 to convert notebook files into different format.
4 that uses Jinja2 to convert notebook files into different format.
5
5
6 You can register both pre-transformers that will act on the notebook format
6 You can register both pre-transformers that will act on the notebook format
7 befor conversion and jinja filter that would then be availlable in the templates
7 befor conversion and jinja filter that would then be availlable in the templates
8 """
8 """
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (c) 2013, the IPython Development Team.
10 # Copyright (c) 2013, the IPython Development Team.
11 #
11 #
12 # Distributed under the terms of the Modified BSD License.
12 # Distributed under the terms of the Modified BSD License.
13 #
13 #
14 # The full license is in the file COPYING.txt, distributed with this software.
14 # The full license is in the file COPYING.txt, distributed with this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 from __future__ import print_function, absolute_import
21 from __future__ import print_function, absolute_import
22
22
23 # Stdlib imports
23 # Stdlib imports
24 import io
24 import io
25
25
26 # IPython imports
26 # IPython imports
27 from IPython.utils.traitlets import MetaHasTraits
27 from IPython.utils.traitlets import MetaHasTraits
28 from IPython.utils.traitlets import (Unicode, List, Bool)
28 from IPython.utils.traitlets import (Unicode, List, Bool)
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.nbformat import current as nbformat
30 from IPython.nbformat import current as nbformat
31
31
32
32
33 # other libs/dependencies
33 # other libs/dependencies
34 from jinja2 import Environment, FileSystemLoader
34 from jinja2 import Environment, FileSystemLoader
35
35
36
36
37 # local import (pre-transformers)
37 # local import (pre-transformers)
38 import converters.transformers as trans
38 import converters.transformers as trans
39
39
40 # some jinja filters
40 # some jinja filters
41 from converters.jinja_filters import (python_comment, indent,
41 from converters.jinja_filters import (python_comment, indent,
42 rm_fake, remove_ansi, markdown, highlight,
42 rm_fake, remove_ansi, markdown, highlight,
43 ansi2html, markdown2latex, escape_tex, FilterDataType)
43 ansi2html, markdown2latex, escape_tex, FilterDataType)
44
44
45 from converters.utils import markdown2rst
45 from converters.utils import markdown2rst
46
46
47 import textwrap
47 import textwrap
48
48
49 def wrap(text, width=100):
49 def wrap(text, width=100):
50 """ try to detect and wrap paragraph"""
50 """ try to detect and wrap paragraph"""
51 splitt = text.split('\n')
51 splitt = text.split('\n')
52 wrp = map(lambda x:textwrap.wrap(x,width),splitt)
52 wrp = map(lambda x:textwrap.wrap(x,width),splitt)
53 wrpd = map('\n'.join, wrp)
53 wrpd = map('\n'.join, wrp)
54 return '\n'.join(wrpd)
54 return '\n'.join(wrpd)
55
55
56
56
57
57
58 # define differents environemnt with different
58 # define differents environemnt with different
59 # delimiters not to conflict with languages inside
59 # delimiters not to conflict with languages inside
60
60
61 env = Environment(
61 env = Environment(
62 loader=FileSystemLoader([
62 loader=FileSystemLoader([
63 './templates/',
63 './templates/',
64 './templates/skeleton/',
64 './templates/skeleton/',
65 ]),
65 ]),
66 extensions=['jinja2.ext.loopcontrols']
66 extensions=['jinja2.ext.loopcontrols']
67 )
67 )
68
68
69 texenv = Environment(
69 texenv = Environment(
70 loader=FileSystemLoader([
70 loader=FileSystemLoader([
71 './templates/tex/',
71 './templates/tex/',
72 './templates/skeleton/tex/',
72 './templates/skeleton/tex/',
73 ]),
73 ]),
74 extensions=['jinja2.ext.loopcontrols']
74 extensions=['jinja2.ext.loopcontrols']
75 )
75 )
76
76
77
77
78 texenv.block_start_string = '((*'
78 texenv.block_start_string = '((*'
79 texenv.block_end_string = '*))'
79 texenv.block_end_string = '*))'
80
80
81 texenv.variable_start_string = '((('
81 texenv.variable_start_string = '((('
82 texenv.variable_end_string = ')))'
82 texenv.variable_end_string = ')))'
83
83
84 texenv.comment_start_string = '((='
84 texenv.comment_start_string = '((='
85 texenv.comment_end_string = '=))'
85 texenv.comment_end_string = '=))'
86
86
87 texenv.filters['escape_tex'] = escape_tex
87 texenv.filters['escape_tex'] = escape_tex
88
88
89 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
90 # Class declarations
90 # Class declarations
91 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
92 class ConversionException(Exception):
92 class ConversionException(Exception):
93 pass
93 pass
94
94
95 class ConverterTemplate(Configurable):
95 class ConverterTemplate(Configurable):
96 """ A Jinja2 base converter templates
96 """ A Jinja2 base converter templates
97
97
98 Preprocess the ipynb files, feed it throug jinja templates,
98 Preprocess the ipynb files, feed it throug jinja templates,
99 and spit an converted files and a data object with other data
99 and spit an converted files and a data object with other data
100
100
101 shoudl be mostly configurable
101 shoudl be mostly configurable
102 """
102 """
103
103
104 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
105 config=True,
106 help= """
107 An ordered list of prefered output type, the first
108 encounterd will usually be used when converting discarding
109 the others.
110 """
111 )
112
104 pre_transformer_order = List(['haspyout_transformer'],
113 pre_transformer_order = List(['haspyout_transformer'],
105 config=True,
114 config=True,
106 help= """
115 help= """
107 An ordered list of pre transformer to apply to the ipynb
116 An ordered list of pre transformer to apply to the ipynb
108 file befor running through templates
117 file befor running through templates
109 """
118 """
110 )
119 )
111
120
121 extract_figures = Bool(False,
122 config=True,
123 help= """
124 wether to remove figure data from ipynb and store them in auxiliary
125 dictionnary
126 """
127 )
128
112 tex_environement = Bool(False,
129 tex_environement = Bool(False,
113 config=True,
130 config=True,
114 help=""" is this a tex environment or not """)
131 help=""" is this a tex environment or not """)
115
132
116 template_file = Unicode('',
133 template_file = Unicode('',
117 config=True,
134 config=True,
118 help=""" Name of the template file to use """ )
135 help=""" Name of the template file to use """ )
119 #-------------------------------------------------------------------------
136 #-------------------------------------------------------------------------
120 # Instance-level attributes that are set in the constructor for this
137 # Instance-level attributes that are set in the constructor for this
121 # class.
138 # class.
122 #-------------------------------------------------------------------------
139 #-------------------------------------------------------------------------
140 infile = Any()
141
123
142
143 infile_dir = Unicode()
144
145 #todo: move to filter
146 def filter_data_type(self, output):
147 """ return the first availlable format in priority """
148 for fmt in self.display_data_priority:
149 if fmt in output:
150 return [fmt]
151 raise Exception("did not found any format I can extract in output, shoudl at lest have one")
124
152
125 preprocessors = []
153 preprocessors = []
126
154
127 def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw):
155 def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw):
128 """ Init a new converter.
156 """
129
157 config: the Configurable config object to pass around.
130 config: the Configurable config object to pass around.
131
158
132 preprocessors: dict of **availlable** key/value function to run on
159 preprocessors: dict of **availlable** key/value function to run on
133 ipynb json data before conversion to extract/inline file.
160 ipynb json data before conversion to extract/inline file.
134 See `transformer.py` and `ConfigurableTransformers`
161 See `transformer.py` and `ConfigurableTransformers`
135
162
136 set the order in which the transformers should apply
163 set the order in which the transformers should apply
137 with the `pre_transformer_order` trait of this class
164 with the `pre_transformer_order` trait of this class
138
165
139 transformers registerd by this key will take precedence on
166 transformers registerd by this key will take precedence on
140 default one.
167 default one.
141
168
142
169
143 jinja_filters: dict of supplementary jinja filter that should be made
170 jinja_filters: dict of supplementary jinja filter that should be made
144 availlable in template. If those are of Configurable Class type,
171 availlable in template. If those are of Configurable Class type,
145 they will be instanciated with the config object as argument.
172 they will be instanciated with the config object as argument.
146
173
147 user defined filter will overwrite the one availlable by default.
174 user defined filter will overwrite the one availlable by default.
148 """
175 """
149 super(ConverterTemplate, self).__init__(config=config, **kw)
176 super(ConverterTemplate, self).__init__(config=config, **kw)
150
177
151 # variable parameters depending on the pype of jinja environement
178 # variable parameters depending on the pype of jinja environement
152 self.env = texenv if self.tex_environement else env
179 self.env = texenv if self.tex_environement else env
153 self.ext = '.tplx' if self.tex_environement else '.tpl'
180 self.ext = '.tplx' if self.tex_environement else '.tpl'
154
181
155 for name in self.pre_transformer_order:
182 for name in self.pre_transformer_order:
156 # get the user-defined transformer first
183 # get the user-defined transformer first
157 transformer = getattr(preprocessors, name, getattr(trans, name, None))
184 transformer = getattr(preprocessors, name, getattr(trans, name, None))
158 if isinstance(transformer, MetaHasTraits):
185 if isinstance(transformer, MetaHasTraits):
159 transformer = transformer(config=config)
186 transformer = transformer(config=config)
160 self.preprocessors.append(transformer)
187 self.preprocessors.append(transformer)
161
188
162 ## for compat, remove later
189 ## for compat, remove later
163 self.preprocessors.append(trans.coalesce_streams)
190 self.preprocessors.append(trans.coalesce_streams)
164 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
191 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
165 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
192 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
166 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
193 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
167
194
168 ##
195 ##
169 self.env.filters['filter_data_type'] = FilterDataType(config=config)
196 self.env.filters['filter_data_type'] = FilterDataType(config=config)
170 self.env.filters['pycomment'] = python_comment
197 self.env.filters['pycomment'] = python_comment
171 self.env.filters['indent'] = indent
198 self.env.filters['indent'] = indent
172 self.env.filters['rm_fake'] = rm_fake
199 self.env.filters['rm_fake'] = rm_fake
173 self.env.filters['rm_ansi'] = remove_ansi
200 self.env.filters['rm_ansi'] = remove_ansi
174 self.env.filters['markdown'] = markdown
201 self.env.filters['markdown'] = markdown
175 self.env.filters['highlight'] = highlight
202 self.env.filters['highlight'] = highlight
176 self.env.filters['ansi2html'] = ansi2html
203 self.env.filters['ansi2html'] = ansi2html
177 self.env.filters['markdown2latex'] = markdown2latex
204 self.env.filters['markdown2latex'] = markdown2latex
178 self.env.filters['markdown2rst'] = markdown2rst
205 self.env.filters['markdown2rst'] = markdown2rst
179 self.env.filters['wrap'] = wrap
206 self.env.filters['wrap'] = wrap
180
207
181 ## user filter will overwrite
208 ## user filter will overwrite
182 for key, filtr in jinja_filters.iteritems():
209 for key, filtr in jinja_filters.iteritems():
183 if isinstance(filtr, MetaHasTraits):
210 if isinstance(filtr, MetaHasTraits):
184 self.env.filters[key] = filtr(config=config)
211 self.env.filters[key] = filtr(config=config)
185 else :
212 else :
186 self.env.filters[key] = filtr
213 self.env.filters[key] = filtr
187
214
188 self.template = self.env.get_template(self.template_file+self.ext)
215 self.template = self.env.get_template(self.template_file+self.ext)
189
216
190
217
191 def process(self, nb):
218 def process(self, nb):
192 """
219 """
193 preprocess the notebook json for easier use with the templates.
220 preprocess the notebook json for easier use with the templates.
194 will call all the `preprocessor`s in order before returning it.
221 will call all the `preprocessor`s in order before returning it.
195 """
222 """
196
223
197 # dict of 'resources' that could be made by the preprocessors
224 # dict of 'resources' that could be made by the preprocessors
198 # like key/value data to extract files from ipynb like in latex conversion
225 # like key/value data to extract files from ipynb like in latex conversion
199 resources = {}
226 resources = {}
200
227
201 for preprocessor in self.preprocessors:
228 for preprocessor in self.preprocessors:
202 nb, resources = preprocessor(nb, resources)
229 nb, resources = preprocessor(nb, resources)
203
230
204 return nb, resources
231 return nb, resources
205
232
206 def convert(self, nb):
233 def convert(self, nb):
207 """ convert the ipynb file
234 """ convert the ipynb file
208
235
209 return both the converted ipynb file and a dict containing potential
236 return both the converted ipynb file and a dict containing potential
210 other resources
237 other resources
211 """
238 """
212 nb, resources = self.process(nb)
239 nb, resources = self.process(nb)
213 return self.template.render(nb=nb, resources=resources), resources
240 return self.template.render(nb=nb, resources=resources), resources
214
241
215
242
216 def from_filename(self, filename):
243 def from_filename(self, filename):
217 """read and convert a notebook from a file name"""
244 """read and convert a notebook from a file name"""
218 with io.open(filename) as f:
245 with io.open(filename) as f:
219 return self.convert(nbformat.read(f, 'json'))
246 return self.convert(nbformat.read(f, 'json'))
220
247
221 def from_file(self, filelike):
248 def from_file(self, filelike):
222 """read and convert a notebook from a filelike object
249 """read and convert a notebook from a filelike object
223
250
224 filelike object will just be "read" and should be json format..
251 filelike object will just be "read" and should be json format..
225 """
252 """
226 return self.convert(nbformat.read(filelike, 'json'))
253 return self.convert(nbformat.read(filelike, 'json'))
227
254
228 def from_json(self, json):
255 def from_json(self, json):
229 """ not implemented
256 """ not implemented
230
257
231 Should convert from a json object
258 Should convert from a json object
232 """
259 """
233 raise NotImplementedError('not implemented (yet?)')
260 raise NotImplementedError('not implemented (yet?)')
234
261
General Comments 0
You need to be logged in to leave comments. Login now