##// END OF EJS Templates
flavor=template
Jonathan Frederic -
Show More
@@ -1,471 +1,480 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 import copy
23 import copy
24 import collections
24 import collections
25 import datetime
25 import datetime
26
26
27 # other libs/dependencies
27 # other libs/dependencies
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
29
29
30 # IPython imports
30 # IPython imports
31 from IPython.config.configurable import Configurable
31 from IPython.config.configurable import Configurable
32 from IPython.config import Config
32 from IPython.config import Config
33 from IPython.nbformat import current as nbformat
33 from IPython.nbformat import current as nbformat
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
35 from IPython.utils.importstring import import_item
35 from IPython.utils.importstring import import_item
36 from IPython.utils.text import indent
36 from IPython.utils.text import indent
37 from IPython.utils import py3compat
37 from IPython.utils import py3compat
38
38
39 from IPython.nbconvert import transformers as nbtransformers
39 from IPython.nbconvert import transformers as nbtransformers
40 from IPython.nbconvert import filters
40 from IPython.nbconvert import filters
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals and constants
43 # Globals and constants
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #Jinja2 extensions to load.
46 #Jinja2 extensions to load.
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
48
48
49 default_filters = {
49 default_filters = {
50 'indent': indent,
50 'indent': indent,
51 'markdown2html': filters.markdown2html,
51 'markdown2html': filters.markdown2html,
52 'ansi2html': filters.ansi2html,
52 'ansi2html': filters.ansi2html,
53 'filter_data_type': filters.DataTypeFilter,
53 'filter_data_type': filters.DataTypeFilter,
54 'get_lines': filters.get_lines,
54 'get_lines': filters.get_lines,
55 'highlight2html': filters.highlight2html,
55 'highlight2html': filters.highlight2html,
56 'highlight2latex': filters.highlight2latex,
56 'highlight2latex': filters.highlight2latex,
57 'ipython2python': filters.ipython2python,
57 'ipython2python': filters.ipython2python,
58 'markdown2latex': filters.markdown2latex,
58 'markdown2latex': filters.markdown2latex,
59 'markdown2rst': filters.markdown2rst,
59 'markdown2rst': filters.markdown2rst,
60 'comment_lines': filters.comment_lines,
60 'comment_lines': filters.comment_lines,
61 'strip_ansi': filters.strip_ansi,
61 'strip_ansi': filters.strip_ansi,
62 'strip_dollars': filters.strip_dollars,
62 'strip_dollars': filters.strip_dollars,
63 'strip_files_prefix': filters.strip_files_prefix,
63 'strip_files_prefix': filters.strip_files_prefix,
64 'html2text' : filters.html2text,
64 'html2text' : filters.html2text,
65 'add_anchor': filters.add_anchor,
65 'add_anchor': filters.add_anchor,
66 'ansi2latex': filters.ansi2latex,
66 'ansi2latex': filters.ansi2latex,
67 'strip_math_space': filters.strip_math_space,
67 'strip_math_space': filters.strip_math_space,
68 'wrap_text': filters.wrap_text,
68 'wrap_text': filters.wrap_text,
69 'escape_latex': filters.escape_latex
69 'escape_latex': filters.escape_latex
70 }
70 }
71
71
72 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
73 # Class
73 # Class
74 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
75
75
76 class ResourcesDict(collections.defaultdict):
76 class ResourcesDict(collections.defaultdict):
77 def __missing__(self, key):
77 def __missing__(self, key):
78 return ''
78 return ''
79
79
80
80
81 class Exporter(Configurable):
81 class Exporter(Configurable):
82 """
82 """
83 Exports notebooks into other file formats. Uses Jinja 2 templating engine
83 Exports notebooks into other file formats. Uses Jinja 2 templating engine
84 to output new formats. Inherit from this class if you are creating a new
84 to output new formats. Inherit from this class if you are creating a new
85 template type along with new filters/transformers. If the filters/
85 template type along with new filters/transformers. If the filters/
86 transformers provided by default suffice, there is no need to inherit from
86 transformers provided by default suffice, there is no need to inherit from
87 this class. Instead, override the template_file and file_extension
87 this class. Instead, override the template_file and file_extension
88 traits via a config file.
88 traits via a config file.
89 """
89 """
90
90
91 # finish the docstring
91 # finish the docstring
92 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
92 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
93
93
94
94
95 flavor = Unicode(config=True, help="""Flavor of the data format to use.
95 template_file = Unicode(u'default',
96 I.E. 'full' or 'basic'""")
97
98 template_file = Unicode(
99 config=True,
96 config=True,
100 help="Name of the template file to use")
97 help="Name of the template file to use")
98 def _template_file_changed(self, name, old, new):
99 if new=='default':
100 self.template_file = self.default_template
101 else:
102 self.template_file = new
103 default_template = Unicode(u'')
101
104
102 file_extension = Unicode(
105 file_extension = Unicode(
103 'txt', config=True,
106 'txt', config=True,
104 help="Extension of the file that should be written to disk"
107 help="Extension of the file that should be written to disk"
105 )
108 )
106
109
107 template_path = List(['.'], config=True)
110 template_path = List(['.'], config=True)
108
111
109 default_template_path = Unicode(
112 default_template_path = Unicode(
110 os.path.join("..", "templates"),
113 os.path.join("..", "templates"),
111 help="Path where the template files are located.")
114 help="Path where the template files are located.")
112
115
113 template_skeleton_path = Unicode(
116 template_skeleton_path = Unicode(
114 os.path.join("..", "templates", "skeleton"),
117 os.path.join("..", "templates", "skeleton"),
115 help="Path where the template skeleton files are located.")
118 help="Path where the template skeleton files are located.")
116
119
117 #Jinja block definitions
120 #Jinja block definitions
118 jinja_comment_block_start = Unicode("", config=True)
121 jinja_comment_block_start = Unicode("", config=True)
119 jinja_comment_block_end = Unicode("", config=True)
122 jinja_comment_block_end = Unicode("", config=True)
120 jinja_variable_block_start = Unicode("", config=True)
123 jinja_variable_block_start = Unicode("", config=True)
121 jinja_variable_block_end = Unicode("", config=True)
124 jinja_variable_block_end = Unicode("", config=True)
122 jinja_logic_block_start = Unicode("", config=True)
125 jinja_logic_block_start = Unicode("", config=True)
123 jinja_logic_block_end = Unicode("", config=True)
126 jinja_logic_block_end = Unicode("", config=True)
124
127
125 #Extension that the template files use.
128 #Extension that the template files use.
126 template_extension = Unicode(".tpl", config=True)
129 template_extension = Unicode(".tpl", config=True)
127
130
128 #Configurability, allows the user to easily add filters and transformers.
131 #Configurability, allows the user to easily add filters and transformers.
129 transformers = List(config=True,
132 transformers = List(config=True,
130 help="""List of transformers, by name or namespace, to enable.""")
133 help="""List of transformers, by name or namespace, to enable.""")
131
134
132 filters = Dict(config=True,
135 filters = Dict(config=True,
133 help="""Dictionary of filters, by name and namespace, to add to the Jinja
136 help="""Dictionary of filters, by name and namespace, to add to the Jinja
134 environment.""")
137 environment.""")
135
138
136 default_transformers = List([nbtransformers.coalesce_streams,
139 default_transformers = List([nbtransformers.coalesce_streams,
137 nbtransformers.SVG2PDFTransformer,
140 nbtransformers.SVG2PDFTransformer,
138 nbtransformers.ExtractOutputTransformer,
141 nbtransformers.ExtractOutputTransformer,
139 nbtransformers.CSSHTMLHeaderTransformer,
142 nbtransformers.CSSHTMLHeaderTransformer,
140 nbtransformers.RevealHelpTransformer,
143 nbtransformers.RevealHelpTransformer,
141 nbtransformers.LatexTransformer,
144 nbtransformers.LatexTransformer,
142 nbtransformers.SphinxTransformer],
145 nbtransformers.SphinxTransformer],
143 config=True,
146 config=True,
144 help="""List of transformers available by default, by name, namespace,
147 help="""List of transformers available by default, by name, namespace,
145 instance, or type.""")
148 instance, or type.""")
146
149
147
150
148 def __init__(self, config=None, extra_loaders=None, **kw):
151 def __init__(self, config=None, extra_loaders=None, **kw):
149 """
152 """
150 Public constructor
153 Public constructor
151
154
152 Parameters
155 Parameters
153 ----------
156 ----------
154 config : config
157 config : config
155 User configuration instance.
158 User configuration instance.
156 extra_loaders : list[of Jinja Loaders]
159 extra_loaders : list[of Jinja Loaders]
157 ordered list of Jinja loder to find templates. Will be tried in order
160 ordered list of Jinja loder to find templates. Will be tried in order
158 before the default FileSysteme ones.
161 before the default FileSysteme ones.
159 flavor : str
162 template : str (optional, kw arg)
160 Flavor to use when exporting. This determines what template to use
163 Template to use when exporting.
161 if one hasn't been specifically provided.
162 """
164 """
163
165
164 #Call the base class constructor
166 #Call the base class constructor
165 c = self.default_config
167 c = self.default_config
166 if config:
168 if config:
167 c.merge(config)
169 c.merge(config)
168
170
169 super(Exporter, self).__init__(config=c, **kw)
171 super(Exporter, self).__init__(config=c, **kw)
170
172
171 #Init
173 #Init
172 self._init_template(**kw)
174 self._init_template(**kw)
173 self._init_environment(extra_loaders=extra_loaders)
175 self._init_environment(extra_loaders=extra_loaders)
174 self._init_transformers()
176 self._init_transformers()
175 self._init_filters()
177 self._init_filters()
176
178
177
179
178 @property
180 @property
179 def default_config(self):
181 def default_config(self):
180 return Config()
182 return Config()
181
183
182
184
183 def from_notebook_node(self, nb, resources=None, **kw):
185 def from_notebook_node(self, nb, resources=None, **kw):
184 """
186 """
185 Convert a notebook from a notebook node instance.
187 Convert a notebook from a notebook node instance.
186
188
187 Parameters
189 Parameters
188 ----------
190 ----------
189 nb : Notebook node
191 nb : Notebook node
190 resources : dict (**kw)
192 resources : dict (**kw)
191 of additional resources that can be accessed read/write by
193 of additional resources that can be accessed read/write by
192 transformers and filters.
194 transformers and filters.
193 """
195 """
194 nb_copy = copy.deepcopy(nb)
196 nb_copy = copy.deepcopy(nb)
195 resources = self._init_resources(resources)
197 resources = self._init_resources(resources)
196
198
197 #Preprocess
199 # Preprocess
198 nb_copy, resources = self._transform(nb_copy, resources)
200 nb_copy, resources = self._transform(nb_copy, resources)
199
201
200 #Convert
202 # Try different template names during conversion. First try to load the
201 self.template = self.environment.get_template(self.template_file + self.template_extension)
203 # template by name with extension added, then try loading the template
202 output = self.template.render(nb=nb_copy, resources=resources)
204 # as if the name is explicitly specified, then try the name as a
205 # 'flavor', and lastly just try to load the template by module name.
206 module_name = self.__module__.split('.')[-1]
207 try_names = [self.template_file + self.template_extension,
208 self.template_file,
209 module_name + '_' + self.template_file + self.template_extension,
210 module_name + self.template_extension]
211 for try_name in try_names:
212 try:
213 self.template = self.environment.get_template(try_name)
214 break
215 except:
216 pass
217
218 if hasattr(self, 'template'):
219 output = self.template.render(nb=nb_copy, resources=resources)
220 else:
221 raise IOError('template file "%s" could not be found' % self.template_file)
203 return output, resources
222 return output, resources
204
223
205
224
206 def from_filename(self, filename, resources=None, **kw):
225 def from_filename(self, filename, resources=None, **kw):
207 """
226 """
208 Convert a notebook from a notebook file.
227 Convert a notebook from a notebook file.
209
228
210 Parameters
229 Parameters
211 ----------
230 ----------
212 filename : str
231 filename : str
213 Full filename of the notebook file to open and convert.
232 Full filename of the notebook file to open and convert.
214 """
233 """
215
234
216 #Pull the metadata from the filesystem.
235 #Pull the metadata from the filesystem.
217 if resources is None:
236 if resources is None:
218 resources = ResourcesDict()
237 resources = ResourcesDict()
219 if not 'metadata' in resources or resources['metadata'] == '':
238 if not 'metadata' in resources or resources['metadata'] == '':
220 resources['metadata'] = ResourcesDict()
239 resources['metadata'] = ResourcesDict()
221 basename = os.path.basename(filename)
240 basename = os.path.basename(filename)
222 notebook_name = basename[:basename.rfind('.')]
241 notebook_name = basename[:basename.rfind('.')]
223 resources['metadata']['name'] = notebook_name
242 resources['metadata']['name'] = notebook_name
224
243
225 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
244 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
226 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
245 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
227
246
228 with io.open(filename) as f:
247 with io.open(filename) as f:
229 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
248 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
230
249
231
250
232 def from_file(self, file_stream, resources=None, **kw):
251 def from_file(self, file_stream, resources=None, **kw):
233 """
252 """
234 Convert a notebook from a notebook file.
253 Convert a notebook from a notebook file.
235
254
236 Parameters
255 Parameters
237 ----------
256 ----------
238 file_stream : file-like object
257 file_stream : file-like object
239 Notebook file-like object to convert.
258 Notebook file-like object to convert.
240 """
259 """
241 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
260 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
242
261
243
262
244 def register_transformer(self, transformer, enabled=False):
263 def register_transformer(self, transformer, enabled=False):
245 """
264 """
246 Register a transformer.
265 Register a transformer.
247 Transformers are classes that act upon the notebook before it is
266 Transformers are classes that act upon the notebook before it is
248 passed into the Jinja templating engine. Transformers are also
267 passed into the Jinja templating engine. Transformers are also
249 capable of passing additional information to the Jinja
268 capable of passing additional information to the Jinja
250 templating engine.
269 templating engine.
251
270
252 Parameters
271 Parameters
253 ----------
272 ----------
254 transformer : transformer
273 transformer : transformer
255 """
274 """
256 if transformer is None:
275 if transformer is None:
257 raise TypeError('transformer')
276 raise TypeError('transformer')
258 isclass = isinstance(transformer, type)
277 isclass = isinstance(transformer, type)
259 constructed = not isclass
278 constructed = not isclass
260
279
261 #Handle transformer's registration based on it's type
280 #Handle transformer's registration based on it's type
262 if constructed and isinstance(transformer, py3compat.string_types):
281 if constructed and isinstance(transformer, py3compat.string_types):
263 #Transformer is a string, import the namespace and recursively call
282 #Transformer is a string, import the namespace and recursively call
264 #this register_transformer method
283 #this register_transformer method
265 transformer_cls = import_item(transformer)
284 transformer_cls = import_item(transformer)
266 return self.register_transformer(transformer_cls, enabled)
285 return self.register_transformer(transformer_cls, enabled)
267
286
268 if constructed and hasattr(transformer, '__call__'):
287 if constructed and hasattr(transformer, '__call__'):
269 #Transformer is a function, no need to construct it.
288 #Transformer is a function, no need to construct it.
270 #Register and return the transformer.
289 #Register and return the transformer.
271 if enabled:
290 if enabled:
272 transformer.enabled = True
291 transformer.enabled = True
273 self._transformers.append(transformer)
292 self._transformers.append(transformer)
274 return transformer
293 return transformer
275
294
276 elif isclass and isinstance(transformer, MetaHasTraits):
295 elif isclass and isinstance(transformer, MetaHasTraits):
277 #Transformer is configurable. Make sure to pass in new default for
296 #Transformer is configurable. Make sure to pass in new default for
278 #the enabled flag if one was specified.
297 #the enabled flag if one was specified.
279 self.register_transformer(transformer(parent=self), enabled)
298 self.register_transformer(transformer(parent=self), enabled)
280
299
281 elif isclass:
300 elif isclass:
282 #Transformer is not configurable, construct it
301 #Transformer is not configurable, construct it
283 self.register_transformer(transformer(), enabled)
302 self.register_transformer(transformer(), enabled)
284
303
285 else:
304 else:
286 #Transformer is an instance of something without a __call__
305 #Transformer is an instance of something without a __call__
287 #attribute.
306 #attribute.
288 raise TypeError('transformer')
307 raise TypeError('transformer')
289
308
290
309
291 def register_filter(self, name, jinja_filter):
310 def register_filter(self, name, jinja_filter):
292 """
311 """
293 Register a filter.
312 Register a filter.
294 A filter is a function that accepts and acts on one string.
313 A filter is a function that accepts and acts on one string.
295 The filters are accesible within the Jinja templating engine.
314 The filters are accesible within the Jinja templating engine.
296
315
297 Parameters
316 Parameters
298 ----------
317 ----------
299 name : str
318 name : str
300 name to give the filter in the Jinja engine
319 name to give the filter in the Jinja engine
301 filter : filter
320 filter : filter
302 """
321 """
303 if jinja_filter is None:
322 if jinja_filter is None:
304 raise TypeError('filter')
323 raise TypeError('filter')
305 isclass = isinstance(jinja_filter, type)
324 isclass = isinstance(jinja_filter, type)
306 constructed = not isclass
325 constructed = not isclass
307
326
308 #Handle filter's registration based on it's type
327 #Handle filter's registration based on it's type
309 if constructed and isinstance(jinja_filter, py3compat.string_types):
328 if constructed and isinstance(jinja_filter, py3compat.string_types):
310 #filter is a string, import the namespace and recursively call
329 #filter is a string, import the namespace and recursively call
311 #this register_filter method
330 #this register_filter method
312 filter_cls = import_item(jinja_filter)
331 filter_cls = import_item(jinja_filter)
313 return self.register_filter(name, filter_cls)
332 return self.register_filter(name, filter_cls)
314
333
315 if constructed and hasattr(jinja_filter, '__call__'):
334 if constructed and hasattr(jinja_filter, '__call__'):
316 #filter is a function, no need to construct it.
335 #filter is a function, no need to construct it.
317 self.environment.filters[name] = jinja_filter
336 self.environment.filters[name] = jinja_filter
318 return jinja_filter
337 return jinja_filter
319
338
320 elif isclass and isinstance(jinja_filter, MetaHasTraits):
339 elif isclass and isinstance(jinja_filter, MetaHasTraits):
321 #filter is configurable. Make sure to pass in new default for
340 #filter is configurable. Make sure to pass in new default for
322 #the enabled flag if one was specified.
341 #the enabled flag if one was specified.
323 filter_instance = jinja_filter(parent=self)
342 filter_instance = jinja_filter(parent=self)
324 self.register_filter(name, filter_instance )
343 self.register_filter(name, filter_instance )
325
344
326 elif isclass:
345 elif isclass:
327 #filter is not configurable, construct it
346 #filter is not configurable, construct it
328 filter_instance = jinja_filter()
347 filter_instance = jinja_filter()
329 self.register_filter(name, filter_instance)
348 self.register_filter(name, filter_instance)
330
349
331 else:
350 else:
332 #filter is an instance of something without a __call__
351 #filter is an instance of something without a __call__
333 #attribute.
352 #attribute.
334 raise TypeError('filter')
353 raise TypeError('filter')
335
354
336
355
337 def _init_template(self, **kw):
356 def _init_template(self, **kw):
338 """
357 """
339 Make sure a template name is specified. If one isn't specified, try to
358 Make sure a template name is specified. If one isn't specified, try to
340 build one from the information we know.
359 build one from the information we know.
341 """
360 """
342
361 self._template_file_changed('template_file', self.template_file, self.template_file)
343 # Set the template_file if it has not been set explicitly.
362 if 'template' in kw:
344 if not self.template_file:
363 self.template_file = kw['template']
345
346 # Build the template file name from the name of the exporter and the
347 # flavor (if available). The flavor can be set on the traitlet
348 # or passed in as a kw arg. The flavor specified in kw overrides
349 # what is set in the flavor traitlet.
350 module_name = self.__module__.split('.')[-1]
351 if self.flavor or 'flavor' in kw:
352 self.template_file = module_name + '_' + kw.get('flavor', self.flavor)
353 else:
354 self.template_file = module_name
355
364
356
365
357 def _init_environment(self, extra_loaders=None):
366 def _init_environment(self, extra_loaders=None):
358 """
367 """
359 Create the Jinja templating environment.
368 Create the Jinja templating environment.
360 """
369 """
361 here = os.path.dirname(os.path.realpath(__file__))
370 here = os.path.dirname(os.path.realpath(__file__))
362 loaders = []
371 loaders = []
363 if extra_loaders:
372 if extra_loaders:
364 loaders.extend(extra_loaders)
373 loaders.extend(extra_loaders)
365
374
366 paths = self.template_path
375 paths = self.template_path
367 paths.extend([os.path.join(here, self.default_template_path),
376 paths.extend([os.path.join(here, self.default_template_path),
368 os.path.join(here, self.template_skeleton_path)])
377 os.path.join(here, self.template_skeleton_path)])
369 loaders.append(FileSystemLoader(paths))
378 loaders.append(FileSystemLoader(paths))
370
379
371 self.environment = Environment(
380 self.environment = Environment(
372 loader= ChoiceLoader(loaders),
381 loader= ChoiceLoader(loaders),
373 extensions=JINJA_EXTENSIONS
382 extensions=JINJA_EXTENSIONS
374 )
383 )
375
384
376 #Set special Jinja2 syntax that will not conflict with latex.
385 #Set special Jinja2 syntax that will not conflict with latex.
377 if self.jinja_logic_block_start:
386 if self.jinja_logic_block_start:
378 self.environment.block_start_string = self.jinja_logic_block_start
387 self.environment.block_start_string = self.jinja_logic_block_start
379 if self.jinja_logic_block_end:
388 if self.jinja_logic_block_end:
380 self.environment.block_end_string = self.jinja_logic_block_end
389 self.environment.block_end_string = self.jinja_logic_block_end
381 if self.jinja_variable_block_start:
390 if self.jinja_variable_block_start:
382 self.environment.variable_start_string = self.jinja_variable_block_start
391 self.environment.variable_start_string = self.jinja_variable_block_start
383 if self.jinja_variable_block_end:
392 if self.jinja_variable_block_end:
384 self.environment.variable_end_string = self.jinja_variable_block_end
393 self.environment.variable_end_string = self.jinja_variable_block_end
385 if self.jinja_comment_block_start:
394 if self.jinja_comment_block_start:
386 self.environment.comment_start_string = self.jinja_comment_block_start
395 self.environment.comment_start_string = self.jinja_comment_block_start
387 if self.jinja_comment_block_end:
396 if self.jinja_comment_block_end:
388 self.environment.comment_end_string = self.jinja_comment_block_end
397 self.environment.comment_end_string = self.jinja_comment_block_end
389
398
390
399
391 def _init_transformers(self):
400 def _init_transformers(self):
392 """
401 """
393 Register all of the transformers needed for this exporter, disabled
402 Register all of the transformers needed for this exporter, disabled
394 unless specified explicitly.
403 unless specified explicitly.
395 """
404 """
396 self._transformers = []
405 self._transformers = []
397
406
398 #Load default transformers (not necessarly enabled by default).
407 #Load default transformers (not necessarly enabled by default).
399 if self.default_transformers:
408 if self.default_transformers:
400 for transformer in self.default_transformers:
409 for transformer in self.default_transformers:
401 self.register_transformer(transformer)
410 self.register_transformer(transformer)
402
411
403 #Load user transformers. Enable by default.
412 #Load user transformers. Enable by default.
404 if self.transformers:
413 if self.transformers:
405 for transformer in self.transformers:
414 for transformer in self.transformers:
406 self.register_transformer(transformer, enabled=True)
415 self.register_transformer(transformer, enabled=True)
407
416
408
417
409 def _init_filters(self):
418 def _init_filters(self):
410 """
419 """
411 Register all of the filters required for the exporter.
420 Register all of the filters required for the exporter.
412 """
421 """
413
422
414 #Add default filters to the Jinja2 environment
423 #Add default filters to the Jinja2 environment
415 for key, value in default_filters.items():
424 for key, value in default_filters.items():
416 self.register_filter(key, value)
425 self.register_filter(key, value)
417
426
418 #Load user filters. Overwrite existing filters if need be.
427 #Load user filters. Overwrite existing filters if need be.
419 if self.filters:
428 if self.filters:
420 for key, user_filter in self.filters.items():
429 for key, user_filter in self.filters.items():
421 self.register_filter(key, user_filter)
430 self.register_filter(key, user_filter)
422
431
423
432
424 def _init_resources(self, resources):
433 def _init_resources(self, resources):
425
434
426 #Make sure the resources dict is of ResourcesDict type.
435 #Make sure the resources dict is of ResourcesDict type.
427 if resources is None:
436 if resources is None:
428 resources = ResourcesDict()
437 resources = ResourcesDict()
429 if not isinstance(resources, ResourcesDict):
438 if not isinstance(resources, ResourcesDict):
430 new_resources = ResourcesDict()
439 new_resources = ResourcesDict()
431 new_resources.update(resources)
440 new_resources.update(resources)
432 resources = new_resources
441 resources = new_resources
433
442
434 #Make sure the metadata extension exists in resources
443 #Make sure the metadata extension exists in resources
435 if 'metadata' in resources:
444 if 'metadata' in resources:
436 if not isinstance(resources['metadata'], ResourcesDict):
445 if not isinstance(resources['metadata'], ResourcesDict):
437 resources['metadata'] = ResourcesDict(resources['metadata'])
446 resources['metadata'] = ResourcesDict(resources['metadata'])
438 else:
447 else:
439 resources['metadata'] = ResourcesDict()
448 resources['metadata'] = ResourcesDict()
440 if not resources['metadata']['name']:
449 if not resources['metadata']['name']:
441 resources['metadata']['name'] = 'Notebook'
450 resources['metadata']['name'] = 'Notebook'
442
451
443 #Set the output extension
452 #Set the output extension
444 resources['output_extension'] = self.file_extension
453 resources['output_extension'] = self.file_extension
445 return resources
454 return resources
446
455
447
456
448 def _transform(self, nb, resources):
457 def _transform(self, nb, resources):
449 """
458 """
450 Preprocess the notebook before passing it into the Jinja engine.
459 Preprocess the notebook before passing it into the Jinja engine.
451 To preprocess the notebook is to apply all of the
460 To preprocess the notebook is to apply all of the
452
461
453 Parameters
462 Parameters
454 ----------
463 ----------
455 nb : notebook node
464 nb : notebook node
456 notebook that is being exported.
465 notebook that is being exported.
457 resources : a dict of additional resources that
466 resources : a dict of additional resources that
458 can be accessed read/write by transformers
467 can be accessed read/write by transformers
459 and filters.
468 and filters.
460 """
469 """
461
470
462 # Do a copy.deepcopy first,
471 # Do a copy.deepcopy first,
463 # we are never safe enough with what the transformers could do.
472 # we are never safe enough with what the transformers could do.
464 nbc = copy.deepcopy(nb)
473 nbc = copy.deepcopy(nb)
465 resc = copy.deepcopy(resources)
474 resc = copy.deepcopy(resources)
466
475
467 #Run each transformer on the notebook. Carry the output along
476 #Run each transformer on the notebook. Carry the output along
468 #to each transformer
477 #to each transformer
469 for transformer in self._transformers:
478 for transformer in self._transformers:
470 nbc, resc = transformer(nbc, resc)
479 nbc, resc = transformer(nbc, resc)
471 return nbc, resc
480 return nbc, resc
@@ -1,55 +1,52 b''
1 """
1 """
2 Exporter that exports Basic HTML.
2 Exporter that exports Basic HTML.
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 IPython.utils.traitlets import Unicode, List
17 from IPython.utils.traitlets import Unicode, List
18
18
19 from IPython.nbconvert import transformers
19 from IPython.nbconvert import transformers
20 from IPython.config import Config
20 from IPython.config import Config
21
21
22 from .exporter import Exporter
22 from .exporter import Exporter
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class HTMLExporter(Exporter):
28 class HTMLExporter(Exporter):
29 """
29 """
30 Exports a basic HTML document. This exporter assists with the export of
30 Exports a basic HTML document. This exporter assists with the export of
31 HTML. Inherit from it if you are writing your own HTML template and need
31 HTML. Inherit from it if you are writing your own HTML template and need
32 custom transformers/filters. If you don't need custom transformers/
32 custom transformers/filters. If you don't need custom transformers/
33 filters, just change the 'template_file' config option.
33 filters, just change the 'template_file' config option.
34 """
34 """
35
35
36 file_extension = Unicode(
36 file_extension = Unicode(
37 'html', config=True,
37 'html', config=True,
38 help="Extension of the file that should be written to disk"
38 help="Extension of the file that should be written to disk"
39 )
39 )
40
40
41 flavor = Unicode('full', config=True, help="""Flavor of the data format to
41 default_template = Unicode('full', config=True, help="""Flavor of the data
42 use. I.E. 'full' or 'basic'""")
42 format to use. I.E. 'full' or 'basic'""")
43
43
44 @property
44 @property
45 def default_config(self):
45 def default_config(self):
46 c = Config({
46 c = Config({
47 'CSSHTMLHeaderTransformer':{
47 'CSSHTMLHeaderTransformer':{
48 'enabled':True
48 'enabled':True
49 },
49 }
50 'RevealHelpTransformer':{
51 'enabled':True,
52 },
53 })
50 })
54 c.merge(super(HTMLExporter,self).default_config)
51 c.merge(super(HTMLExporter,self).default_config)
55 return c
52 return c
@@ -1,91 +1,91 b''
1 """
1 """
2 Exporter that allows Latex Jinja templates to work. Contains logic to
2 Exporter that allows Latex Jinja templates to work. Contains logic to
3 appropriately prepare IPYNB files for export to LaTeX. Including but
3 appropriately prepare IPYNB files for export to LaTeX. Including but
4 not limited to escaping LaTeX, fixing math region tags, using special
4 not limited to escaping LaTeX, fixing math region tags, using special
5 tags to circumvent Jinja/Latex syntax conflicts.
5 tags to circumvent Jinja/Latex syntax conflicts.
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (c) 2013, the IPython Development Team.
8 # Copyright (c) 2013, the IPython Development Team.
9 #
9 #
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11 #
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # Stdlib imports
19 # Stdlib imports
20 import os
20 import os
21
21
22 # IPython imports
22 # IPython imports
23 from IPython.utils.traitlets import Unicode, List
23 from IPython.utils.traitlets import Unicode, List
24 from IPython.config import Config
24 from IPython.config import Config
25
25
26 from IPython.nbconvert import filters, transformers
26 from IPython.nbconvert import filters, transformers
27 from .exporter import Exporter
27 from .exporter import Exporter
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Classes and functions
30 # Classes and functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 class LatexExporter(Exporter):
33 class LatexExporter(Exporter):
34 """
34 """
35 Exports to a Latex template. Inherit from this class if your template is
35 Exports to a Latex template. Inherit from this class if your template is
36 LaTeX based and you need custom tranformers/filters. Inherit from it if
36 LaTeX based and you need custom tranformers/filters. Inherit from it if
37 you are writing your own HTML template and need custom tranformers/filters.
37 you are writing your own HTML template and need custom tranformers/filters.
38 If you don't need custom tranformers/filters, just change the
38 If you don't need custom tranformers/filters, just change the
39 'template_file' config option. Place your template in the special "/latex"
39 'template_file' config option. Place your template in the special "/latex"
40 subfolder of the "../templates" folder.
40 subfolder of the "../templates" folder.
41 """
41 """
42
42
43 file_extension = Unicode(
43 file_extension = Unicode(
44 'tex', config=True,
44 'tex', config=True,
45 help="Extension of the file that should be written to disk")
45 help="Extension of the file that should be written to disk")
46
46
47 flavor = Unicode('article', config=True, help="""Flavor of the data format to
47 default_template = Unicode('article', config=True, help="""Template of the
48 use. I.E. 'full' or 'basic'""")
48 data format to use. I.E. 'full' or 'basic'""")
49
49
50 #Latex constants
50 #Latex constants
51 default_template_path = Unicode(
51 default_template_path = Unicode(
52 os.path.join("..", "templates", "latex"), config=True,
52 os.path.join("..", "templates", "latex"), config=True,
53 help="Path where the template files are located.")
53 help="Path where the template files are located.")
54
54
55 template_skeleton_path = Unicode(
55 template_skeleton_path = Unicode(
56 os.path.join("..", "templates", "latex", "skeleton"), config=True,
56 os.path.join("..", "templates", "latex", "skeleton"), config=True,
57 help="Path where the template skeleton files are located.")
57 help="Path where the template skeleton files are located.")
58
58
59 #Special Jinja2 syntax that will not conflict when exporting latex.
59 #Special Jinja2 syntax that will not conflict when exporting latex.
60 jinja_comment_block_start = Unicode("((=", config=True)
60 jinja_comment_block_start = Unicode("((=", config=True)
61 jinja_comment_block_end = Unicode("=))", config=True)
61 jinja_comment_block_end = Unicode("=))", config=True)
62 jinja_variable_block_start = Unicode("(((", config=True)
62 jinja_variable_block_start = Unicode("(((", config=True)
63 jinja_variable_block_end = Unicode(")))", config=True)
63 jinja_variable_block_end = Unicode(")))", config=True)
64 jinja_logic_block_start = Unicode("((*", config=True)
64 jinja_logic_block_start = Unicode("((*", config=True)
65 jinja_logic_block_end = Unicode("*))", config=True)
65 jinja_logic_block_end = Unicode("*))", config=True)
66
66
67 #Extension that the template files use.
67 #Extension that the template files use.
68 template_extension = Unicode(".tplx", config=True)
68 template_extension = Unicode(".tplx", config=True)
69
69
70
70
71 @property
71 @property
72 def default_config(self):
72 def default_config(self):
73 c = Config({
73 c = Config({
74 'NbConvertBase': {
74 'NbConvertBase': {
75 'display_data_priority' : ['latex', 'pdf', 'png', 'jpg', 'svg', 'jpeg', 'text']
75 'display_data_priority' : ['latex', 'pdf', 'png', 'jpg', 'svg', 'jpeg', 'text']
76 },
76 },
77 'ExtractOutputTransformer': {
77 'ExtractOutputTransformer': {
78 'enabled':True
78 'enabled':True
79 },
79 },
80 'SVG2PDFTransformer': {
80 'SVG2PDFTransformer': {
81 'enabled':True
81 'enabled':True
82 },
82 },
83 'LatexTransformer': {
83 'LatexTransformer': {
84 'enabled':True
84 'enabled':True
85 },
85 },
86 'SphinxTransformer': {
86 'SphinxTransformer': {
87 'enabled':True
87 'enabled':True
88 }
88 }
89 })
89 })
90 c.merge(super(LatexExporter,self).default_config)
90 c.merge(super(LatexExporter,self).default_config)
91 return c
91 return c
@@ -1,52 +1,52 b''
1 """
1 """
2 Exporter that exports Basic HTML.
2 Exporter that exports Basic HTML.
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 IPython.utils.traitlets import Unicode
17 from IPython.utils.traitlets import Unicode
18
18
19 from IPython.nbconvert import transformers
19 from IPython.nbconvert import transformers
20 from IPython.config import Config
20 from IPython.config import Config
21
21
22 from .exporter import Exporter
22 from .exporter import Exporter
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class SlidesExporter(Exporter):
28 class SlidesExporter(Exporter):
29 """
29 """
30 Exports slides
30 Exports slides
31 """
31 """
32
32
33 file_extension = Unicode(
33 file_extension = Unicode(
34 'html', config=True,
34 'html', config=True,
35 help="Extension of the file that should be written to disk"
35 help="Extension of the file that should be written to disk"
36 )
36 )
37
37
38 flavor = Unicode('reveal', config=True, help="""Flavor of the data format to
38 default_template = Unicode('reveal', config=True, help="""Template of the
39 use. I.E. 'reveal'""")
39 data format to use. I.E. 'reveal'""")
40
40
41 @property
41 @property
42 def default_config(self):
42 def default_config(self):
43 c = Config({
43 c = Config({
44 'CSSHTMLHeaderTransformer':{
44 'CSSHTMLHeaderTransformer':{
45 'enabled':True
45 'enabled':True
46 },
46 },
47 'RevealHelpTransformer':{
47 'RevealHelpTransformer':{
48 'enabled':True,
48 'enabled':True,
49 },
49 },
50 })
50 })
51 c.merge(super(SlidesExporter,self).default_config)
51 c.merge(super(SlidesExporter,self).default_config)
52 return c
52 return c
@@ -1,56 +1,56 b''
1 """
1 """
2 Module with tests for html.py
2 Module with tests for html.py
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 .base import ExportersTestsBase
17 from .base import ExportersTestsBase
18 from ..html import HTMLExporter
18 from ..html import HTMLExporter
19 from IPython.testing.decorators import onlyif_cmds_exist
19 from IPython.testing.decorators import onlyif_cmds_exist
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Class
22 # Class
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class TestHTMLExporter(ExportersTestsBase):
25 class TestHTMLExporter(ExportersTestsBase):
26 """Contains test functions for html.py"""
26 """Contains test functions for html.py"""
27
27
28 def test_constructor(self):
28 def test_constructor(self):
29 """
29 """
30 Can a HTMLExporter be constructed?
30 Can a HTMLExporter be constructed?
31 """
31 """
32 HTMLExporter()
32 HTMLExporter()
33
33
34 @onlyif_cmds_exist('pandoc')
34 @onlyif_cmds_exist('pandoc')
35 def test_export(self):
35 def test_export(self):
36 """
36 """
37 Can a HTMLExporter export something?
37 Can a HTMLExporter export something?
38 """
38 """
39 (output, resources) = HTMLExporter().from_filename(self._get_notebook())
39 (output, resources) = HTMLExporter().from_filename(self._get_notebook())
40 assert len(output) > 0
40 assert len(output) > 0
41
41
42
42
43 def test_export_basic(self):
43 def test_export_basic(self):
44 """
44 """
45 Can a HTMLExporter export using the 'basic' flavor?
45 Can a HTMLExporter export using the 'basic' template?
46 """
46 """
47 (output, resources) = HTMLExporter(flavor='basic').from_filename(self._get_notebook())
47 (output, resources) = HTMLExporter(template='basic').from_filename(self._get_notebook())
48 assert len(output) > 0
48 assert len(output) > 0
49
49
50
50
51 def test_export_full(self):
51 def test_export_full(self):
52 """
52 """
53 Can a HTMLExporter export using the 'full' flavor?
53 Can a HTMLExporter export using the 'full' template?
54 """
54 """
55 (output, resources) = HTMLExporter(flavor='full').from_filename(self._get_notebook())
55 (output, resources) = HTMLExporter(template='full').from_filename(self._get_notebook())
56 assert len(output) > 0
56 assert len(output) > 0
@@ -1,65 +1,65 b''
1 """
1 """
2 Module with tests for latex.py
2 Module with tests for latex.py
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 .base import ExportersTestsBase
17 from .base import ExportersTestsBase
18 from ..latex import LatexExporter
18 from ..latex import LatexExporter
19 from IPython.testing.decorators import onlyif_cmds_exist
19 from IPython.testing.decorators import onlyif_cmds_exist
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Class
22 # Class
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class TestLatexExporter(ExportersTestsBase):
25 class TestLatexExporter(ExportersTestsBase):
26 """Contains test functions for latex.py"""
26 """Contains test functions for latex.py"""
27
27
28 def test_constructor(self):
28 def test_constructor(self):
29 """
29 """
30 Can a LatexExporter be constructed?
30 Can a LatexExporter be constructed?
31 """
31 """
32 LatexExporter()
32 LatexExporter()
33
33
34
34
35 @onlyif_cmds_exist('pandoc')
35 @onlyif_cmds_exist('pandoc')
36 def test_export(self):
36 def test_export(self):
37 """
37 """
38 Can a LatexExporter export something?
38 Can a LatexExporter export something?
39 """
39 """
40 (output, resources) = LatexExporter().from_filename(self._get_notebook())
40 (output, resources) = LatexExporter().from_filename(self._get_notebook())
41 assert len(output) > 0
41 assert len(output) > 0
42
42
43
43
44 def test_export_book(self):
44 def test_export_book(self):
45 """
45 """
46 Can a LatexExporter export using 'book' flavor?
46 Can a LatexExporter export using 'book' template?
47 """
47 """
48 (output, resources) = LatexExporter(flavor='book').from_filename(self._get_notebook())
48 (output, resources) = LatexExporter(template='book').from_filename(self._get_notebook())
49 assert len(output) > 0
49 assert len(output) > 0
50
50
51
51
52 def test_export_basic(self):
52 def test_export_basic(self):
53 """
53 """
54 Can a LatexExporter export using 'basic' flavor?
54 Can a LatexExporter export using 'basic' template?
55 """
55 """
56 (output, resources) = LatexExporter(flavor='basic').from_filename(self._get_notebook())
56 (output, resources) = LatexExporter(template='basic').from_filename(self._get_notebook())
57 assert len(output) > 0
57 assert len(output) > 0
58
58
59
59
60 def test_export_article(self):
60 def test_export_article(self):
61 """
61 """
62 Can a LatexExporter export using 'article' flavor?
62 Can a LatexExporter export using 'article' template?
63 """
63 """
64 (output, resources) = LatexExporter(flavor='article').from_filename(self._get_notebook())
64 (output, resources) = LatexExporter(template='article').from_filename(self._get_notebook())
65 assert len(output) > 0 No newline at end of file
65 assert len(output) > 0
@@ -1,47 +1,47 b''
1 """
1 """
2 Module with tests for slides.py
2 Module with tests for slides.py
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 .base import ExportersTestsBase
17 from .base import ExportersTestsBase
18 from ..slides import SlidesExporter
18 from ..slides import SlidesExporter
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Class
21 # Class
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 class TestSlidesExporter(ExportersTestsBase):
24 class TestSlidesExporter(ExportersTestsBase):
25 """Contains test functions for slides.py"""
25 """Contains test functions for slides.py"""
26
26
27 def test_constructor(self):
27 def test_constructor(self):
28 """
28 """
29 Can a SlidesExporter be constructed?
29 Can a SlidesExporter be constructed?
30 """
30 """
31 SlidesExporter()
31 SlidesExporter()
32
32
33
33
34 def test_export(self):
34 def test_export(self):
35 """
35 """
36 Can a SlidesExporter export something?
36 Can a SlidesExporter export something?
37 """
37 """
38 (output, resources) = SlidesExporter().from_filename(self._get_notebook())
38 (output, resources) = SlidesExporter().from_filename(self._get_notebook())
39 assert len(output) > 0
39 assert len(output) > 0
40
40
41
41
42 def test_export_reveal(self):
42 def test_export_reveal(self):
43 """
43 """
44 Can a SlidesExporter export using the 'reveal' flavor?
44 Can a SlidesExporter export using the 'reveal' template?
45 """
45 """
46 (output, resources) = SlidesExporter(flavor='reveal').from_filename(self._get_notebook())
46 (output, resources) = SlidesExporter(template='reveal').from_filename(self._get_notebook())
47 assert len(output) > 0
47 assert len(output) > 0
@@ -1,259 +1,258 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """NBConvert is a utility for conversion of .ipynb files.
2 """NBConvert is a utility for conversion of .ipynb files.
3
3
4 Command-line interface for the NbConvert conversion utility.
4 Command-line interface for the NbConvert conversion utility.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 #Copyright (c) 2013, the IPython Development Team.
7 #Copyright (c) 2013, 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 # Stdlib imports
18 # Stdlib imports
19 from __future__ import print_function
19 from __future__ import print_function
20 import sys
20 import sys
21 import os
21 import os
22 import glob
22 import glob
23
23
24 # From IPython
24 # From IPython
25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
26 from IPython.config import catch_config_error, Configurable
26 from IPython.config import catch_config_error, Configurable
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
29 )
29 )
30 from IPython.utils.importstring import import_item
30 from IPython.utils.importstring import import_item
31
31
32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
33 from IPython.nbconvert import exporters, transformers, writers
33 from IPython.nbconvert import exporters, transformers, writers
34 from .utils.base import NbConvertBase
34 from .utils.base import NbConvertBase
35 from .utils.exceptions import ConversionException
35 from .utils.exceptions import ConversionException
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 #Classes and functions
38 #Classes and functions
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41 nbconvert_aliases = {}
41 nbconvert_aliases = {}
42 nbconvert_aliases.update(base_aliases)
42 nbconvert_aliases.update(base_aliases)
43 nbconvert_aliases.update({
43 nbconvert_aliases.update({
44 'to' : 'NbConvertApp.export_format',
44 'to' : 'NbConvertApp.export_format',
45 'flavor' : 'Exporter.flavor',
46 'template' : 'Exporter.template_file',
45 'template' : 'Exporter.template_file',
47 'notebooks' : 'NbConvertApp.notebooks',
46 'notebooks' : 'NbConvertApp.notebooks',
48 'writer' : 'NbConvertApp.writer_class',
47 'writer' : 'NbConvertApp.writer_class',
49 })
48 })
50
49
51 nbconvert_flags = {}
50 nbconvert_flags = {}
52 nbconvert_flags.update(base_flags)
51 nbconvert_flags.update(base_flags)
53 nbconvert_flags.update({
52 nbconvert_flags.update({
54 'stdout' : (
53 'stdout' : (
55 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
54 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
56 "Write notebook output to stdout instead of files."
55 "Write notebook output to stdout instead of files."
57 ),
56 ),
58
57
59 'pdf' : (
58 'pdf' : (
60 {'NbConvertApp' : {'writer_class' : "PDFWriter"}},
59 {'NbConvertApp' : {'writer_class' : "PDFWriter"}},
61 "Compile notebook output to a PDF."
60 "Compile notebook output to a PDF (requires `--to latex`)."
62 )
61 )
63 })
62 })
64
63
65
64
66 class NbConvertApp(BaseIPythonApplication):
65 class NbConvertApp(BaseIPythonApplication):
67 """Application used to convert to and from notebook file type (*.ipynb)"""
66 """Application used to convert to and from notebook file type (*.ipynb)"""
68
67
69 name = 'ipython-nbconvert'
68 name = 'ipython-nbconvert'
70 aliases = nbconvert_aliases
69 aliases = nbconvert_aliases
71 flags = nbconvert_flags
70 flags = nbconvert_flags
72
71
73 def _classes_default(self):
72 def _classes_default(self):
74 classes = [NbConvertBase]
73 classes = [NbConvertBase]
75 for pkg in (exporters, transformers, writers):
74 for pkg in (exporters, transformers, writers):
76 for name in dir(pkg):
75 for name in dir(pkg):
77 cls = getattr(pkg, name)
76 cls = getattr(pkg, name)
78 if isinstance(cls, type) and issubclass(cls, Configurable):
77 if isinstance(cls, type) and issubclass(cls, Configurable):
79 classes.append(cls)
78 classes.append(cls)
80 return classes
79 return classes
81
80
82 description = Unicode(
81 description = Unicode(
83 u"""This application is used to convert notebook files (*.ipynb)
82 u"""This application is used to convert notebook files (*.ipynb)
84 to various other formats.""")
83 to various other formats.""")
85
84
86 examples = Unicode(u"""
85 examples = Unicode(u"""
87 The simplest way to use nbconvert is
86 The simplest way to use nbconvert is
88
87
89 > ipython nbconvert mynotebook.ipynb
88 > ipython nbconvert mynotebook.ipynb
90
89
91 which will convert mynotebook.ipynb to the default format (probably HTML).
90 which will convert mynotebook.ipynb to the default format (probably HTML).
92
91
93 You can specify the export format with `--to`.
92 You can specify the export format with `--to`.
94 Options include {0}
93 Options include {0}
95
94
96 > ipython nbconvert --to latex mynotebook.ipnynb
95 > ipython nbconvert --to latex mynotebook.ipnynb
97
96
98 Both HTML and LaTeX support multiple flavors of output. LaTeX includes
97 Both HTML and LaTeX support multiple output templates. LaTeX includes
99 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
98 'basic', 'book', and 'article'. HTML includes 'basic' and 'full'. You
100 can specify the flavor of the format used.
99 can specify the flavor of the format used.
101
100
102 > ipython nbconvert --to html --flavor reveal mynotebook.ipnynb
101 > ipython nbconvert --to html --template reveal mynotebook.ipnynb
103
102
104 You can also pipe the output to stdout, rather than a file
103 You can also pipe the output to stdout, rather than a file
105
104
106 > ipython nbconvert mynotebook.ipynb --stdout
105 > ipython nbconvert mynotebook.ipynb --stdout
107
106
108 or to a PDF
107 or to a PDF
109
108
110 > ipython nbconvert mynotebook.ipynb --pdf
109 > ipython nbconvert mynotebook.ipynb --to latex --pdf
111
110
112 Multiple notebooks can be given at the command line in a couple of
111 Multiple notebooks can be given at the command line in a couple of
113 different ways:
112 different ways:
114
113
115 > ipython nbconvert notebook*.ipynb
114 > ipython nbconvert notebook*.ipynb
116 > ipython nbconvert notebook1.ipynb notebook2.ipynb
115 > ipython nbconvert notebook1.ipynb notebook2.ipynb
117
116
118 or you can specify the notebooks list in a config file, containing::
117 or you can specify the notebooks list in a config file, containing::
119
118
120 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
119 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
121
120
122 > ipython nbconvert --config mycfg.py
121 > ipython nbconvert --config mycfg.py
123 """.format(get_export_names()))
122 """.format(get_export_names()))
124 # Writer specific variables
123 # Writer specific variables
125 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
124 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
126 help="""Instance of the writer class used to write the
125 help="""Instance of the writer class used to write the
127 results of the conversion.""")
126 results of the conversion.""")
128 writer_class = DottedObjectName('FilesWriter', config=True,
127 writer_class = DottedObjectName('FilesWriter', config=True,
129 help="""Writer class used to write the
128 help="""Writer class used to write the
130 results of the conversion""")
129 results of the conversion""")
131 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
130 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
132 'PDFWriter': 'IPython.nbconvert.writers.pdf.PDFWriter',
131 'PDFWriter': 'IPython.nbconvert.writers.pdf.PDFWriter',
133 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
132 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
134 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
133 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
135 writer_factory = Type()
134 writer_factory = Type()
136
135
137 def _writer_class_changed(self, name, old, new):
136 def _writer_class_changed(self, name, old, new):
138 if new in self.writer_aliases:
137 if new in self.writer_aliases:
139 new = self.writer_aliases[new]
138 new = self.writer_aliases[new]
140 self.writer_factory = import_item(new)
139 self.writer_factory = import_item(new)
141
140
142
141
143 # Other configurable variables
142 # Other configurable variables
144 export_format = CaselessStrEnum(get_export_names(),
143 export_format = CaselessStrEnum(get_export_names(),
145 default_value="html",
144 default_value="html",
146 config=True,
145 config=True,
147 help="""The export format to be used."""
146 help="""The export format to be used."""
148 )
147 )
149
148
150 notebooks = List([], config=True, help="""List of notebooks to convert.
149 notebooks = List([], config=True, help="""List of notebooks to convert.
151 Wildcards are supported.
150 Wildcards are supported.
152 Filenames passed positionally will be added to the list.
151 Filenames passed positionally will be added to the list.
153 """)
152 """)
154
153
155 @catch_config_error
154 @catch_config_error
156 def initialize(self, argv=None):
155 def initialize(self, argv=None):
157 super(NbConvertApp, self).initialize(argv)
156 super(NbConvertApp, self).initialize(argv)
158 self.init_syspath()
157 self.init_syspath()
159 self.init_notebooks()
158 self.init_notebooks()
160 self.init_writer()
159 self.init_writer()
161
160
162
161
163 def init_syspath(self):
162 def init_syspath(self):
164 """
163 """
165 Add the cwd to the sys.path ($PYTHONPATH)
164 Add the cwd to the sys.path ($PYTHONPATH)
166 """
165 """
167 sys.path.insert(0, os.getcwd())
166 sys.path.insert(0, os.getcwd())
168
167
169
168
170 def init_notebooks(self):
169 def init_notebooks(self):
171 """Construct the list of notebooks.
170 """Construct the list of notebooks.
172 If notebooks are passed on the command-line,
171 If notebooks are passed on the command-line,
173 they override notebooks specified in config files.
172 they override notebooks specified in config files.
174 Glob each notebook to replace notebook patterns with filenames.
173 Glob each notebook to replace notebook patterns with filenames.
175 """
174 """
176
175
177 # Specifying notebooks on the command-line overrides (rather than adds)
176 # Specifying notebooks on the command-line overrides (rather than adds)
178 # the notebook list
177 # the notebook list
179 if self.extra_args:
178 if self.extra_args:
180 patterns = self.extra_args
179 patterns = self.extra_args
181 else:
180 else:
182 patterns = self.notebooks
181 patterns = self.notebooks
183
182
184 # Use glob to replace all the notebook patterns with filenames.
183 # Use glob to replace all the notebook patterns with filenames.
185 filenames = []
184 filenames = []
186 for pattern in patterns:
185 for pattern in patterns:
187
186
188 # Use glob to find matching filenames. Allow the user to convert
187 # Use glob to find matching filenames. Allow the user to convert
189 # notebooks without having to type the extension.
188 # notebooks without having to type the extension.
190 globbed_files = glob.glob(pattern)
189 globbed_files = glob.glob(pattern)
191 globbed_files.extend(glob.glob(pattern + '.ipynb'))
190 globbed_files.extend(glob.glob(pattern + '.ipynb'))
192
191
193 for filename in globbed_files:
192 for filename in globbed_files:
194 if not filename in filenames:
193 if not filename in filenames:
195 filenames.append(filename)
194 filenames.append(filename)
196 self.notebooks = filenames
195 self.notebooks = filenames
197
196
198 def init_writer(self):
197 def init_writer(self):
199 """
198 """
200 Initialize the writer (which is stateless)
199 Initialize the writer (which is stateless)
201 """
200 """
202 self._writer_class_changed(None, self.writer_class, self.writer_class)
201 self._writer_class_changed(None, self.writer_class, self.writer_class)
203 self.writer = self.writer_factory(parent=self)
202 self.writer = self.writer_factory(parent=self)
204
203
205 def start(self):
204 def start(self):
206 """
205 """
207 Ran after initialization completed
206 Ran after initialization completed
208 """
207 """
209 super(NbConvertApp, self).start()
208 super(NbConvertApp, self).start()
210 self.convert_notebooks()
209 self.convert_notebooks()
211
210
212 def convert_notebooks(self):
211 def convert_notebooks(self):
213 """
212 """
214 Convert the notebooks in the self.notebook traitlet
213 Convert the notebooks in the self.notebook traitlet
215 """
214 """
216 # Export each notebook
215 # Export each notebook
217 conversion_success = 0
216 conversion_success = 0
218 for notebook_filename in self.notebooks:
217 for notebook_filename in self.notebooks:
219
218
220 # Get a unique key for the notebook and set it in the resources object.
219 # Get a unique key for the notebook and set it in the resources object.
221 basename = os.path.basename(notebook_filename)
220 basename = os.path.basename(notebook_filename)
222 notebook_name = basename[:basename.rfind('.')]
221 notebook_name = basename[:basename.rfind('.')]
223 resources = {}
222 resources = {}
224 resources['unique_key'] = notebook_name
223 resources['unique_key'] = notebook_name
225 resources['output_files_dir'] = '%s_files' % notebook_name
224 resources['output_files_dir'] = '%s_files' % notebook_name
226
225
227 # Try to export
226 # Try to export
228 try:
227 try:
229 output, resources = export_by_name(self.export_format,
228 output, resources = export_by_name(self.export_format,
230 notebook_filename,
229 notebook_filename,
231 resources=resources,
230 resources=resources,
232 config=self.config)
231 config=self.config)
233 except ExporterNameError as e:
232 except ExporterNameError as e:
234 print("Error while converting '%s': '%s' exporter not found."
233 print("Error while converting '%s': '%s' exporter not found."
235 %(notebook_filename, self.export_format),
234 %(notebook_filename, self.export_format),
236 file=sys.stderr)
235 file=sys.stderr)
237 print("Known exporters are:",
236 print("Known exporters are:",
238 "\n\t" + "\n\t".join(get_export_names()),
237 "\n\t" + "\n\t".join(get_export_names()),
239 file=sys.stderr)
238 file=sys.stderr)
240 self.exit(1)
239 self.exit(1)
241 except ConversionException as e:
240 except ConversionException as e:
242 print("Error while converting '%s': %s" %(notebook_filename, e),
241 print("Error while converting '%s': %s" %(notebook_filename, e),
243 file=sys.stderr)
242 file=sys.stderr)
244 self.exit(1)
243 self.exit(1)
245 else:
244 else:
246 self.writer.write(output, resources, notebook_name=notebook_name)
245 self.writer.write(output, resources, notebook_name=notebook_name)
247 conversion_success += 1
246 conversion_success += 1
248
247
249 # If nothing was converted successfully, help the user.
248 # If nothing was converted successfully, help the user.
250 if conversion_success == 0:
249 if conversion_success == 0:
251 self.print_help()
250 self.print_help()
252 sys.exit(-1)
251 sys.exit(-1)
253
252
254
253
255 #-----------------------------------------------------------------------------
254 #-----------------------------------------------------------------------------
256 # Main entry point
255 # Main entry point
257 #-----------------------------------------------------------------------------
256 #-----------------------------------------------------------------------------
258
257
259 launch_new_instance = NbConvertApp.launch_instance
258 launch_new_instance = NbConvertApp.launch_instance
@@ -1,135 +1,135 b''
1 """
1 """
2 Contains tests for the nbconvertapp
2 Contains tests for the nbconvertapp
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
5 #Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 #Distributed under the terms of the Modified BSD License.
7 #Distributed under the terms of the Modified BSD License.
8 #
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 import os
16 import os
17 from .base import TestsBase
17 from .base import TestsBase
18
18
19 from IPython.utils import py3compat
19 from IPython.utils import py3compat
20
20
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Constants
23 # Constants
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 # Define ipython commandline name
26 # Define ipython commandline name
27 if py3compat.PY3:
27 if py3compat.PY3:
28 IPYTHON = 'ipython3'
28 IPYTHON = 'ipython3'
29 else:
29 else:
30 IPYTHON = 'ipython'
30 IPYTHON = 'ipython'
31
31
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Classes and functions
34 # Classes and functions
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 class TestNbConvertApp(TestsBase):
37 class TestNbConvertApp(TestsBase):
38 """Collection of NbConvertApp tests"""
38 """Collection of NbConvertApp tests"""
39
39
40
40
41 def test_notebook_help(self):
41 def test_notebook_help(self):
42 """
42 """
43 Will help show if no notebooks are specified?
43 Will help show if no notebooks are specified?
44 """
44 """
45 with self.create_temp_cwd():
45 with self.create_temp_cwd():
46 assert "see '--help-all'" in self.call([IPYTHON, 'nbconvert'])
46 assert "see '--help-all'" in self.call([IPYTHON, 'nbconvert'])
47
47
48
48
49 def test_glob(self):
49 def test_glob(self):
50 """
50 """
51 Do search patterns work for notebook names?
51 Do search patterns work for notebook names?
52 """
52 """
53 with self.create_temp_cwd(['notebook*.ipynb']):
53 with self.create_temp_cwd(['notebook*.ipynb']):
54 assert not 'error' in self.call([IPYTHON, 'nbconvert',
54 assert not 'error' in self.call([IPYTHON, 'nbconvert',
55 '--to="python"', '--notebooks=["*.ipynb"]']).lower()
55 '--to="python"', '--notebooks=["*.ipynb"]']).lower()
56 assert os.path.isfile('notebook1.py')
56 assert os.path.isfile('notebook1.py')
57 assert os.path.isfile('notebook2.py')
57 assert os.path.isfile('notebook2.py')
58
58
59
59
60 def test_glob_subdir(self):
60 def test_glob_subdir(self):
61 """
61 """
62 Do search patterns work for subdirectory notebook names?
62 Do search patterns work for subdirectory notebook names?
63 """
63 """
64 with self.create_temp_cwd() as cwd:
64 with self.create_temp_cwd() as cwd:
65 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
65 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
66 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
66 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
67 '--notebooks=["%s"]' % os.path.join('subdir', '*.ipynb')]).lower()
67 '--notebooks=["%s"]' % os.path.join('subdir', '*.ipynb')]).lower()
68 assert os.path.isfile('notebook1.py')
68 assert os.path.isfile('notebook1.py')
69 assert os.path.isfile('notebook2.py')
69 assert os.path.isfile('notebook2.py')
70
70
71
71
72 def test_explicit(self):
72 def test_explicit(self):
73 """
73 """
74 Do explicit notebook names work?
74 Do explicit notebook names work?
75 """
75 """
76 with self.create_temp_cwd(['notebook*.ipynb']):
76 with self.create_temp_cwd(['notebook*.ipynb']):
77 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
77 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
78 '--notebooks=["notebook2.ipynb"]']).lower()
78 '--notebooks=["notebook2.ipynb"]']).lower()
79 assert not os.path.isfile('notebook1.py')
79 assert not os.path.isfile('notebook1.py')
80 assert os.path.isfile('notebook2.py')
80 assert os.path.isfile('notebook2.py')
81
81
82
82
83 def test_flavor(self):
83 def test_template(self):
84 """
84 """
85 Do export flavors work?
85 Do export templates work?
86 """
86 """
87 with self.create_temp_cwd(['notebook*.ipynb']):
87 with self.create_temp_cwd(['notebook*.ipynb']):
88 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="slides"',
88 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="slides"',
89 '--notebooks=["notebook2.ipynb"]', '--flavor="reveal"']).lower()
89 '--notebooks=["notebook2.ipynb"]', '--template="reveal"']).lower()
90 assert os.path.isfile('notebook2.html')
90 assert os.path.isfile('notebook2.html')
91 with open('notebook2.html') as f:
91 with open('notebook2.html') as f:
92 assert '/reveal.css' in f.read()
92 assert '/reveal.css' in f.read()
93
93
94
94
95 def test_glob_explicit(self):
95 def test_glob_explicit(self):
96 """
96 """
97 Can a search pattern be used along with matching explicit notebook names?
97 Can a search pattern be used along with matching explicit notebook names?
98 """
98 """
99 with self.create_temp_cwd(['notebook*.ipynb']):
99 with self.create_temp_cwd(['notebook*.ipynb']):
100 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
100 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
101 '--notebooks=["*.ipynb", "notebook1.ipynb", "notebook2.ipynb"]']).lower()
101 '--notebooks=["*.ipynb", "notebook1.ipynb", "notebook2.ipynb"]']).lower()
102 assert os.path.isfile('notebook1.py')
102 assert os.path.isfile('notebook1.py')
103 assert os.path.isfile('notebook2.py')
103 assert os.path.isfile('notebook2.py')
104
104
105
105
106 def test_explicit_glob(self):
106 def test_explicit_glob(self):
107 """
107 """
108 Can explicit notebook names be used and then a matching search pattern?
108 Can explicit notebook names be used and then a matching search pattern?
109 """
109 """
110 with self.create_temp_cwd(['notebook*.ipynb']):
110 with self.create_temp_cwd(['notebook*.ipynb']):
111 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
111 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--to="python"',
112 '--notebooks=["notebook1.ipynb", "notebook2.ipynb", "*.ipynb"]']).lower()
112 '--notebooks=["notebook1.ipynb", "notebook2.ipynb", "*.ipynb"]']).lower()
113 assert os.path.isfile('notebook1.py')
113 assert os.path.isfile('notebook1.py')
114 assert os.path.isfile('notebook2.py')
114 assert os.path.isfile('notebook2.py')
115
115
116
116
117 def test_default_config(self):
117 def test_default_config(self):
118 """
118 """
119 Does the default config work?
119 Does the default config work?
120 """
120 """
121 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
121 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
122 assert not 'error' in self.call([IPYTHON, 'nbconvert']).lower()
122 assert not 'error' in self.call([IPYTHON, 'nbconvert']).lower()
123 assert os.path.isfile('notebook1.py')
123 assert os.path.isfile('notebook1.py')
124 assert not os.path.isfile('notebook2.py')
124 assert not os.path.isfile('notebook2.py')
125
125
126
126
127 def test_override_config(self):
127 def test_override_config(self):
128 """
128 """
129 Can the default config be overriden?
129 Can the default config be overriden?
130 """
130 """
131 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py',
131 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py',
132 'override.py']):
132 'override.py']):
133 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--config="override.py"']).lower()
133 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--config="override.py"']).lower()
134 assert not os.path.isfile('notebook1.py')
134 assert not os.path.isfile('notebook1.py')
135 assert os.path.isfile('notebook2.py')
135 assert os.path.isfile('notebook2.py')
@@ -1,42 +1,47 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """
2 """
3 Contains writer for writing nbconvert output to PDF.
3 Contains writer for writing nbconvert output to PDF.
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 import subprocess
17 import subprocess
18 import os
18 import os
19
19
20 from IPython.utils.traitlets import Integer
20 from IPython.utils.traitlets import Integer, Unicode
21
21
22 from .files import FilesWriter
22 from .files import FilesWriter
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 class PDFWriter(FilesWriter):
27 class PDFWriter(FilesWriter):
28 """Writer designed to write to PDF files"""
28 """Writer designed to write to PDF files"""
29
29
30 iteration_count = Integer(3, config=True, help="""
30 iteration_count = Integer(3, config=True, help="""
31 How many times pdflatex will be called.
31 How many times pdflatex will be called.
32 """)
32 """)
33
33
34 compiler = Unicode(u'pdflatex {0}', config=True, help="""
35 Shell command used to compile PDF.""")
36
34 def write(self, output, resources, notebook_name=None, **kw):
37 def write(self, output, resources, notebook_name=None, **kw):
35 """
38 """
36 Consume and write Jinja output a PDF.
39 Consume and write Jinja output a PDF.
37 See files.py for more...
40 See files.py for more...
38 """
41 """
39 dest = super(PDFWriter, self).write(output, resources, notebook_name=notebook_name, **kw)
42 dest = super(PDFWriter, self).write(output, resources,
40 command = 'pdflatex ' + dest
43 notebook_name=notebook_name, **kw)
44 command = self.compiler.format(dest)
45
41 for index in range(self.iteration_count):
46 for index in range(self.iteration_count):
42 subprocess.Popen(command, shell=True, stdout=open(os.devnull, 'wb'))
47 subprocess.Popen(command, shell=True, stdout=open(os.devnull, 'wb'))
General Comments 0
You need to be logged in to leave comments. Login now