##// END OF EJS Templates
use configuration file to do nice stuff
Matthias BUSSONNIER -
Show More
@@ -0,0 +1,5 b''
1 c = get_config()
2
3 c.ConverterTemplate.extract_figures=False
4 c.ConverterTemplate.template_file='fullhtml'
5 c.ConverterTemplate.tex_environement=False
@@ -0,0 +1,5 b''
1 c = get_config()
2
3 c.ConverterTemplate.extract_figures=False
4 c.ConverterTemplate.template_file='latex_base'
5 c.ConverterTemplate.tex_environement=True
@@ -1,287 +1,295 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 A list of ast.NodeTransformer subclass instances, which will be applied
199 to user input before code is run.
199 to user input before code is run.
200 """
200 """
201 )
201 )
202
202
203 extract_figures = Bool(False,
203 extract_figures = Bool(False,
204 config=True,
204 config=True,
205 help= """
205 help= """
206 wether to remove figure data from ipynb and store them in auxiliary
206 wether to remove figure data from ipynb and store them in auxiliary
207 dictionnary
207 dictionnary
208 """
208 """
209 )
209 )
210
211 tex_environement = Bool(False,
212 config=True,
213 help=""" is this a tex environment or not """)
214
215 template_file = Unicode('',
216 config=True,
217 help=""" whetever """ )
210 #-------------------------------------------------------------------------
218 #-------------------------------------------------------------------------
211 # Instance-level attributes that are set in the constructor for this
219 # Instance-level attributes that are set in the constructor for this
212 # class.
220 # class.
213 #-------------------------------------------------------------------------
221 #-------------------------------------------------------------------------
214 infile = Any()
222 infile = Any()
215
223
216
224
217 infile_dir = Unicode()
225 infile_dir = Unicode()
218
226
219 def filter_data_type(self,output):
227 def filter_data_type(self,output):
220 for fmt in self.display_data_priority:
228 for fmt in self.display_data_priority:
221 if fmt in output:
229 if fmt in output:
222 return [fmt]
230 return [fmt]
223
231
224 def __init__(self, tplfile='fullhtml', preprocessors=[], config=None,tex_environement=False, **kw):
232 def __init__(self, preprocessors=[], config=None, **kw):
225 """
233 """
226 tplfile : jinja template file to process.
234 tplfile : jinja template file to process.
227
235
228 config: the Configurable confg object to pass around
236 config: the Configurable confg object to pass around
229
237
230 preprocessors: list of function to run on ipynb json data before conversion
238 preprocessors: list of function to run on ipynb json data before conversion
231 to extract/inline file,
239 to extract/inline file,
232
240
233 """
241 """
234 super(ConverterTemplate, self).__init__(config=config, **kw)
242 super(ConverterTemplate, self).__init__(config=config, **kw)
235 self.env = texenv if tex_environement else env
243 self.env = texenv if self.tex_environement else env
236 self.ext = '.tplx' if tex_environement else '.tpl'
244 self.ext = '.tplx' if self.tex_environement else '.tpl'
237 self.nb = None
245 self.nb = None
238 self.preprocessors = preprocessors
246 self.preprocessors = preprocessors
239 self.preprocessors.append(haspyout_transformer)
247 self.preprocessors.append(haspyout_transformer)
240 if self.extract_figures:
248 if self.extract_figures:
241 self.preprocessors.append(extract_figure_transformer)
249 self.preprocessors.append(extract_figure_transformer)
242
250
243 self.env.filters['filter_data_type'] = self.filter_data_type
251 self.env.filters['filter_data_type'] = self.filter_data_type
244 self.env.filters['pycomment'] = python_comment
252 self.env.filters['pycomment'] = python_comment
245 self.env.filters['indent'] = indent
253 self.env.filters['indent'] = indent
246 self.env.filters['rm_fake'] = rm_fake
254 self.env.filters['rm_fake'] = rm_fake
247 self.env.filters['rm_ansi'] = remove_ansi
255 self.env.filters['rm_ansi'] = remove_ansi
248 self.env.filters['markdown'] = markdown
256 self.env.filters['markdown'] = markdown
249 self.env.filters['highlight'] = highlight
257 self.env.filters['highlight'] = highlight
250 self.env.filters['ansi2html'] = ansi2html
258 self.env.filters['ansi2html'] = ansi2html
251 self.env.filters['markdown2latex'] = markdown2latex
259 self.env.filters['markdown2latex'] = markdown2latex
252
260
253 self.template = self.env.get_template(tplfile+self.ext)
261 self.template = self.env.get_template(self.template_file+self.ext)
254
262
255
263
256
264
257 def process(self):
265 def process(self):
258 """
266 """
259 preprocess the notebook json for easier use with the templates.
267 preprocess the notebook json for easier use with the templates.
260 will call all the `preprocessor`s in order before returning it.
268 will call all the `preprocessor`s in order before returning it.
261 """
269 """
262 nb = self.nb
270 nb = self.nb
263
271
264 # dict of 'resources' that could be made by the preprocessors
272 # dict of 'resources' that could be made by the preprocessors
265 # like key/value data to extract files from ipynb like in latex conversion
273 # like key/value data to extract files from ipynb like in latex conversion
266 resources = {}
274 resources = {}
267
275
268 for preprocessor in self.preprocessors:
276 for preprocessor in self.preprocessors:
269 nb,resources = preprocessor(nb,resources)
277 nb,resources = preprocessor(nb,resources)
270
278
271 return nb, resources
279 return nb, resources
272
280
273 def convert(self):
281 def convert(self):
274 """ convert the ipynb file
282 """ convert the ipynb file
275
283
276 return both the converted ipynb file and a dict containing potential
284 return both the converted ipynb file and a dict containing potential
277 other resources
285 other resources
278 """
286 """
279 nb,resources = self.process()
287 nb,resources = self.process()
280 return self.template.render(nb=nb, inlining=inlining), resources
288 return self.template.render(nb=nb, inlining=inlining), resources
281
289
282
290
283 def read(self, filename):
291 def read(self, filename):
284 "read and parse notebook into NotebookNode called self.nb"
292 "read and parse notebook into NotebookNode called self.nb"
285 with io.open(filename) as f:
293 with io.open(filename) as f:
286 self.nb = nbformat.read(f, 'json')
294 self.nb = nbformat.read(f, 'json')
287
295
@@ -1,80 +1,94 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Imports
3 # Imports
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 from __future__ import print_function
5 from __future__ import print_function
6 import sys
6 import sys
7 import io
7 import io
8 import os
8
9
9 from converters.template import *
10 from converters.template import *
10 from converters.template import ConverterTemplate
11 from converters.template import ConverterTemplate
11 from converters.html import ConverterHTML
12 from converters.html import ConverterHTML
12 # From IPython
13 # From IPython
13
14
14 # All the stuff needed for the configurable things
15 # All the stuff needed for the configurable things
15 from IPython.config.application import Application
16 from IPython.config.application import Application
17 from IPython.config.loader import ConfigFileNotFound
16 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, CaselessStrEnum
18 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, CaselessStrEnum
17
19
18
20
19 class NbconvertApp(Application):
21 class NbconvertApp(Application):
20
22
21
23
22 def __init__(self, **kwargs):
24 def __init__(self, **kwargs):
23 super(NbconvertApp, self).__init__(**kwargs)
25 super(NbconvertApp, self).__init__(**kwargs)
24 self.classes.insert(0,ConverterTemplate)
26 self.classes.insert(0,ConverterTemplate)
25 # ensure those are registerd
27 # ensure those are registerd
26
28
29 def load_config_file(self, profile_name):
30 try:
31 Application.load_config_file(
32 self,
33 profile_name+'.nbcv',
34 path=[os.path.join(os.getcwdu(),'profile')]
35 )
36 except ConfigFileNotFound:
37 self.log.warn("Config file for profile '%s' not found, giving up ",profile_name)
38 exit(1)
39
27
40
28 def initialize(self, argv=None):
41 def initialize(self, argv=None):
29 self.parse_command_line(argv)
42 self.parse_command_line(argv)
30 cl_config = self.config
43 cl_config = self.config
44 profile_file = sys.argv[1]
45 self.load_config_file(profile_file)
31 self.update_config(cl_config)
46 self.update_config(cl_config)
32
47
33
48
34
49
35 def run(self):
50 def run(self):
36 """Convert a notebook to html in one step"""
51 """Convert a notebook to html in one step"""
37 template_file = (self.extra_args or [None])[0]
52 template_file = (self.extra_args or [None])[0]
38 ipynb_file = (self.extra_args or [None])[1]
53 ipynb_file = (self.extra_args or [None])[1]
39
54
40 template_file = sys.argv[1]
55 template_file = sys.argv[1]
41
56
42 if template_file.startswith('latex'):
57 if template_file.startswith('latex'):
43 tex_environement=True
58 tex_environement=True
44 else:
59 else:
45 tex_environement=False
60 tex_environement=False
46
61
47 C = ConverterTemplate(tplfile=sys.argv[1],
62 C = ConverterTemplate(tplfile=sys.argv[1],
48 tex_environement=tex_environement,
49 config=self.config)
63 config=self.config)
50 C.read(ipynb_file)
64 C.read(ipynb_file)
51
65
52 output,resources = C.convert()
66 output,resources = C.convert()
53
67
54 print(output.encode('utf-8'))
68 print(output.encode('utf-8'))
55
69
56 keys = resources.keys()
70 keys = resources.keys()
57 if keys :
71 if keys :
58 print('''
72 print('''
59 ====================== Keys in Resources ==================================
73 ====================== Keys in Resources ==================================
60 ''')
74 ''')
61 print(resources.keys())
75 print(resources.keys())
62 print("""
76 print("""
63 ===========================================================================
77 ===========================================================================
64 you are responsible from writing those data do a file in the right place if
78 you are responsible from writing those data do a file in the right place if
65 they need to be.
79 they need to be.
66 ===========================================================================
80 ===========================================================================
67 """)
81 """)
68
82
69 def main():
83 def main():
70 """Convert a notebook to html in one step"""
84 """Convert a notebook to html in one step"""
71 app = NbconvertApp.instance()
85 app = NbconvertApp.instance()
72 app.initialize()
86 app.initialize()
73 app.start()
87 app.start()
74 app.run()
88 app.run()
75 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
76 # Script main
90 # Script main
77 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
78
92
79 if __name__ == '__main__':
93 if __name__ == '__main__':
80 main()
94 main()
General Comments 0
You need to be logged in to leave comments. Login now