##// END OF EJS Templates
add markdown2html filter...
MinRK -
Show More
@@ -1,338 +1,337 b''
1 """This module defines Exporter, a highly configurable converter
1 """This module defines Exporter, a highly configurable converter
2 that uses Jinja2 to export notebook files into different formats.
2 that uses Jinja2 to export notebook files into different formats.
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from __future__ import print_function, absolute_import
17 from __future__ import print_function, absolute_import
18
18
19 # Stdlib imports
19 # Stdlib imports
20 import io
20 import io
21 import os
21 import os
22 import inspect
22 import inspect
23 from copy import deepcopy
23 from copy import deepcopy
24
24
25 # other libs/dependencies
25 # other libs/dependencies
26 from jinja2 import Environment, FileSystemLoader
26 from jinja2 import Environment, FileSystemLoader
27 from markdown import markdown
28
27
29 # IPython imports
28 # IPython imports
30 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
31 from IPython.config import Config
30 from IPython.config import Config
32 from IPython.nbformat import current as nbformat
31 from IPython.nbformat import current as nbformat
33 from IPython.utils.traitlets import MetaHasTraits, Unicode
32 from IPython.utils.traitlets import MetaHasTraits, Unicode
34 from IPython.utils.text import indent
33 from IPython.utils.text import indent
35
34
36 from IPython.nbconvert import filters
35 from IPython.nbconvert import filters
37 from IPython.nbconvert import transformers
36 from IPython.nbconvert import transformers
38
37
39 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
40 # Globals and constants
39 # Globals and constants
41 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
42
41
43 #Jinja2 extensions to load.
42 #Jinja2 extensions to load.
44 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
43 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
45
44
46 default_filters = {
45 default_filters = {
47 'indent': indent,
46 'indent': indent,
48 'markdown': markdown,
47 'markdown': filters.markdown2html,
49 'ansi2html': filters.ansi2html,
48 'ansi2html': filters.ansi2html,
50 'filter_data_type': filters.DataTypeFilter,
49 'filter_data_type': filters.DataTypeFilter,
51 'get_lines': filters.get_lines,
50 'get_lines': filters.get_lines,
52 'highlight': filters.highlight,
51 'highlight': filters.highlight,
53 'highlight2html': filters.highlight,
52 'highlight2html': filters.highlight,
54 'highlight2latex': filters.highlight2latex,
53 'highlight2latex': filters.highlight2latex,
55 'markdown2latex': filters.markdown2latex,
54 'markdown2latex': filters.markdown2latex,
56 'markdown2rst': filters.markdown2rst,
55 'markdown2rst': filters.markdown2rst,
57 'pycomment': filters.python_comment,
56 'pycomment': filters.python_comment,
58 'rm_ansi': filters.remove_ansi,
57 'rm_ansi': filters.remove_ansi,
59 'rm_dollars': filters.strip_dollars,
58 'rm_dollars': filters.strip_dollars,
60 'rm_fake': filters.rm_fake,
59 'rm_fake': filters.rm_fake,
61 'ansi2latex': filters.ansi2latex,
60 'ansi2latex': filters.ansi2latex,
62 'rm_math_space': filters.rm_math_space,
61 'rm_math_space': filters.rm_math_space,
63 'wrap': filters.wrap
62 'wrap': filters.wrap
64 }
63 }
65
64
66 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
67 # Class
66 # Class
68 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
69
68
70 class Exporter(Configurable):
69 class Exporter(Configurable):
71 """
70 """
72 Exports notebooks into other file formats. Uses Jinja 2 templating engine
71 Exports notebooks into other file formats. Uses Jinja 2 templating engine
73 to output new formats. Inherit from this class if you are creating a new
72 to output new formats. Inherit from this class if you are creating a new
74 template type along with new filters/transformers. If the filters/
73 template type along with new filters/transformers. If the filters/
75 transformers provided by default suffice, there is no need to inherit from
74 transformers provided by default suffice, there is no need to inherit from
76 this class. Instead, override the template_file and file_extension
75 this class. Instead, override the template_file and file_extension
77 traits via a config file.
76 traits via a config file.
78
77
79 {filters}
78 {filters}
80 """
79 """
81
80
82 # finish the docstring
81 # finish the docstring
83 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
82 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
84
83
85
84
86 template_file = Unicode(
85 template_file = Unicode(
87 '', config=True,
86 '', config=True,
88 help="Name of the template file to use")
87 help="Name of the template file to use")
89
88
90 file_extension = Unicode(
89 file_extension = Unicode(
91 'txt', config=True,
90 'txt', config=True,
92 help="Extension of the file that should be written to disk"
91 help="Extension of the file that should be written to disk"
93 )
92 )
94
93
95 template_path = Unicode(
94 template_path = Unicode(
96 os.path.join("..", "templates"), config=True,
95 os.path.join("..", "templates"), config=True,
97 help="Path where the template files are located.")
96 help="Path where the template files are located.")
98
97
99 template_skeleton_path = Unicode(
98 template_skeleton_path = Unicode(
100 os.path.join("..", "templates", "skeleton"), config=True,
99 os.path.join("..", "templates", "skeleton"), config=True,
101 help="Path where the template skeleton files are located.")
100 help="Path where the template skeleton files are located.")
102
101
103 #Jinja block definitions
102 #Jinja block definitions
104 jinja_comment_block_start = Unicode("", config=True)
103 jinja_comment_block_start = Unicode("", config=True)
105 jinja_comment_block_end = Unicode("", config=True)
104 jinja_comment_block_end = Unicode("", config=True)
106 jinja_variable_block_start = Unicode("", config=True)
105 jinja_variable_block_start = Unicode("", config=True)
107 jinja_variable_block_end = Unicode("", config=True)
106 jinja_variable_block_end = Unicode("", config=True)
108 jinja_logic_block_start = Unicode("", config=True)
107 jinja_logic_block_start = Unicode("", config=True)
109 jinja_logic_block_end = Unicode("", config=True)
108 jinja_logic_block_end = Unicode("", config=True)
110
109
111 #Extension that the template files use.
110 #Extension that the template files use.
112 template_extension = Unicode(".tpl", config=True)
111 template_extension = Unicode(".tpl", config=True)
113
112
114 #Processors that process the input data prior to the export, set in the
113 #Processors that process the input data prior to the export, set in the
115 #constructor for this class.
114 #constructor for this class.
116 transformers = None
115 transformers = None
117
116
118
117
119 def __init__(self, transformers=None, filters=None, config=None, **kw):
118 def __init__(self, transformers=None, filters=None, config=None, **kw):
120 """
119 """
121 Public constructor
120 Public constructor
122
121
123 Parameters
122 Parameters
124 ----------
123 ----------
125 transformers : list[of transformer]
124 transformers : list[of transformer]
126 Custom transformers to apply to the notebook prior to engaging
125 Custom transformers to apply to the notebook prior to engaging
127 the Jinja template engine. Any transformers specified here
126 the Jinja template engine. Any transformers specified here
128 will override existing transformers if a naming conflict
127 will override existing transformers if a naming conflict
129 occurs.
128 occurs.
130 filters : dict[of filter]
129 filters : dict[of filter]
131 filters specified here will override existing filters if a naming
130 filters specified here will override existing filters if a naming
132 conflict occurs. Filters are availlable in jinja template through
131 conflict occurs. Filters are availlable in jinja template through
133 the name of the corresponding key. Cf class docstring for
132 the name of the corresponding key. Cf class docstring for
134 availlable default filters.
133 availlable default filters.
135 config : config
134 config : config
136 User configuration instance.
135 User configuration instance.
137 """
136 """
138
137
139 #Call the base class constructor
138 #Call the base class constructor
140 c = self.default_config
139 c = self.default_config
141 if config:
140 if config:
142 c.merge(config)
141 c.merge(config)
143
142
144 super(Exporter, self).__init__(config=c, **kw)
143 super(Exporter, self).__init__(config=c, **kw)
145
144
146 #Standard environment
145 #Standard environment
147 self._init_environment()
146 self._init_environment()
148
147
149 #Add transformers
148 #Add transformers
150 self._register_transformers()
149 self._register_transformers()
151
150
152 #Add filters to the Jinja2 environment
151 #Add filters to the Jinja2 environment
153 self._register_filters()
152 self._register_filters()
154
153
155 #Load user transformers. Overwrite existing transformers if need be.
154 #Load user transformers. Overwrite existing transformers if need be.
156 if transformers :
155 if transformers :
157 for transformer in transformers:
156 for transformer in transformers:
158 self.register_transformer(transformer)
157 self.register_transformer(transformer)
159
158
160 #Load user filters. Overwrite existing filters if need be.
159 #Load user filters. Overwrite existing filters if need be.
161 if not filters is None:
160 if not filters is None:
162 for key, user_filter in filters.iteritems():
161 for key, user_filter in filters.iteritems():
163 self.register_filter(key, user_filter)
162 self.register_filter(key, user_filter)
164
163
165 @property
164 @property
166 def default_config(self):
165 def default_config(self):
167 return Config()
166 return Config()
168
167
169
168
170
169
171 def from_notebook_node(self, nb, resources=None):
170 def from_notebook_node(self, nb, resources=None):
172 """
171 """
173 Convert a notebook from a notebook node instance.
172 Convert a notebook from a notebook node instance.
174
173
175 Parameters
174 Parameters
176 ----------
175 ----------
177 nb : Notebook node
176 nb : Notebook node
178 resources : a dict of additional resources that
177 resources : a dict of additional resources that
179 can be accessed read/write by transformers
178 can be accessed read/write by transformers
180 and filters.
179 and filters.
181 """
180 """
182 if resources is None:
181 if resources is None:
183 resources = {}
182 resources = {}
184 nb, resources = self._preprocess(nb, resources)
183 nb, resources = self._preprocess(nb, resources)
185
184
186 #Load the template file.
185 #Load the template file.
187 self.template = self.environment.get_template(self.template_file+self.template_extension)
186 self.template = self.environment.get_template(self.template_file+self.template_extension)
188
187
189 return self.template.render(nb=nb, resources=resources), resources
188 return self.template.render(nb=nb, resources=resources), resources
190
189
191
190
192 def from_filename(self, filename):
191 def from_filename(self, filename):
193 """
192 """
194 Convert a notebook from a notebook file.
193 Convert a notebook from a notebook file.
195
194
196 Parameters
195 Parameters
197 ----------
196 ----------
198 filename : str
197 filename : str
199 Full filename of the notebook file to open and convert.
198 Full filename of the notebook file to open and convert.
200 """
199 """
201
200
202 with io.open(filename) as f:
201 with io.open(filename) as f:
203 return self.from_notebook_node(nbformat.read(f, 'json'))
202 return self.from_notebook_node(nbformat.read(f, 'json'))
204
203
205
204
206 def from_file(self, file_stream):
205 def from_file(self, file_stream):
207 """
206 """
208 Convert a notebook from a notebook file.
207 Convert a notebook from a notebook file.
209
208
210 Parameters
209 Parameters
211 ----------
210 ----------
212 file_stream : file-like object
211 file_stream : file-like object
213 Notebook file-like object to convert.
212 Notebook file-like object to convert.
214 """
213 """
215 return self.from_notebook_node(nbformat.read(file_stream, 'json'))
214 return self.from_notebook_node(nbformat.read(file_stream, 'json'))
216
215
217
216
218 def register_transformer(self, transformer):
217 def register_transformer(self, transformer):
219 """
218 """
220 Register a transformer.
219 Register a transformer.
221 Transformers are classes that act upon the notebook before it is
220 Transformers are classes that act upon the notebook before it is
222 passed into the Jinja templating engine. Transformers are also
221 passed into the Jinja templating engine. Transformers are also
223 capable of passing additional information to the Jinja
222 capable of passing additional information to the Jinja
224 templating engine.
223 templating engine.
225
224
226 Parameters
225 Parameters
227 ----------
226 ----------
228 transformer : transformer
227 transformer : transformer
229 """
228 """
230 if self.transformers is None:
229 if self.transformers is None:
231 self.transformers = []
230 self.transformers = []
232
231
233 if inspect.isfunction(transformer):
232 if inspect.isfunction(transformer):
234 self.transformers.append(transformer)
233 self.transformers.append(transformer)
235 return transformer
234 return transformer
236 elif isinstance(transformer, MetaHasTraits):
235 elif isinstance(transformer, MetaHasTraits):
237 transformer_instance = transformer(config=self.config)
236 transformer_instance = transformer(config=self.config)
238 self.transformers.append(transformer_instance)
237 self.transformers.append(transformer_instance)
239 return transformer_instance
238 return transformer_instance
240 else:
239 else:
241 transformer_instance = transformer()
240 transformer_instance = transformer()
242 self.transformers.append(transformer_instance)
241 self.transformers.append(transformer_instance)
243 return transformer_instance
242 return transformer_instance
244
243
245
244
246 def register_filter(self, name, filter):
245 def register_filter(self, name, filter):
247 """
246 """
248 Register a filter.
247 Register a filter.
249 A filter is a function that accepts and acts on one string.
248 A filter is a function that accepts and acts on one string.
250 The filters are accesible within the Jinja templating engine.
249 The filters are accesible within the Jinja templating engine.
251
250
252 Parameters
251 Parameters
253 ----------
252 ----------
254 name : str
253 name : str
255 name to give the filter in the Jinja engine
254 name to give the filter in the Jinja engine
256 filter : filter
255 filter : filter
257 """
256 """
258 if inspect.isfunction(filter):
257 if inspect.isfunction(filter):
259 self.environment.filters[name] = filter
258 self.environment.filters[name] = filter
260 elif isinstance(filter, MetaHasTraits):
259 elif isinstance(filter, MetaHasTraits):
261 self.environment.filters[name] = filter(config=self.config)
260 self.environment.filters[name] = filter(config=self.config)
262 else:
261 else:
263 self.environment.filters[name] = filter()
262 self.environment.filters[name] = filter()
264 return self.environment.filters[name]
263 return self.environment.filters[name]
265
264
266
265
267 def _register_transformers(self):
266 def _register_transformers(self):
268 """
267 """
269 Register all of the transformers needed for this exporter.
268 Register all of the transformers needed for this exporter.
270 """
269 """
271
270
272 self.register_transformer(transformers.coalesce_streams)
271 self.register_transformer(transformers.coalesce_streams)
273
272
274 #Remember the figure extraction transformer so it can be enabled and
273 #Remember the figure extraction transformer so it can be enabled and
275 #disabled easily later.
274 #disabled easily later.
276 self.extract_figure_transformer = self.register_transformer(transformers.ExtractFigureTransformer)
275 self.extract_figure_transformer = self.register_transformer(transformers.ExtractFigureTransformer)
277
276
278
277
279 def _register_filters(self):
278 def _register_filters(self):
280 """
279 """
281 Register all of the filters required for the exporter.
280 Register all of the filters required for the exporter.
282 """
281 """
283 for k, v in default_filters.iteritems():
282 for k, v in default_filters.iteritems():
284 self.register_filter(k, v)
283 self.register_filter(k, v)
285
284
286
285
287 def _init_environment(self):
286 def _init_environment(self):
288 """
287 """
289 Create the Jinja templating environment.
288 Create the Jinja templating environment.
290 """
289 """
291 here = os.path.dirname(os.path.realpath(__file__))
290 here = os.path.dirname(os.path.realpath(__file__))
292 self.environment = Environment(
291 self.environment = Environment(
293 loader=FileSystemLoader([
292 loader=FileSystemLoader([
294 os.path.join(here, self.template_path),
293 os.path.join(here, self.template_path),
295 os.path.join(here, self.template_skeleton_path),
294 os.path.join(here, self.template_skeleton_path),
296 ]),
295 ]),
297 extensions=JINJA_EXTENSIONS
296 extensions=JINJA_EXTENSIONS
298 )
297 )
299
298
300 #Set special Jinja2 syntax that will not conflict with latex.
299 #Set special Jinja2 syntax that will not conflict with latex.
301 if self.jinja_logic_block_start:
300 if self.jinja_logic_block_start:
302 self.environment.block_start_string = self.jinja_logic_block_start
301 self.environment.block_start_string = self.jinja_logic_block_start
303 if self.jinja_logic_block_end:
302 if self.jinja_logic_block_end:
304 self.environment.block_end_string = self.jinja_logic_block_end
303 self.environment.block_end_string = self.jinja_logic_block_end
305 if self.jinja_variable_block_start:
304 if self.jinja_variable_block_start:
306 self.environment.variable_start_string = self.jinja_variable_block_start
305 self.environment.variable_start_string = self.jinja_variable_block_start
307 if self.jinja_variable_block_end:
306 if self.jinja_variable_block_end:
308 self.environment.variable_end_string = self.jinja_variable_block_end
307 self.environment.variable_end_string = self.jinja_variable_block_end
309 if self.jinja_comment_block_start:
308 if self.jinja_comment_block_start:
310 self.environment.comment_start_string = self.jinja_comment_block_start
309 self.environment.comment_start_string = self.jinja_comment_block_start
311 if self.jinja_comment_block_end:
310 if self.jinja_comment_block_end:
312 self.environment.comment_end_string = self.jinja_comment_block_end
311 self.environment.comment_end_string = self.jinja_comment_block_end
313
312
314
313
315 def _preprocess(self, nb, resources):
314 def _preprocess(self, nb, resources):
316 """
315 """
317 Preprocess the notebook before passing it into the Jinja engine.
316 Preprocess the notebook before passing it into the Jinja engine.
318 To preprocess the notebook is to apply all of the
317 To preprocess the notebook is to apply all of the
319
318
320 Parameters
319 Parameters
321 ----------
320 ----------
322 nb : notebook node
321 nb : notebook node
323 notebook that is being exported.
322 notebook that is being exported.
324 resources : a dict of additional resources that
323 resources : a dict of additional resources that
325 can be accessed read/write by transformers
324 can be accessed read/write by transformers
326 and filters.
325 and filters.
327 """
326 """
328
327
329 # Do a deepcopy first,
328 # Do a deepcopy first,
330 # we are never safe enough with what the transformers could do.
329 # we are never safe enough with what the transformers could do.
331 nbc = deepcopy(nb)
330 nbc = deepcopy(nb)
332 resc = deepcopy(resources)
331 resc = deepcopy(resources)
333 #Run each transformer on the notebook. Carry the output along
332 #Run each transformer on the notebook. Carry the output along
334 #to each transformer
333 #to each transformer
335 for transformer in self.transformers:
334 for transformer in self.transformers:
336 nb, resources = transformer(nbc, resc)
335 nb, resources = transformer(nbc, resc)
337 return nb, resources
336 return nb, resources
338
337
@@ -1,85 +1,74 b''
1 """Markdown filters
1 """Markdown filters
2 This file contains a collection of utility filters for dealing with
2 This file contains a collection of utility filters for dealing with
3 markdown within Jinja templates.
3 markdown within Jinja templates.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib imports
18 # Stdlib imports
19 import sys
19 import sys
20 import subprocess
20 import subprocess
21
21
22 from IPython.nbconvert.utils.pandoc import pandoc
23
22 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
23 # Functions
25 # Functions
24 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
25
27
26 __all__ = [
28 __all__ = [
29 'markdown2html',
27 'markdown2latex',
30 'markdown2latex',
28 'markdown2rst'
31 'markdown2rst'
29 ]
32 ]
30
33
31
32 def markdown2latex(source):
34 def markdown2latex(source):
33 """Convert a markdown string to LaTeX via pandoc.
35 """Convert a markdown string to LaTeX via pandoc.
34
36
35 This function will raise an error if pandoc is not installed.
37 This function will raise an error if pandoc is not installed.
36 Any error messages generated by pandoc are printed to stderr.
38 Any error messages generated by pandoc are printed to stderr.
37
39
38 Parameters
40 Parameters
39 ----------
41 ----------
40 source : string
42 source : string
41 Input string, assumed to be valid markdown.
43 Input string, assumed to be valid markdown.
42
44
43 Returns
45 Returns
44 -------
46 -------
45 out : string
47 out : string
46 Output as returned by pandoc.
48 Output as returned by pandoc.
47 """
49 """
48 p = subprocess.Popen('pandoc -f markdown -t latex'.split(),
50 return pandoc(source, 'markdown', 'latex')
49 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
50
51 out, err = p.communicate(source.encode('utf-8'))
52
53 if err:
54 print(err, file=sys.stderr)
55 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
56
57 return unicode(out, 'utf-8')[:-1]
58
51
59
52
53 def markdown2html(source):
54 """Convert a markdown string to HTML via pandoc"""
55 return pandoc(source, 'markdown', 'html')
56
60 def markdown2rst(source):
57 def markdown2rst(source):
61 """Convert a markdown string to LaTeX via pandoc.
58 """Convert a markdown string to LaTeX via pandoc.
62
59
63 This function will raise an error if pandoc is not installed.
60 This function will raise an error if pandoc is not installed.
64 Any error messages generated by pandoc are printed to stderr.
61 Any error messages generated by pandoc are printed to stderr.
65
62
66 Parameters
63 Parameters
67 ----------
64 ----------
68 source : string
65 source : string
69 Input string, assumed to be valid markdown.
66 Input string, assumed to be valid markdown.
70
67
71 Returns
68 Returns
72 -------
69 -------
73 out : string
70 out : string
74 Output as returned by pandoc.
71 Output as returned by pandoc.
75 """
72 """
76 p = subprocess.Popen('pandoc -f markdown -t rst'.split(),
73 return pandoc(source, 'markdown', 'rst')
77 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
74
78
79 out, err = p.communicate(source.encode('utf-8'))
80
81 if err:
82 print(err, file=sys.stderr)
83 #print('*'*20+'\n', out, '\n'+'*'*20) # dbg
84
85 return unicode(out, 'utf-8')
General Comments 0
You need to be logged in to leave comments. Login now