##// END OF EJS Templates
pretransformer configurables
Matthias BUSSONNIER -
Show More
@@ -1,295 +1,301 b''
1 """Base classes for the notebook conversion pipeline.
1 """Base classes for the notebook conversion pipeline.
2
2
3 This module defines Converter, from which all objects designed to implement
3 This module defines Converter, from which all objects designed to implement
4 a conversion of IPython notebooks to some other format should inherit.
4 a conversion of IPython notebooks to some other format should inherit.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2012, the IPython Development Team.
7 # Copyright (c) 2012, the IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import print_function, absolute_import
18 from __future__ import print_function, absolute_import
19
19
20 # Stdlib imports
20 # Stdlib imports
21 import io
21 import io
22 import os
22 import os
23 import re
23 import re
24 from IPython.utils import path
24 from IPython.utils import path
25
25
26 from jinja2 import Environment, FileSystemLoader
26 from jinja2 import Environment, FileSystemLoader
27 env = Environment(
27 env = Environment(
28 loader=FileSystemLoader([
28 loader=FileSystemLoader([
29 './templates/',
29 './templates/',
30 './templates/skeleton/',
30 './templates/skeleton/',
31 ]),
31 ]),
32 extensions=['jinja2.ext.loopcontrols']
32 extensions=['jinja2.ext.loopcontrols']
33 )
33 )
34
34
35 texenv = Environment(
35 texenv = Environment(
36 loader=FileSystemLoader([
36 loader=FileSystemLoader([
37 './templates/tex/',
37 './templates/tex/',
38 './templates/skeleton/tex/',
38 './templates/skeleton/tex/',
39 ]),
39 ]),
40 extensions=['jinja2.ext.loopcontrols']
40 extensions=['jinja2.ext.loopcontrols']
41 )
41 )
42
42
43 # IPython imports
43 # IPython imports
44 from IPython.nbformat import current as nbformat
44 from IPython.nbformat import current as nbformat
45 from IPython.config.configurable import Configurable
45 from IPython.config.configurable import Configurable
46 from IPython.utils.traitlets import ( Unicode, Any, List, Bool)
46 from IPython.utils.traitlets import ( Unicode, Any, List, Bool)
47
47
48 # Our own imports
48 # Our own imports
49 from IPython.utils.text import indent
49 from IPython.utils.text import indent
50 from .utils import remove_ansi
50 from .utils import remove_ansi
51 from markdown import markdown
51 from markdown import markdown
52 from .utils import highlight, ansi2html
52 from .utils import highlight, ansi2html
53 from .utils import markdown2latex
53 from .utils import markdown2latex
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Class declarations
55 # Class declarations
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57 def rm_fake(strng):
57 def rm_fake(strng):
58 return strng.replace('/files/', '')
58 return strng.replace('/files/', '')
59
59
60 class ConversionException(Exception):
60 class ConversionException(Exception):
61 pass
61 pass
62
62
63
63
64 def python_comment(string):
64 def python_comment(string):
65 return '# '+'\n# '.join(string.split('\n'))
65 return '# '+'\n# '.join(string.split('\n'))
66
66
67
67
68
68
69 def header_body():
69 def header_body():
70 """Return the body of the header as a list of strings."""
70 """Return the body of the header as a list of strings."""
71
71
72 from pygments.formatters import HtmlFormatter
72 from pygments.formatters import HtmlFormatter
73
73
74 header = []
74 header = []
75 static = os.path.join(path.get_ipython_package_dir(),
75 static = os.path.join(path.get_ipython_package_dir(),
76 'frontend', 'html', 'notebook', 'static',
76 'frontend', 'html', 'notebook', 'static',
77 )
77 )
78 here = os.path.split(os.path.realpath(__file__))[0]
78 here = os.path.split(os.path.realpath(__file__))[0]
79 css = os.path.join(static, 'css')
79 css = os.path.join(static, 'css')
80 for sheet in [
80 for sheet in [
81 # do we need jquery and prettify?
81 # do we need jquery and prettify?
82 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
82 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
83 # 'jquery-ui.min.css'),
83 # 'jquery-ui.min.css'),
84 # os.path.join(static, 'prettify', 'prettify.css'),
84 # os.path.join(static, 'prettify', 'prettify.css'),
85 os.path.join(css, 'boilerplate.css'),
85 os.path.join(css, 'boilerplate.css'),
86 os.path.join(css, 'fbm.css'),
86 os.path.join(css, 'fbm.css'),
87 os.path.join(css, 'notebook.css'),
87 os.path.join(css, 'notebook.css'),
88 os.path.join(css, 'renderedhtml.css'),
88 os.path.join(css, 'renderedhtml.css'),
89 # our overrides:
89 # our overrides:
90 os.path.join(here, '..', 'css', 'static_html.css'),
90 os.path.join(here, '..', 'css', 'static_html.css'),
91 ]:
91 ]:
92
92
93 with io.open(sheet, encoding='utf-8') as f:
93 with io.open(sheet, encoding='utf-8') as f:
94 s = f.read()
94 s = f.read()
95 header.append(s)
95 header.append(s)
96
96
97 pygments_css = HtmlFormatter().get_style_defs('.highlight')
97 pygments_css = HtmlFormatter().get_style_defs('.highlight')
98 header.append(pygments_css)
98 header.append(pygments_css)
99 return header
99 return header
100
100
101 # todo, make the key part configurable.
101 # todo, make the key part configurable.
102 def _new_figure(data, fmt, count):
102 def _new_figure(data, fmt, count):
103 """Create a new figure file in the given format.
103 """Create a new figure file in the given format.
104
104
105 Returns a path relative to the input file.
105 Returns a path relative to the input file.
106 """
106 """
107 figname = '_fig_%02i.%s' % (count, fmt)
107 figname = '_fig_%02i.%s' % (count, fmt)
108
108
109 # Binary files are base64-encoded, SVG is already XML
109 # Binary files are base64-encoded, SVG is already XML
110 if fmt in ('png', 'jpg', 'pdf'):
110 if fmt in ('png', 'jpg', 'pdf'):
111 data = data.decode('base64')
111 data = data.decode('base64')
112
112
113 return figname,data
113 return figname,data
114
114
115
115
116
116
117
117
118 inlining = {}
118 inlining = {}
119 inlining['css'] = header_body()
119 inlining['css'] = header_body()
120
120
121 LATEX_SUBS = (
121 LATEX_SUBS = (
122 (re.compile(r'\\'), r'\\textbackslash'),
122 (re.compile(r'\\'), r'\\textbackslash'),
123 (re.compile(r'([{}_#%&$])'), r'\\\1'),
123 (re.compile(r'([{}_#%&$])'), r'\\\1'),
124 (re.compile(r'~'), r'\~{}'),
124 (re.compile(r'~'), r'\~{}'),
125 (re.compile(r'\^'), r'\^{}'),
125 (re.compile(r'\^'), r'\^{}'),
126 (re.compile(r'"'), r"''"),
126 (re.compile(r'"'), r"''"),
127 (re.compile(r'\.\.\.+'), r'\\ldots'),
127 (re.compile(r'\.\.\.+'), r'\\ldots'),
128 )
128 )
129
129
130 def escape_tex(value):
130 def escape_tex(value):
131 newval = value
131 newval = value
132 for pattern, replacement in LATEX_SUBS:
132 for pattern, replacement in LATEX_SUBS:
133 newval = pattern.sub(replacement, newval)
133 newval = pattern.sub(replacement, newval)
134 return newval
134 return newval
135
135
136 texenv.block_start_string = '((*'
136 texenv.block_start_string = '((*'
137 texenv.block_end_string = '*))'
137 texenv.block_end_string = '*))'
138 texenv.variable_start_string = '((('
138 texenv.variable_start_string = '((('
139 texenv.variable_end_string = ')))'
139 texenv.variable_end_string = ')))'
140 texenv.comment_start_string = '((='
140 texenv.comment_start_string = '((='
141 texenv.comment_end_string = '=))'
141 texenv.comment_end_string = '=))'
142 texenv.filters['escape_tex'] = escape_tex
142 texenv.filters['escape_tex'] = escape_tex
143
143
144 def cell_preprocessor(function):
144 def cell_preprocessor(function):
145 """ wrap a function to be executed on all cells of a notebook
145 """ wrap a function to be executed on all cells of a notebook
146
146
147 wrapped function parameters :
147 wrapped function parameters :
148 cell : the cell
148 cell : the cell
149 other : external resources
149 other : external resources
150 index : index of the cell
150 index : index of the cell
151 """
151 """
152 def wrappedfunc(nb,other):
152 def wrappedfunc(nb,other):
153 for worksheet in nb.worksheets :
153 for worksheet in nb.worksheets :
154 for index, cell in enumerate(worksheet.cells):
154 for index, cell in enumerate(worksheet.cells):
155 worksheet.cells[index],other= function(cell,other,index)
155 worksheet.cells[index],other= function(cell,other,index)
156 return nb,other
156 return nb,other
157 return wrappedfunc
157 return wrappedfunc
158
158
159
159
160
160
161 @cell_preprocessor
161 @cell_preprocessor
162 def haspyout_transformer(cell, other, count):
162 def haspyout_transformer(cell, other, count):
163 """
163 """
164 Add a haspyout flag to cell that have it
164 Add a haspyout flag to cell that have it
165
165
166 Easier for templating, where you can't know in advance
166 Easier for templating, where you can't know in advance
167 wether to write the out prompt
167 wether to write the out prompt
168
168
169 """
169 """
170 cell.type = cell.cell_type
170 cell.type = cell.cell_type
171 cell.haspyout = False
171 cell.haspyout = False
172 for out in cell.get('outputs', []):
172 for out in cell.get('outputs', []):
173 if out.output_type == 'pyout':
173 if out.output_type == 'pyout':
174 cell.haspyout = True
174 cell.haspyout = True
175 break
175 break
176 return cell,other
176 return cell,other
177
177
178
178
179 @cell_preprocessor
179 @cell_preprocessor
180 def extract_figure_transformer(cell,other,count):
180 def extract_figure_transformer(cell,other,count):
181 for i,out in enumerate(cell.get('outputs', [])):
181 for i,out in enumerate(cell.get('outputs', [])):
182 for type in ['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg']:
182 for type in ['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg']:
183 if out.hasattr(type):
183 if out.hasattr(type):
184 figname,data = _new_figure(out[type], type,count)
184 figname,data = _new_figure(out[type], type,count)
185 cell.outputs[i][type] = figname
185 cell.outputs[i][type] = figname
186 out['key_'+type] = figname
186 out['key_'+type] = figname
187 other[figname] = data
187 other[figname] = data
188 count = count+1
188 count = count+1
189 return cell,other
189 return cell,other
190
190
191
191
192 class ConverterTemplate(Configurable):
192 class ConverterTemplate(Configurable):
193 """ A Jinja2 base converter templates"""
193 """ A Jinja2 base converter templates"""
194
194
195 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
195 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
196 config=True,
196 config=True,
197 help= """
197 help= """
198 A list of ast.NodeTransformer subclass instances, which will be applied
198 An ordered list of prefered output type, the firs encounterd will usually be
199 to user input before code is run.
199 used when converting discarding the others.
200 """
201 )
202
203 pre_transformer_order = List([],
204 config=True,
205 help= """ An ordered list of pretransformer to apply to the ipynb file befor running through templates
200 """
206 """
201 )
207 )
202
208
203 extract_figures = Bool(False,
209 extract_figures = Bool(False,
204 config=True,
210 config=True,
205 help= """
211 help= """
206 wether to remove figure data from ipynb and store them in auxiliary
212 wether to remove figure data from ipynb and store them in auxiliary
207 dictionnary
213 dictionnary
208 """
214 """
209 )
215 )
210
216
211 tex_environement = Bool(False,
217 tex_environement = Bool(False,
212 config=True,
218 config=True,
213 help=""" is this a tex environment or not """)
219 help=""" is this a tex environment or not """)
214
220
215 template_file = Unicode('',
221 template_file = Unicode('',
216 config=True,
222 config=True,
217 help=""" whetever """ )
223 help=""" whetever """ )
218 #-------------------------------------------------------------------------
224 #-------------------------------------------------------------------------
219 # Instance-level attributes that are set in the constructor for this
225 # Instance-level attributes that are set in the constructor for this
220 # class.
226 # class.
221 #-------------------------------------------------------------------------
227 #-------------------------------------------------------------------------
222 infile = Any()
228 infile = Any()
223
229
224
230
225 infile_dir = Unicode()
231 infile_dir = Unicode()
226
232
227 def filter_data_type(self,output):
233 def filter_data_type(self,output):
228 for fmt in self.display_data_priority:
234 for fmt in self.display_data_priority:
229 if fmt in output:
235 if fmt in output:
230 return [fmt]
236 return [fmt]
231
237
232 def __init__(self, preprocessors=[], config=None, **kw):
238 def __init__(self, preprocessors=[], config=None, **kw):
233 """
239 """
234 tplfile : jinja template file to process.
240 tplfile : jinja template file to process.
235
241
236 config: the Configurable confg object to pass around
242 config: the Configurable confg object to pass around
237
243
238 preprocessors: list of function to run on ipynb json data before conversion
244 preprocessors: list of function to run on ipynb json data before conversion
239 to extract/inline file,
245 to extract/inline file,
240
246
241 """
247 """
242 super(ConverterTemplate, self).__init__(config=config, **kw)
248 super(ConverterTemplate, self).__init__(config=config, **kw)
243 self.env = texenv if self.tex_environement else env
249 self.env = texenv if self.tex_environement else env
244 self.ext = '.tplx' if self.tex_environement else '.tpl'
250 self.ext = '.tplx' if self.tex_environement else '.tpl'
245 self.nb = None
251 self.nb = None
246 self.preprocessors = preprocessors
252 self.preprocessors = preprocessors
247 self.preprocessors.append(haspyout_transformer)
253 self.preprocessors.append(haspyout_transformer)
248 if self.extract_figures:
254 if self.extract_figures:
249 self.preprocessors.append(extract_figure_transformer)
255 self.preprocessors.append(extract_figure_transformer)
250
256
251 self.env.filters['filter_data_type'] = self.filter_data_type
257 self.env.filters['filter_data_type'] = self.filter_data_type
252 self.env.filters['pycomment'] = python_comment
258 self.env.filters['pycomment'] = python_comment
253 self.env.filters['indent'] = indent
259 self.env.filters['indent'] = indent
254 self.env.filters['rm_fake'] = rm_fake
260 self.env.filters['rm_fake'] = rm_fake
255 self.env.filters['rm_ansi'] = remove_ansi
261 self.env.filters['rm_ansi'] = remove_ansi
256 self.env.filters['markdown'] = markdown
262 self.env.filters['markdown'] = markdown
257 self.env.filters['highlight'] = highlight
263 self.env.filters['highlight'] = highlight
258 self.env.filters['ansi2html'] = ansi2html
264 self.env.filters['ansi2html'] = ansi2html
259 self.env.filters['markdown2latex'] = markdown2latex
265 self.env.filters['markdown2latex'] = markdown2latex
260
266
261 self.template = self.env.get_template(self.template_file+self.ext)
267 self.template = self.env.get_template(self.template_file+self.ext)
262
268
263
269
264
270
265 def process(self):
271 def process(self):
266 """
272 """
267 preprocess the notebook json for easier use with the templates.
273 preprocess the notebook json for easier use with the templates.
268 will call all the `preprocessor`s in order before returning it.
274 will call all the `preprocessor`s in order before returning it.
269 """
275 """
270 nb = self.nb
276 nb = self.nb
271
277
272 # dict of 'resources' that could be made by the preprocessors
278 # dict of 'resources' that could be made by the preprocessors
273 # like key/value data to extract files from ipynb like in latex conversion
279 # like key/value data to extract files from ipynb like in latex conversion
274 resources = {}
280 resources = {}
275
281
276 for preprocessor in self.preprocessors:
282 for preprocessor in self.preprocessors:
277 nb,resources = preprocessor(nb,resources)
283 nb,resources = preprocessor(nb,resources)
278
284
279 return nb, resources
285 return nb, resources
280
286
281 def convert(self):
287 def convert(self):
282 """ convert the ipynb file
288 """ convert the ipynb file
283
289
284 return both the converted ipynb file and a dict containing potential
290 return both the converted ipynb file and a dict containing potential
285 other resources
291 other resources
286 """
292 """
287 nb,resources = self.process()
293 nb,resources = self.process()
288 return self.template.render(nb=nb, inlining=inlining), resources
294 return self.template.render(nb=nb, inlining=inlining), resources
289
295
290
296
291 def read(self, filename):
297 def read(self, filename):
292 "read and parse notebook into NotebookNode called self.nb"
298 "read and parse notebook into NotebookNode called self.nb"
293 with io.open(filename) as f:
299 with io.open(filename) as f:
294 self.nb = nbformat.read(f, 'json')
300 self.nb = nbformat.read(f, 'json')
295
301
General Comments 0
You need to be logged in to leave comments. Login now