##// END OF EJS Templates
pylint
Matthias BUSSONNIER -
Show More
@@ -1,88 +1,88
1 1 #-----------------------------------------------------------------------------
2 2 # Copyright (c) 2012, the IPython Development Team.
3 3 #
4 4 # Distributed under the terms of the Modified BSD License.
5 5 #
6 6 # The full license is in the file COPYING.txt, distributed with this software.
7 7 #-----------------------------------------------------------------------------
8 8
9 9 from __future__ import absolute_import
10 10
11 11 # Stdlib imports
12 12 import re
13 13
14 14 from IPython.utils.text import indent
15 15 from markdown import markdown
16 16 from .utils import remove_ansi
17 17 from .utils import highlight, ansi2html
18 18 from .utils import markdown2latex
19 19
20 20 from IPython.config.configurable import Configurable
21 from IPython.utils.traitlets import Unicode, Bool, Dict, List
21 from IPython.utils.traitlets import List
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Class declarations
25 25 #-----------------------------------------------------------------------------
26 26
27 27 class GlobalConfigurable(Configurable):
28 28 """Global configurable class for shared config
29 29
30 30 Usefull for display data priority that might be use by many trasnformers
31 31 """
32 32
33 33 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
34 34 config=True,
35 35 help= """
36 36 An ordered list of prefered output type, the first
37 37 encounterd will usually be used when converting discarding
38 38 the others.
39 39 """
40 40 )
41 41
42 42 def __init__(self, config=None, **kw):
43 43 super(GlobalConfigurable, self).__init__( config=config, **kw)
44 44
45 45 class ConfigurableFilter(GlobalConfigurable):
46 46 """Configurable Jinja Filter"""
47 47
48 48 def __init__(self, config=None, **kw):
49 49 super(ConfigurableFilter, self).__init__(config=config, **kw)
50 50
51 51 def __call__(self, *args, **kwargs):
52 52 raise NotImplementedError('should be implemented by subclass')
53 53
54 54
55 55 class FilterDataType(ConfigurableFilter):
56 56 """ return the preferd displayed format
57 57 """
58 58
59 59 def __call__(self, output):
60 60 """ return the first availlable format in priority """
61 61 for fmt in self.display_data_priority:
62 62 if fmt in output:
63 63 return [fmt]
64 64 raise Exception("did not found any format I can extract in output, shoudl at lest have one")
65 65
66 66
67 67
68 68 def rm_fake(strng):
69 69 return strng.replace('/files/', '')
70 70
71 71 def python_comment(string):
72 72 return '# '+'\n# '.join(string.split('\n'))
73 73
74 74 LATEX_SUBS = (
75 75 (re.compile(r'\\'), r'\\textbackslash'),
76 76 (re.compile(r'([{}_#%&$])'), r'\\\1'),
77 77 (re.compile(r'~'), r'\~{}'),
78 78 (re.compile(r'\^'), r'\^{}'),
79 79 (re.compile(r'"'), r"''"),
80 80 (re.compile(r'\.\.\.+'), r'\\ldots'),
81 81 )
82 82
83 83 def escape_tex(value):
84 84 newval = value
85 85 for pattern, replacement in LATEX_SUBS:
86 86 newval = pattern.sub(replacement, newval)
87 87 return newval
88 88
@@ -1,187 +1,187
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 import converters.transformers as trans
20 20 from converters.jinja_filters import (python_comment, indent,
21 21 rm_fake, remove_ansi, markdown, highlight,
22 22 ansi2html, markdown2latex, escape_tex, FilterDataType)
23 23
24 24 from converters.utils import markdown2rst
25 25
26 26
27 27
28 28 # Stdlib imports
29 29 import io
30 30
31 31 from IPython.utils.traitlets import MetaHasTraits
32 32
33 33 from jinja2 import Environment, FileSystemLoader
34 34 env = Environment(
35 35 loader=FileSystemLoader([
36 36 './templates/',
37 37 './templates/skeleton/',
38 38 ]),
39 39 extensions=['jinja2.ext.loopcontrols']
40 40 )
41 41
42 42 texenv = Environment(
43 43 loader=FileSystemLoader([
44 44 './templates/tex/',
45 45 './templates/skeleton/tex/',
46 46 ]),
47 47 extensions=['jinja2.ext.loopcontrols']
48 48 )
49 49
50 50 # IPython imports
51 51 from IPython.nbformat import current as nbformat
52 52 from IPython.config.configurable import Configurable
53 from IPython.utils.traitlets import ( Unicode, Any, List, Bool)
53 from IPython.utils.traitlets import ( Unicode, List, Bool)
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Class declarations
57 57 #-----------------------------------------------------------------------------
58 58 class ConversionException(Exception):
59 59 pass
60 60
61 61
62 62
63 63 texenv.block_start_string = '((*'
64 64 texenv.block_end_string = '*))'
65 65
66 66 texenv.variable_start_string = '((('
67 67 texenv.variable_end_string = ')))'
68 68
69 69 texenv.comment_start_string = '((='
70 70 texenv.comment_end_string = '=))'
71 71
72 72 texenv.filters['escape_tex'] = escape_tex
73 73
74 74
75 75 class ConverterTemplate(Configurable):
76 76 """ A Jinja2 base converter templates
77 77
78 78 Preprocess the ipynb files, feed it throug jinja templates,
79 79 and spit an converted files and a data object with other data
80 80
81 81 shoudl be mostly configurable
82 82 """
83 83
84 84 pre_transformer_order = List(['haspyout_transformer'],
85 85 config=True,
86 86 help= """
87 87 An ordered list of pre transformer to apply to the ipynb
88 88 file befor running through templates
89 89 """
90 90 )
91 91
92 92 tex_environement = Bool(False,
93 93 config=True,
94 94 help=""" is this a tex environment or not """)
95 95
96 96 template_file = Unicode('',
97 97 config=True,
98 98 help=""" Name of the template file to use """ )
99 99 #-------------------------------------------------------------------------
100 100 # Instance-level attributes that are set in the constructor for this
101 101 # class.
102 102 #-------------------------------------------------------------------------
103 103
104 104
105 105 preprocessors = []
106 106
107 107 def __init__(self, preprocessors={}, jinja_filters={}, config=None, **kw):
108 108 """ Init a new converter.
109 109
110 110
111 111 config: the Configurable confgg object to pass around
112 112
113 113 preprocessors: dict of **availlable** key/value function to run on
114 114 ipynb json data before conversion to extract/inline file,
115 115
116 116 jinja_filter : dict of supplementary jinja filter that should be made
117 117 availlable in template. If those are of Configurable Class type, they
118 118 will be instanciated with the config object as argument.
119 119
120 120 """
121 121 super(ConverterTemplate, self).__init__(config=config, **kw)
122 122 self.env = texenv if self.tex_environement else env
123 123 self.ext = '.tplx' if self.tex_environement else '.tpl'
124 124
125 125 for name in self.pre_transformer_order:
126 126 transformer = getattr(preprocessors, name, getattr(trans, name, None))
127 127 if isinstance(transformer, MetaHasTraits):
128 128 transformer = transformer(config=config)
129 129 self.preprocessors.append(transformer)
130 130
131 131 ## for compat, remove later
132 132 self.preprocessors.append(trans.ExtractFigureTransformer(config=config))
133 133 self.preprocessors.append(trans.RevealHelpTransformer(config=config))
134 134 self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config))
135 135
136 136 ##
137 137 self.env.filters['filter_data_type'] = FilterDataType(config=config)
138 138 self.env.filters['pycomment'] = python_comment
139 139 self.env.filters['indent'] = indent
140 140 self.env.filters['rm_fake'] = rm_fake
141 141 self.env.filters['rm_ansi'] = remove_ansi
142 142 self.env.filters['markdown'] = markdown
143 143 self.env.filters['highlight'] = highlight
144 144 self.env.filters['ansi2html'] = ansi2html
145 145 self.env.filters['markdown2latex'] = markdown2latex
146 146 self.env.filters['markdown2rst'] = markdown2rst
147 147 for key, filtr in jinja_filters.iteritems():
148 148 if isinstance(filtr, MetaHasTraits):
149 149 self.env.filters[key] = filtr(config=config)
150 150 else :
151 151 self.env.filters[key] = filtr
152 152
153 153 self.template = self.env.get_template(self.template_file+self.ext)
154 154
155 155
156 156 def process(self, nb):
157 157 """
158 158 preprocess the notebook json for easier use with the templates.
159 159 will call all the `preprocessor`s in order before returning it.
160 160 """
161 161
162 162 # dict of 'resources' that could be made by the preprocessors
163 163 # like key/value data to extract files from ipynb like in latex conversion
164 164 resources = {}
165 165
166 166 for preprocessor in self.preprocessors:
167 167 nb, resources = preprocessor(nb, resources)
168 168
169 169 return nb, resources
170 170
171 171 def convert(self, nb):
172 172 """ convert the ipynb file
173 173
174 174 return both the converted ipynb file and a dict containing potential
175 175 other resources
176 176 """
177 177 nb, resources = self.process(nb)
178 178 return self.template.render(nb=nb, resources=resources), resources
179 179
180 180
181 181 def from_filename(self, filename):
182 182 "read and parse notebook into NotebookNode called self.nb"
183 183 with io.open(filename) as f:
184 184 return self.convert(nbformat.read(f, 'json'))
185 185
186 186
187 187
@@ -1,293 +1,293
1 1 """
2 2
3 3 """
4 4
5 5 from __future__ import print_function
6 6
7 7
8 8 from IPython.config.configurable import Configurable
9 9 from IPython.utils.traitlets import Unicode, Bool, Dict, List
10 10
11 11 class ConfigurableTransformers(Configurable):
12 12 """ A configurable transformer """
13 13
14 14 def __init__(self, config=None, **kw):
15 15 super(ConfigurableTransformers, self).__init__(config=config, **kw)
16 16
17 17 def __call__(self, nb, other):
18 18 try :
19 19 for worksheet in nb.worksheets :
20 20 for index, cell in enumerate(worksheet.cells):
21 21 worksheet.cells[index], other = self.cell_transform(cell, other, index)
22 22 return nb, other
23 except NotImplementedError as error :
23 except NotImplementedError:
24 24 raise NotImplementedError('should be implemented by subclass')
25 25
26 26 def cell_transform(self, cell, other, index):
27 27 """
28 28 Overwrite if you want to apply a transformation on each cell
29 29 """
30 30 raise NotImplementedError('should be implemented by subclass')
31 31
32 32
33 33 class ActivatableTransformer(ConfigurableTransformers):
34 34
35 35 enabled = Bool(False, config=True)
36 36
37 37 def __call__(self, nb, other):
38 38 if not self.enabled :
39 39 return nb,other
40 40 else :
41 41 return super(ActivatableTransformer,self).__call__(nb, other)
42 42
43 43
44 44 def cell_preprocessor(function):
45 45 """ wrap a function to be executed on all cells of a notebook
46 46
47 47 wrapped function parameters :
48 48 cell : the cell
49 49 other : external resources
50 50 index : index of the cell
51 51 """
52 52 def wrappedfunc(nb, other):
53 53 for worksheet in nb.worksheets :
54 54 for index, cell in enumerate(worksheet.cells):
55 55 worksheet.cells[index], other = function(cell, other, index)
56 56 return nb, other
57 57 return wrappedfunc
58 58
59 59
60 60 @cell_preprocessor
61 61 def haspyout_transformer(cell, other, count):
62 62 """
63 63 Add a haspyout flag to cell that have it
64 64
65 65 Easier for templating, where you can't know in advance
66 66 wether to write the out prompt
67 67
68 68 """
69 69 cell.type = cell.cell_type
70 70 cell.haspyout = False
71 71 for out in cell.get('outputs', []):
72 72 if out.output_type == 'pyout':
73 73 cell.haspyout = True
74 74 break
75 75 return cell, other
76 76
77 77
78 78 # todo, make the key part configurable.
79 79
80 80 class ExtractFigureTransformer(ActivatableTransformer):
81 81
82 82 extra_ext_map = Dict({},
83 83 config=True,
84 84 help="""extra map to override extension based on type.
85 85 Usefull for latex where svg will be converted to pdf before inclusion
86 86 """
87 87 )
88 88 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
89 89 config=True,
90 90 help= """
91 91 An ordered list of prefered output type, the first
92 92 encounterd will usually be used when converting discarding
93 93 the others.
94 94 """
95 95 )
96 96
97 97
98 98 #to do change this to .format {} syntax
99 99 key_tpl = Unicode('_fig_%02i.%s', config=True)
100 100
101 101 def _get_ext(self, ext):
102 102 if ext in self.extra_ext_map :
103 103 return self.extra_ext_map[ext]
104 104 return ext
105 105
106 106 def _new_figure(self, data, fmt, count):
107 107 """Create a new figure file in the given format.
108 108
109 109 Returns a path relative to the input file.
110 110 """
111 111 figname = self.key_tpl % (count, self._get_ext(fmt))
112 112 key = self.key_tpl % (count, fmt)
113 113
114 114 # Binary files are base64-encoded, SVG is already XML
115 115 if fmt in ('png', 'jpg', 'pdf'):
116 116 data = data.decode('base64')
117 117
118 118 return figname, key, data
119 119
120 120
121 121 def cell_transform(self, cell, other, count):
122 122 if other.get('figures',None) is None :
123 123 other['figures']={}
124 for i, out in enumerate(cell.get('outputs', [])):
125 for type in self.display_data_priority:
126 if out.hasattr(type):
127 figname, key, data = self._new_figure(out[type], type, count)
128 out['key_'+type] = figname
124 for out in cell.get('outputs', []):
125 for out_type in self.display_data_priority:
126 if out.hasattr(out_type):
127 figname, key, data = self._new_figure(out[out_type], out_type, count)
128 out['key_'+out_type] = figname
129 129 other['figures'][key] = data
130 130 count = count+1
131 131 return cell, other
132 132
133 133 class RevealHelpTransformer(ConfigurableTransformers):
134 134
135 135 section_open = False
136 136 subsection_open = False
137 137 fragment_open = False
138 138
139 139 def open_subsection(self):
140 140 self.subsection_open = True
141 141 return True
142 142
143 143 def open_section(self):
144 144 self.section_open = True
145 145 return True
146 146
147 147 def open_fragment(self):
148 148 self.fragment_open = True
149 149 return True
150 150
151 151 # could probaly write those maybe_close/open
152 152 # with a function functor
153 153 def maybe_close_section(self):
154 154 """return True is already open, false otherwise
155 155 and change state to close
156 156 """
157 157 if self.section_open :
158 158 self.section_open = False
159 159 return True
160 160 else :
161 161 return False
162 162
163 163 def maybe_open_section(self):
164 164 """return True is already open, false otherwise
165 165 and change state to close
166 166 """
167 167 if not self.section_open :
168 168 self.section_open = True
169 169 return True
170 170 else :
171 171 return False
172 172
173 173 def maybe_open_subsection(self):
174 174 """return True is already open, false otherwise
175 175 and change state to close
176 176 """
177 177 if not self.subsection_open :
178 178 self.subsection_open = True
179 179 return True
180 180 else :
181 181 return False
182 182
183 183 def maybe_close_subsection(self):
184 184 """return True is already open, false otherwise
185 185 and change state to close
186 186 """
187 187 if self.subsection_open :
188 188 self.subsection_open = False
189 189 return True
190 190 else :
191 191 return False
192 192
193 193 def maybe_close_fragment(self):
194 194 """return True is already open, false otherwise
195 195 and change state to close
196 196 """
197 197 if self.fragment_open :
198 198 self.fragment_open = False
199 199 return True
200 200 else :
201 201 return False
202 202
203 203 def cell_transform(self, cell, other,count):
204 204 ctype = cell.metadata.get('slideshow',{}).get('slide_type',None)
205 205 if ctype in [None, '-'] :
206 206 cell.metadata.slideshow = {}
207 207 cell.metadata.slideshow['slide_type'] = None
208 208 elif ctype == 'fragment':
209 209 cell.metadata.slideshow.close_fragment = self.maybe_close_fragment()
210 210 cell.metadata.slideshow.close_subsection = False
211 211 cell.metadata.slideshow.close_section = False
212 212
213 213 cell.metadata.slideshow.open_section = self.maybe_open_section()
214 214 cell.metadata.slideshow.open_subsection = self.maybe_open_subsection()
215 215 cell.metadata.slideshow.open_fragment = self.open_fragment()
216 216
217 217 elif ctype == 'subslide':
218 218 cell.metadata.slideshow.close_fragment = self.maybe_close_fragment()
219 219 cell.metadata.slideshow.close_subsection = self.maybe_close_subsection()
220 220 cell.metadata.slideshow.close_section = False
221 221
222 222 cell.metadata.slideshow.open_section = self.maybe_open_section()
223 223 cell.metadata.slideshow.open_subsection = self.open_subsection()
224 224 cell.metadata.slideshow.open_fragment = False
225 225 elif ctype == 'slide':
226 226 cell.metadata.slideshow.close_fragment = self.maybe_close_fragment()
227 227 cell.metadata.slideshow.close_subsection = self.maybe_close_subsection()
228 228 cell.metadata.slideshow.close_section = self.maybe_close_section()
229 229
230 230 cell.metadata.slideshow.open_section = self.open_section()
231 231 cell.metadata.slideshow.open_subsection = self.open_subsection()
232 232 cell.metadata.slideshow.open_fragment = False
233 233 return cell,other
234 234
235 235
236 236 class CSSHtmlHeaderTransformer(ActivatableTransformer):
237 237
238 238 def __call__(self, nb, resources):
239 239 """Fetch and add css to the resource dict
240 240
241 241 Fetch css from IPython adn Pygment to add at the beginning
242 242 of the html files.
243 243
244 244 Add this css in resources in the "inlining.css" key
245 245 """
246 246 resources['inlining']= {}
247 247 resources['inlining']['css'] = self.header
248 248 return nb, resources
249 249
250 250 header= []
251 251
252 252 def __init__(self, config=None, **kw):
253 253 super(CSSHtmlHeaderTransformer,self).__init__(config=config, **kw)
254 254 if self.enabled :
255 255 self.regen_header()
256 256
257 257 def regen_header(self):
258 258 ## lazy load asa this might not be use in many transformers
259 259 import os
260 260 from IPython.utils import path
261 261 import io
262 262 from pygments.formatters import HtmlFormatter
263 263 header = []
264 264 static = os.path.join(path.get_ipython_package_dir(),
265 265 'frontend', 'html', 'notebook', 'static',
266 266 )
267 267 here = os.path.split(os.path.realpath(__file__))[0]
268 268 css = os.path.join(static, 'css')
269 269 for sheet in [
270 270 # do we need jquery and prettify?
271 271 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
272 272 # 'jquery-ui.min.css'),
273 273 # os.path.join(static, 'prettify', 'prettify.css'),
274 274 os.path.join(css, 'boilerplate.css'),
275 275 os.path.join(css, 'fbm.css'),
276 276 os.path.join(css, 'notebook.css'),
277 277 os.path.join(css, 'renderedhtml.css'),
278 278 os.path.join(css, 'style.min.css'),
279 279 # our overrides:
280 280 os.path.join(here, '..', 'css', 'static_html.css'),
281 281 ]:
282 282 try:
283 283 with io.open(sheet, encoding='utf-8') as f:
284 284 s = f.read()
285 285 header.append(s)
286 286 except IOError:
287 287 # new version of ipython with style.min.css, pass
288 288 pass
289 289
290 290 pygments_css = HtmlFormatter().get_style_defs('.highlight')
291 291 header.append(pygments_css)
292 292 self.header = header
293 293
General Comments 0
You need to be logged in to leave comments. Login now