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