##// END OF EJS Templates
Merge pull request #3734 from jdfreder/file_subdir...
Matthias Bussonnier -
r11642:321025c8 merge
parent child Browse files
Show More
@@ -1,443 +1,443 b''
1 1 """This module defines Exporter, a highly configurable converter
2 2 that uses Jinja2 to export notebook files into different formats.
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from __future__ import print_function, absolute_import
18 18
19 19 # Stdlib imports
20 20 import io
21 21 import os
22 22 import inspect
23 23 import copy
24 24 import collections
25 25 import datetime
26 26
27 27 # other libs/dependencies
28 28 from jinja2 import Environment, FileSystemLoader, ChoiceLoader
29 29
30 30 # IPython imports
31 31 from IPython.config.configurable import Configurable
32 32 from IPython.config import Config
33 33 from IPython.nbformat import current as nbformat
34 34 from IPython.utils.traitlets import MetaHasTraits, DottedObjectName, Unicode, List, Dict
35 35 from IPython.utils.importstring import import_item
36 36 from IPython.utils.text import indent
37 37 from IPython.utils import py3compat
38 38
39 39 from IPython.nbconvert import transformers as nbtransformers
40 40 from IPython.nbconvert import filters
41 41
42 42 #-----------------------------------------------------------------------------
43 43 # Globals and constants
44 44 #-----------------------------------------------------------------------------
45 45
46 46 #Jinja2 extensions to load.
47 47 JINJA_EXTENSIONS = ['jinja2.ext.loopcontrols']
48 48
49 49 default_filters = {
50 50 'indent': indent,
51 51 'markdown': filters.markdown2html,
52 52 'ansi2html': filters.ansi2html,
53 53 'filter_data_type': filters.DataTypeFilter,
54 54 'get_lines': filters.get_lines,
55 55 'highlight': filters.highlight,
56 56 'highlight2html': filters.highlight,
57 57 'highlight2latex': filters.highlight2latex,
58 58 'markdown2latex': filters.markdown2latex,
59 59 'markdown2rst': filters.markdown2rst,
60 60 'pycomment': filters.python_comment,
61 61 'rm_ansi': filters.remove_ansi,
62 62 'rm_dollars': filters.strip_dollars,
63 63 'rm_fake': filters.rm_fake,
64 64 'html_text' : filters.html_text,
65 65 'add_anchor': filters.add_anchor,
66 66 'ansi2latex': filters.ansi2latex,
67 67 'rm_math_space': filters.rm_math_space,
68 68 'wrap': filters.wrap
69 69 }
70 70
71 71 #-----------------------------------------------------------------------------
72 72 # Class
73 73 #-----------------------------------------------------------------------------
74 74
75 75 class ResourcesDict(collections.defaultdict):
76 76 def __missing__(self, key):
77 77 return ''
78 78
79 79
80 80 class Exporter(Configurable):
81 81 """
82 82 Exports notebooks into other file formats. Uses Jinja 2 templating engine
83 83 to output new formats. Inherit from this class if you are creating a new
84 84 template type along with new filters/transformers. If the filters/
85 85 transformers provided by default suffice, there is no need to inherit from
86 86 this class. Instead, override the template_file and file_extension
87 87 traits via a config file.
88 88
89 89 {filters}
90 90 """
91 91
92 92 # finish the docstring
93 93 __doc__ = __doc__.format(filters = '- '+'\n - '.join(default_filters.keys()))
94 94
95 95
96 96 template_file = Unicode(
97 97 '', config=True,
98 98 help="Name of the template file to use")
99 99
100 100 file_extension = Unicode(
101 101 'txt', config=True,
102 102 help="Extension of the file that should be written to disk"
103 103 )
104 104
105 105 template_path = Unicode(
106 106 os.path.join("..", "templates"), config=True,
107 107 help="Path where the template files are located.")
108 108
109 109 template_skeleton_path = Unicode(
110 110 os.path.join("..", "templates", "skeleton"), config=True,
111 111 help="Path where the template skeleton files are located.")
112 112
113 113 #Jinja block definitions
114 114 jinja_comment_block_start = Unicode("", config=True)
115 115 jinja_comment_block_end = Unicode("", config=True)
116 116 jinja_variable_block_start = Unicode("", config=True)
117 117 jinja_variable_block_end = Unicode("", config=True)
118 118 jinja_logic_block_start = Unicode("", config=True)
119 119 jinja_logic_block_end = Unicode("", config=True)
120 120
121 121 #Extension that the template files use.
122 122 template_extension = Unicode(".tpl", config=True)
123 123
124 124 #Configurability, allows the user to easily add filters and transformers.
125 125 transformers = List(config=True,
126 126 help="""List of transformers, by name or namespace, to enable.""")
127 127
128 128 filters = Dict(config=True,
129 129 help="""Dictionary of filters, by name and namespace, to add to the Jinja
130 130 environment.""")
131 131
132 132 default_transformers = List([nbtransformers.coalesce_streams,
133 133 nbtransformers.SVG2PDFTransformer,
134 nbtransformers.ExtractFigureTransformer,
134 nbtransformers.ExtractOutputTransformer,
135 135 nbtransformers.CSSHTMLHeaderTransformer,
136 136 nbtransformers.RevealHelpTransformer,
137 137 nbtransformers.LatexTransformer,
138 138 nbtransformers.SphinxTransformer],
139 139 config=True,
140 140 help="""List of transformers available by default, by name, namespace,
141 141 instance, or type.""")
142 142
143 143
144 144 def __init__(self, config=None, extra_loaders=None, **kw):
145 145 """
146 146 Public constructor
147 147
148 148 Parameters
149 149 ----------
150 150 config : config
151 151 User configuration instance.
152 152 extra_loaders : list[of Jinja Loaders]
153 153 ordered list of Jinja loder to find templates. Will be tried in order
154 154 before the default FileSysteme ones.
155 155 """
156 156
157 157 #Call the base class constructor
158 158 c = self.default_config
159 159 if config:
160 160 c.merge(config)
161 161
162 162 super(Exporter, self).__init__(config=c, **kw)
163 163
164 164 #Init
165 165 self._init_environment(extra_loaders=extra_loaders)
166 166 self._init_transformers()
167 167 self._init_filters()
168 168
169 169
170 170 @property
171 171 def default_config(self):
172 172 return Config()
173 173
174 174
175 175 def from_notebook_node(self, nb, resources=None, **kw):
176 176 """
177 177 Convert a notebook from a notebook node instance.
178 178
179 179 Parameters
180 180 ----------
181 181 nb : Notebook node
182 182 resources : dict (**kw)
183 183 of additional resources that can be accessed read/write by
184 184 transformers and filters.
185 185 """
186 186 nb_copy = copy.deepcopy(nb)
187 187 resources = self._init_resources(resources)
188 188
189 189 #Preprocess
190 190 nb_copy, resources = self._transform(nb_copy, resources)
191 191
192 192 #Convert
193 193 self.template = self.environment.get_template(self.template_file + self.template_extension)
194 194 output = self.template.render(nb=nb_copy, resources=resources)
195 195 return output, resources
196 196
197 197
198 198 def from_filename(self, filename, resources=None, **kw):
199 199 """
200 200 Convert a notebook from a notebook file.
201 201
202 202 Parameters
203 203 ----------
204 204 filename : str
205 205 Full filename of the notebook file to open and convert.
206 206 """
207 207
208 208 #Pull the metadata from the filesystem.
209 209 if resources is None:
210 210 resources = ResourcesDict()
211 211 if not 'metadata' in resources or resources['metadata'] == '':
212 212 resources['metadata'] = ResourcesDict()
213 213 basename = os.path.basename(filename)
214 214 notebook_name = basename[:basename.rfind('.')]
215 215 resources['metadata']['name'] = notebook_name
216 216
217 217 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
218 218 resources['metadata']['modified_date'] = modified_date.strftime("%B %-d, %Y")
219 219
220 220 with io.open(filename) as f:
221 221 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources,**kw)
222 222
223 223
224 224 def from_file(self, file_stream, resources=None, **kw):
225 225 """
226 226 Convert a notebook from a notebook file.
227 227
228 228 Parameters
229 229 ----------
230 230 file_stream : file-like object
231 231 Notebook file-like object to convert.
232 232 """
233 233 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
234 234
235 235
236 236 def register_transformer(self, transformer, enabled=False):
237 237 """
238 238 Register a transformer.
239 239 Transformers are classes that act upon the notebook before it is
240 240 passed into the Jinja templating engine. Transformers are also
241 241 capable of passing additional information to the Jinja
242 242 templating engine.
243 243
244 244 Parameters
245 245 ----------
246 246 transformer : transformer
247 247 """
248 248 if transformer is None:
249 249 raise TypeError('transformer')
250 250 isclass = isinstance(transformer, type)
251 251 constructed = not isclass
252 252
253 253 #Handle transformer's registration based on it's type
254 254 if constructed and isinstance(transformer, py3compat.string_types):
255 255 #Transformer is a string, import the namespace and recursively call
256 256 #this register_transformer method
257 257 transformer_cls = import_item(transformer)
258 258 return self.register_transformer(transformer_cls, enabled)
259 259
260 260 if constructed and hasattr(transformer, '__call__'):
261 261 #Transformer is a function, no need to construct it.
262 262 #Register and return the transformer.
263 263 if enabled:
264 264 transformer.enabled = True
265 265 self._transformers.append(transformer)
266 266 return transformer
267 267
268 268 elif isclass and isinstance(transformer, MetaHasTraits):
269 269 #Transformer is configurable. Make sure to pass in new default for
270 270 #the enabled flag if one was specified.
271 271 self.register_transformer(transformer(parent=self), enabled)
272 272
273 273 elif isclass:
274 274 #Transformer is not configurable, construct it
275 275 self.register_transformer(transformer(), enabled)
276 276
277 277 else:
278 278 #Transformer is an instance of something without a __call__
279 279 #attribute.
280 280 raise TypeError('transformer')
281 281
282 282
283 283 def register_filter(self, name, jinja_filter):
284 284 """
285 285 Register a filter.
286 286 A filter is a function that accepts and acts on one string.
287 287 The filters are accesible within the Jinja templating engine.
288 288
289 289 Parameters
290 290 ----------
291 291 name : str
292 292 name to give the filter in the Jinja engine
293 293 filter : filter
294 294 """
295 295 if jinja_filter is None:
296 296 raise TypeError('filter')
297 297 isclass = isinstance(jinja_filter, type)
298 298 constructed = not isclass
299 299
300 300 #Handle filter's registration based on it's type
301 301 if constructed and isinstance(jinja_filter, py3compat.string_types):
302 302 #filter is a string, import the namespace and recursively call
303 303 #this register_filter method
304 304 filter_cls = import_item(jinja_filter)
305 305 return self.register_filter(name, filter_cls)
306 306
307 307 if constructed and hasattr(jinja_filter, '__call__'):
308 308 #filter is a function, no need to construct it.
309 309 self.environment.filters[name] = jinja_filter
310 310 return jinja_filter
311 311
312 312 elif isclass and isinstance(jinja_filter, MetaHasTraits):
313 313 #filter is configurable. Make sure to pass in new default for
314 314 #the enabled flag if one was specified.
315 315 filter_instance = jinja_filter(parent=self)
316 316 self.register_filter(name, filter_instance )
317 317
318 318 elif isclass:
319 319 #filter is not configurable, construct it
320 320 filter_instance = jinja_filter()
321 321 self.register_filter(name, filter_instance)
322 322
323 323 else:
324 324 #filter is an instance of something without a __call__
325 325 #attribute.
326 326 raise TypeError('filter')
327 327
328 328
329 329 def _init_environment(self, extra_loaders=None):
330 330 """
331 331 Create the Jinja templating environment.
332 332 """
333 333 here = os.path.dirname(os.path.realpath(__file__))
334 334 loaders = []
335 335 if extra_loaders:
336 336 loaders.extend(extra_loaders)
337 337
338 338 loaders.append(FileSystemLoader([
339 339 os.path.join(here, self.template_path),
340 340 os.path.join(here, self.template_skeleton_path),
341 341 ]))
342 342
343 343 self.environment = Environment(
344 344 loader= ChoiceLoader(loaders),
345 345 extensions=JINJA_EXTENSIONS
346 346 )
347 347
348 348 #Set special Jinja2 syntax that will not conflict with latex.
349 349 if self.jinja_logic_block_start:
350 350 self.environment.block_start_string = self.jinja_logic_block_start
351 351 if self.jinja_logic_block_end:
352 352 self.environment.block_end_string = self.jinja_logic_block_end
353 353 if self.jinja_variable_block_start:
354 354 self.environment.variable_start_string = self.jinja_variable_block_start
355 355 if self.jinja_variable_block_end:
356 356 self.environment.variable_end_string = self.jinja_variable_block_end
357 357 if self.jinja_comment_block_start:
358 358 self.environment.comment_start_string = self.jinja_comment_block_start
359 359 if self.jinja_comment_block_end:
360 360 self.environment.comment_end_string = self.jinja_comment_block_end
361 361
362 362
363 363 def _init_transformers(self):
364 364 """
365 365 Register all of the transformers needed for this exporter, disabled
366 366 unless specified explicitly.
367 367 """
368 368 self._transformers = []
369 369
370 370 #Load default transformers (not necessarly enabled by default).
371 371 if self.default_transformers:
372 372 for transformer in self.default_transformers:
373 373 self.register_transformer(transformer)
374 374
375 375 #Load user transformers. Enable by default.
376 376 if self.transformers:
377 377 for transformer in self.transformers:
378 378 self.register_transformer(transformer, enabled=True)
379 379
380 380
381 381 def _init_filters(self):
382 382 """
383 383 Register all of the filters required for the exporter.
384 384 """
385 385
386 386 #Add default filters to the Jinja2 environment
387 387 for key, value in default_filters.items():
388 388 self.register_filter(key, value)
389 389
390 390 #Load user filters. Overwrite existing filters if need be.
391 391 if self.filters:
392 392 for key, user_filter in self.filters.items():
393 393 self.register_filter(key, user_filter)
394 394
395 395
396 396 def _init_resources(self, resources):
397 397
398 398 #Make sure the resources dict is of ResourcesDict type.
399 399 if resources is None:
400 400 resources = ResourcesDict()
401 401 if not isinstance(resources, ResourcesDict):
402 402 new_resources = ResourcesDict()
403 403 new_resources.update(resources)
404 404 resources = new_resources
405 405
406 406 #Make sure the metadata extension exists in resources
407 407 if 'metadata' in resources:
408 408 if not isinstance(resources['metadata'], ResourcesDict):
409 409 resources['metadata'] = ResourcesDict(resources['metadata'])
410 410 else:
411 411 resources['metadata'] = ResourcesDict()
412 412 if not resources['metadata']['name']:
413 413 resources['metadata']['name'] = 'Notebook'
414 414
415 415 #Set the output extension
416 416 resources['output_extension'] = self.file_extension
417 417 return resources
418 418
419 419
420 420 def _transform(self, nb, resources):
421 421 """
422 422 Preprocess the notebook before passing it into the Jinja engine.
423 423 To preprocess the notebook is to apply all of the
424 424
425 425 Parameters
426 426 ----------
427 427 nb : notebook node
428 428 notebook that is being exported.
429 429 resources : a dict of additional resources that
430 430 can be accessed read/write by transformers
431 431 and filters.
432 432 """
433 433
434 434 # Do a copy.deepcopy first,
435 435 # we are never safe enough with what the transformers could do.
436 436 nbc = copy.deepcopy(nb)
437 437 resc = copy.deepcopy(resources)
438 438
439 439 #Run each transformer on the notebook. Carry the output along
440 440 #to each transformer
441 441 for transformer in self._transformers:
442 442 nbc, resc = transformer(nbc, resc)
443 443 return nbc, resc
@@ -1,102 +1,102 b''
1 1 """
2 2 Exporter that allows Latex Jinja templates to work. Contains logic to
3 3 appropriately prepare IPYNB files for export to LaTeX. Including but
4 4 not limited to escaping LaTeX, fixing math region tags, using special
5 5 tags to circumvent Jinja/Latex syntax conflicts.
6 6 """
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (c) 2013, the IPython Development Team.
9 9 #
10 10 # Distributed under the terms of the Modified BSD License.
11 11 #
12 12 # The full license is in the file COPYING.txt, distributed with this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 # Stdlib imports
20 20 import os
21 21
22 22 # IPython imports
23 23 from IPython.utils.traitlets import Unicode, List
24 24 from IPython.config import Config
25 25
26 26 from IPython.nbconvert import filters, transformers
27 27 from .exporter import Exporter
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes and functions
31 31 #-----------------------------------------------------------------------------
32 32
33 33 class LatexExporter(Exporter):
34 34 """
35 35 Exports to a Latex template. Inherit from this class if your template is
36 36 LaTeX based and you need custom tranformers/filters. Inherit from it if
37 37 you are writing your own HTML template and need custom tranformers/filters.
38 38 If you don't need custom tranformers/filters, just change the
39 39 'template_file' config option. Place your template in the special "/latex"
40 40 subfolder of the "../templates" folder.
41 41 """
42 42
43 43 file_extension = Unicode(
44 44 'tex', config=True,
45 45 help="Extension of the file that should be written to disk")
46 46
47 47 template_file = Unicode(
48 48 'base', config=True,
49 49 help="Name of the template file to use")
50 50
51 51 #Latex constants
52 52 template_path = Unicode(
53 53 os.path.join("..", "templates", "latex"), config=True,
54 54 help="Path where the template files are located.")
55 55
56 56 template_skeleton_path = Unicode(
57 57 os.path.join("..", "templates", "latex", "skeleton"), config=True,
58 58 help="Path where the template skeleton files are located.")
59 59
60 60 #Special Jinja2 syntax that will not conflict when exporting latex.
61 61 jinja_comment_block_start = Unicode("((=", config=True)
62 62 jinja_comment_block_end = Unicode("=))", config=True)
63 63 jinja_variable_block_start = Unicode("(((", config=True)
64 64 jinja_variable_block_end = Unicode(")))", config=True)
65 65 jinja_logic_block_start = Unicode("((*", config=True)
66 66 jinja_logic_block_end = Unicode("*))", config=True)
67 67
68 68 #Extension that the template files use.
69 69 template_extension = Unicode(".tplx", config=True)
70 70
71 71
72 72 def _init_filters(self):
73 73 """
74 74 Register all of the filters required for the exporter.
75 75 """
76 76
77 77 #Register the filters of the base class.
78 78 super(LatexExporter, self)._init_filters()
79 79
80 80 #Add latex filters to the Jinja2 environment
81 81 self.register_filter('escape_tex', filters.escape_latex)
82 82 self.register_filter('highlight', filters.highlight2latex)
83 83
84 84
85 85 @property
86 86 def default_config(self):
87 87 c = Config({
88 88 'NbConvertBase': {
89 89 'display_data_priority' : ['latex', 'pdf', 'png', 'jpg', 'svg', 'jpeg', 'text']
90 90 },
91 'ExtractFigureTransformer': {
91 'ExtractOutputTransformer': {
92 92 'enabled':True
93 93 },
94 94 'SVG2PDFTransformer': {
95 95 'enabled':True
96 96 },
97 97 'LatexTransformer': {
98 98 'enabled':True
99 99 }
100 100 })
101 101 c.merge(super(LatexExporter,self).default_config)
102 102 return c
@@ -1,42 +1,42 b''
1 1 """
2 2 Exporter for exporting notebooks to restructured text.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 # Distributed under the terms of the Modified BSD License.
8 8 #
9 9 # The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 from IPython.utils.traitlets import Unicode
17 17 from IPython.config import Config
18 18
19 19 from .exporter import Exporter
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Classes
23 23 #-----------------------------------------------------------------------------
24 24
25 25 class RSTExporter(Exporter):
26 26 """
27 27 Exports restructured text documents.
28 28 """
29 29
30 30 file_extension = Unicode(
31 31 'rst', config=True,
32 32 help="Extension of the file that should be written to disk")
33 33
34 34 template_file = Unicode(
35 35 'rst', config=True,
36 36 help="Name of the template file to use")
37 37
38 38 @property
39 39 def default_config(self):
40 c = Config({'ExtractFigureTransformer':{'enabled':True}})
40 c = Config({'ExtractOutputTransformer':{'enabled':True}})
41 41 c.merge(super(RSTExporter,self).default_config)
42 42 return c
@@ -1,113 +1,113 b''
1 1 """
2 2 Module with tests for exporter.py
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from IPython.config import Config
18 18
19 19 from .base import ExportersTestsBase
20 20 from .cheese import CheeseTransformer
21 21 from ..exporter import Exporter
22 22
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Class
26 26 #-----------------------------------------------------------------------------
27 27
28 28 class TestExporter(ExportersTestsBase):
29 29 """Contains test functions for exporter.py"""
30 30
31 31
32 32 def test_constructor(self):
33 33 """
34 34 Can an Exporter be constructed?
35 35 """
36 36 Exporter()
37 37
38 38
39 39 def test_export(self):
40 40 """
41 41 Can an Exporter export something?
42 42 """
43 43 exporter = self._make_exporter()
44 44 (output, resources) = exporter.from_filename(self._get_notebook())
45 45 assert len(output) > 0
46 46
47 47
48 def test_extract_figures(self):
48 def test_extract_outputs(self):
49 49 """
50 If the ExtractFigureTransformer is enabled, are figures extracted?
50 If the ExtractOutputTransformer is enabled, are outputs extracted?
51 51 """
52 config = Config({'ExtractFigureTransformer': {'enabled': True}})
52 config = Config({'ExtractOutputTransformer': {'enabled': True}})
53 53 exporter = self._make_exporter(config=config)
54 54 (output, resources) = exporter.from_filename(self._get_notebook())
55 55 assert resources is not None
56 assert 'figures' in resources
57 assert len(resources['figures']) > 0
56 assert 'outputs' in resources
57 assert len(resources['outputs']) > 0
58 58
59 59
60 60 def test_transformer_class(self):
61 61 """
62 62 Can a transformer be added to the transformers list by class type?
63 63 """
64 64 config = Config({'Exporter': {'transformers': [CheeseTransformer]}})
65 65 exporter = self._make_exporter(config=config)
66 66 (output, resources) = exporter.from_filename(self._get_notebook())
67 67 assert resources is not None
68 68 assert 'cheese' in resources
69 69 assert resources['cheese'] == 'real'
70 70
71 71
72 72 def test_transformer_instance(self):
73 73 """
74 74 Can a transformer be added to the transformers list by instance?
75 75 """
76 76 config = Config({'Exporter': {'transformers': [CheeseTransformer()]}})
77 77 exporter = self._make_exporter(config=config)
78 78 (output, resources) = exporter.from_filename(self._get_notebook())
79 79 assert resources is not None
80 80 assert 'cheese' in resources
81 81 assert resources['cheese'] == 'real'
82 82
83 83
84 84 def test_transformer_dottedobjectname(self):
85 85 """
86 86 Can a transformer be added to the transformers list by dotted object name?
87 87 """
88 88 config = Config({'Exporter': {'transformers': ['IPython.nbconvert.exporters.tests.cheese.CheeseTransformer']}})
89 89 exporter = self._make_exporter(config=config)
90 90 (output, resources) = exporter.from_filename(self._get_notebook())
91 91 assert resources is not None
92 92 assert 'cheese' in resources
93 93 assert resources['cheese'] == 'real'
94 94
95 95
96 96 def test_transformer_via_method(self):
97 97 """
98 98 Can a transformer be added via the Exporter convinience method?
99 99 """
100 100 exporter = self._make_exporter()
101 101 exporter.register_transformer(CheeseTransformer, enabled=True)
102 102 (output, resources) = exporter.from_filename(self._get_notebook())
103 103 assert resources is not None
104 104 assert 'cheese' in resources
105 105 assert resources['cheese'] == 'real'
106 106
107 107
108 108 def _make_exporter(self, config=None):
109 109 #Create the exporter instance, make sure to set a template name since
110 110 #the base Exporter doesn't have a template associated with it.
111 111 exporter = Exporter(config=config)
112 112 exporter.template_file = 'python'
113 113 return exporter No newline at end of file
@@ -1,229 +1,230 b''
1 1 #!/usr/bin/env python
2 2 """NBConvert is a utility for conversion of .ipynb files.
3 3
4 4 Command-line interface for the NbConvert conversion utility.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 #Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 #Distributed under the terms of the Modified BSD License.
10 10 #
11 11 #The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 #Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #Stdlib imports
19 19 from __future__ import print_function
20 20 import sys
21 21 import os
22 22 import glob
23 23
24 24 #From IPython
25 25 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
26 26 from IPython.config import catch_config_error, Configurable
27 27 from IPython.utils.traitlets import (
28 28 Unicode, List, Instance, DottedObjectName, Type, CaselessStrEnum,
29 29 )
30 30 from IPython.utils.importstring import import_item
31 31
32 32 from .exporters.export import export_by_name, get_export_names, ExporterNameError
33 33 from IPython.nbconvert import exporters, transformers, writers
34 34 from .utils.base import NbConvertBase
35 35
36 36 #-----------------------------------------------------------------------------
37 37 #Classes and functions
38 38 #-----------------------------------------------------------------------------
39 39
40 40 nbconvert_aliases = {}
41 41 nbconvert_aliases.update(base_aliases)
42 42 nbconvert_aliases.update({
43 43 'format' : 'NbConvertApp.export_format',
44 44 'notebooks' : 'NbConvertApp.notebooks',
45 45 'writer' : 'NbConvertApp.writer_class',
46 46 })
47 47
48 48 nbconvert_flags = {}
49 49 nbconvert_flags.update(base_flags)
50 50 nbconvert_flags.update({
51 51 'stdout' : (
52 52 {'NbConvertApp' : {'writer_class' : "StdoutWriter"}},
53 53 "Write notebook output to stdout instead of files."
54 54 )
55 55 })
56 56
57 57
58 58 class NbConvertApp(BaseIPythonApplication):
59 59 """Application used to convert to and from notebook file type (*.ipynb)"""
60 60
61 61 name = 'ipython-nbconvert'
62 62 aliases = nbconvert_aliases
63 63 flags = nbconvert_flags
64 64
65 65 def _classes_default(self):
66 66 classes = [NbConvertBase]
67 67 for pkg in (exporters, transformers, writers):
68 68 for name in dir(pkg):
69 69 cls = getattr(pkg, name)
70 70 if isinstance(cls, type) and issubclass(cls, Configurable):
71 71 classes.append(cls)
72 72 return classes
73 73
74 74 description = Unicode(
75 75 u"""This application is used to convert notebook files (*.ipynb)
76 76 to various other formats.""")
77 77
78 78 examples = Unicode(u"""
79 79 The simplest way to use nbconvert is
80 80
81 81 > ipython nbconvert mynotebook.ipynb
82 82
83 83 which will convert mynotebook.ipynb to the default format (probably HTML).
84 84
85 85 You can specify the export format with `--format`.
86 86 Options include {0}
87 87
88 88 > ipython nbconvert --format latex mynotebook.ipnynb
89 89
90 90 You can also pipe the output to stdout, rather than a file
91 91
92 92 > ipython nbconvert mynotebook.ipynb --stdout
93 93
94 94 Multiple notebooks can be given at the command line in a couple of
95 95 different ways:
96 96
97 97 > ipython nbconvert notebook*.ipynb
98 98 > ipython nbconvert notebook1.ipynb notebook2.ipynb
99 99
100 100 or you can specify the notebooks list in a config file, containing::
101 101
102 102 c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
103 103
104 104 > ipython nbconvert --config mycfg.py
105 105 """.format(get_export_names()))
106 106 #Writer specific variables
107 107 writer = Instance('IPython.nbconvert.writers.base.WriterBase',
108 108 help="""Instance of the writer class used to write the
109 109 results of the conversion.""")
110 110 writer_class = DottedObjectName('FilesWriter', config=True,
111 111 help="""Writer class used to write the
112 112 results of the conversion""")
113 113 writer_aliases = {'FilesWriter': 'IPython.nbconvert.writers.files.FilesWriter',
114 114 'DebugWriter': 'IPython.nbconvert.writers.debug.DebugWriter',
115 115 'StdoutWriter': 'IPython.nbconvert.writers.stdout.StdoutWriter'}
116 116 writer_factory = Type()
117 117
118 118 def _writer_class_changed(self, name, old, new):
119 119 if new in self.writer_aliases:
120 120 new = self.writer_aliases[new]
121 121 self.writer_factory = import_item(new)
122 122
123 123
124 124 #Other configurable variables
125 125 export_format = CaselessStrEnum(get_export_names(),
126 126 default_value="full_html",
127 127 config=True,
128 128 help="""The export format to be used."""
129 129 )
130 130
131 131 notebooks = List([], config=True, help="""List of notebooks to convert.
132 132 Wildcards are supported.
133 133 Filenames passed positionally will be added to the list.
134 134 """)
135 135
136 136 @catch_config_error
137 137 def initialize(self, argv=None):
138 138 super(NbConvertApp, self).initialize(argv)
139 139 self.init_notebooks()
140 140 self.init_writer()
141 141
142 142 def init_notebooks(self):
143 143 """Construct the list of notebooks.
144 144 If notebooks are passed on the command-line,
145 145 they override notebooks specified in config files.
146 146 Glob each notebook to replace notebook patterns with filenames.
147 147 """
148 148
149 149 # Specifying notebooks on the command-line overrides (rather than adds)
150 150 # the notebook list
151 151 if self.extra_args:
152 152 patterns = self.extra_args
153 153 else:
154 154 patterns = self.notebooks
155 155
156 156 #Use glob to replace all the notebook patterns with filenames.
157 157 filenames = []
158 158 for pattern in patterns:
159 159 for filename in glob.glob(pattern):
160 160 if not filename in filenames:
161 161 filenames.append(filename)
162 162 self.notebooks = filenames
163 163
164 164 def init_writer(self):
165 165 """
166 166 Initialize the writer (which is stateless)
167 167 """
168 168 self._writer_class_changed(None, self.writer_class, self.writer_class)
169 169 self.writer = self.writer_factory(parent=self)
170 170
171 171 def start(self):
172 172 """
173 173 Ran after initialization completed
174 174 """
175 175 super(NbConvertApp, self).start()
176 176 self.convert_notebooks()
177 177
178 178 def convert_notebooks(self):
179 179 """
180 180 Convert the notebooks in the self.notebook traitlet
181 181 """
182 182 #Export each notebook
183 183 conversion_success = 0
184 184 for notebook_filename in self.notebooks:
185 185
186 186 #Get a unique key for the notebook and set it in the resources object.
187 187 basename = os.path.basename(notebook_filename)
188 188 notebook_name = basename[:basename.rfind('.')]
189 189 resources = {}
190 190 resources['unique_key'] = notebook_name
191 resources['output_files_dir'] = '%s_files' % notebook_name
191 192
192 193 #Try to export
193 194 try:
194 195 output, resources = export_by_name(self.export_format,
195 196 notebook_filename,
196 197 resources=resources,
197 198 config=self.config)
198 199 except ExporterNameError as e:
199 200 print("Error: '%s' exporter not found." % self.export_format,
200 201 file=sys.stderr)
201 202 print("Known exporters are:",
202 203 "\n\t" + "\n\t".join(get_export_names()),
203 204 file=sys.stderr)
204 205 sys.exit(-1)
205 206 #except Exception as e:
206 207 #print("Error: could not export '%s'" % notebook_filename, file=sys.stderr)
207 208 #print(e, file=sys.stderr)
208 209 else:
209 210 self.writer.write(output, resources, notebook_name=notebook_name)
210 211 conversion_success += 1
211 212
212 213 #If nothing was converted successfully, help the user.
213 214 if conversion_success == 0:
214 215
215 216 #No notebooks were specified, show help.
216 217 if len(self.notebooks) == 0:
217 218 self.print_help()
218 219
219 220 #Notebooks were specified, but not converted successfully. Show how
220 221 #to access help.
221 222 else:
222 223 print('For help, use "ipython nbconvert --help"')
223 224
224 225
225 226 #-----------------------------------------------------------------------------
226 227 # Main entry point
227 228 #-----------------------------------------------------------------------------
228 229
229 230 launch_new_instance = NbConvertApp.launch_instance
@@ -1,123 +1,123 b''
1 1 """
2 2 Contains tests for the nbconvertapp
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 #Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 #Distributed under the terms of the Modified BSD License.
8 8 #
9 9 #The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import os
17 17 from .base import TestsBase
18 18
19 19 from IPython.utils import py3compat
20 20
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Constants
24 24 #-----------------------------------------------------------------------------
25 25
26 26 #Define ipython commandline name
27 27 if py3compat.PY3:
28 28 IPYTHON = 'ipython3'
29 29 else:
30 30 IPYTHON = 'ipython'
31 31
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Classes and functions
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class TestNbConvertApp(TestsBase):
38 38 """Collection of NbConvertApp tests"""
39 39
40 40
41 41 def test_notebook_help(self):
42 42 """
43 43 Will help show if no notebooks are specified?
44 44 """
45 45 with self.create_temp_cwd():
46 46 assert "see '--help-all'" in self.call([IPYTHON, 'nbconvert'])
47 47
48 48
49 49 def test_glob(self):
50 50 """
51 51 Do search patterns work for notebook names?
52 52 """
53 53 with self.create_temp_cwd(['notebook*.ipynb']):
54 54 assert not 'error' in self.call([IPYTHON, 'nbconvert',
55 55 '--format="python"', '--notebooks=["*.ipynb"]']).lower()
56 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
57 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
56 assert os.path.isfile('notebook1.py')
57 assert os.path.isfile('notebook2.py')
58 58
59 59
60 60 def test_glob_subdir(self):
61 61 """
62 62 Do search patterns work for subdirectory notebook names?
63 63 """
64 64 with self.create_temp_cwd() as cwd:
65 65 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
66 66 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
67 67 '--notebooks=["%s"]' % os.path.join('subdir', '*.ipynb')]).lower()
68 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
69 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
68 assert os.path.isfile('notebook1.py')
69 assert os.path.isfile('notebook2.py')
70 70
71 71
72 72 def test_explicit(self):
73 73 """
74 74 Do explicit notebook names work?
75 75 """
76 76 with self.create_temp_cwd(['notebook*.ipynb']):
77 77 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
78 78 '--notebooks=["notebook2.ipynb"]']).lower()
79 assert not os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
80 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
79 assert not os.path.isfile('notebook1.py')
80 assert os.path.isfile('notebook2.py')
81 81
82 82
83 83 def test_glob_explicit(self):
84 84 """
85 85 Can a search pattern be used along with matching explicit notebook names?
86 86 """
87 87 with self.create_temp_cwd(['notebook*.ipynb']):
88 88 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
89 89 '--notebooks=["*.ipynb", "notebook1.ipynb", "notebook2.ipynb"]']).lower()
90 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
91 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
90 assert os.path.isfile('notebook1.py')
91 assert os.path.isfile('notebook2.py')
92 92
93 93
94 94 def test_explicit_glob(self):
95 95 """
96 96 Can explicit notebook names be used and then a matching search pattern?
97 97 """
98 98 with self.create_temp_cwd(['notebook*.ipynb']):
99 99 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--format="python"',
100 100 '--notebooks=["notebook1.ipynb", "notebook2.ipynb", "*.ipynb"]']).lower()
101 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
102 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
101 assert os.path.isfile('notebook1.py')
102 assert os.path.isfile('notebook2.py')
103 103
104 104
105 105 def test_default_config(self):
106 106 """
107 107 Does the default config work?
108 108 """
109 109 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
110 110 assert not 'error' in self.call([IPYTHON, 'nbconvert']).lower()
111 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
112 assert not os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
111 assert os.path.isfile('notebook1.py')
112 assert not os.path.isfile('notebook2.py')
113 113
114 114
115 115 def test_override_config(self):
116 116 """
117 117 Can the default config be overriden?
118 118 """
119 119 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py',
120 120 'override.py']):
121 121 assert not 'error' in self.call([IPYTHON, 'nbconvert', '--config="override.py"']).lower()
122 assert not os.path.isfile(os.path.join('nbconvert_build', 'notebook1.py'))
123 assert os.path.isfile(os.path.join('nbconvert_build', 'notebook2.py'))
122 assert not os.path.isfile('notebook1.py')
123 assert os.path.isfile('notebook2.py')
@@ -1,12 +1,12 b''
1 1 # Class base Transformers
2 2 from .base import Transformer
3 3 from .convertfigures import ConvertFiguresTransformer
4 4 from .svg2pdf import SVG2PDFTransformer
5 from .extractfigure import ExtractFigureTransformer
5 from .extractoutput import ExtractOutputTransformer
6 6 from .revealhelp import RevealHelpTransformer
7 7 from .latex import LatexTransformer
8 8 from .sphinx import SphinxTransformer
9 9 from .csshtmlheader import CSSHTMLHeaderTransformer
10 10
11 11 # decorated function Transformers
12 12 from .coalescestreams import coalesce_streams
@@ -1,64 +1,64 b''
1 1 """Module containing a transformer that converts outputs in the notebook from
2 2 one format to another.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 # Distributed under the terms of the Modified BSD License.
8 8 #
9 9 # The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 from .base import Transformer
17 17 from IPython.utils.traitlets import Unicode
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22
23 23 class ConvertFiguresTransformer(Transformer):
24 24 """
25 25 Converts all of the outputs in a notebook from one format to another.
26 26 """
27 27
28 28 from_format = Unicode(config=True, help='Format the converter accepts')
29 29 to_format = Unicode(config=True, help='Format the converter writes')
30 30
31 31 def __init__(self, **kw):
32 32 """
33 33 Public constructor
34 34 """
35 35 super(ConvertFiguresTransformer, self).__init__(**kw)
36 36
37 37
38 38 def convert_figure(self, data_format, data):
39 39 raise NotImplementedError()
40 40
41 41
42 42 def transform_cell(self, cell, resources, cell_index):
43 43 """
44 44 Apply a transformation on each cell,
45 45
46 46 See base.py
47 47 """
48 48
49 49 #Loop through all of the datatypes of the outputs in the cell.
50 50 for index, cell_out in enumerate(cell.get('outputs', [])):
51 51 for data_type, data in cell_out.items():
52 # this must run *before* extract figures,
52 # this must run *before* extract outputs,
53 53 # so figure_name and filename do not exist
54 54 self._convert_figure(cell_out, resources, data_type, data)
55 55 return cell, resources
56 56
57 57
58 58 def _convert_figure(self, cell_out, resources, data_type, data):
59 59 """
60 60 Convert a figure and output the results to the cell output
61 61 """
62 62 if not self.to_format in cell_out and data_type == self.from_format:
63 63 data = self.convert_figure(data_type, data)
64 64 cell_out[self.to_format] = data
@@ -1,96 +1,101 b''
1 """Module containing a transformer that extracts all of the figures from the
2 notebook file. The extracted figures are returned in the 'resources' dictionary.
1 """Module containing a transformer that extracts all of the outputs from the
2 notebook file. The extracted outputs are returned in the 'resources' dictionary.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 # Distributed under the terms of the Modified BSD License.
8 8 #
9 9 # The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import base64
17 17 import sys
18 import os
18 19
19 20 from IPython.utils.traitlets import Unicode
20 21 from .base import Transformer
21 22 from IPython.utils import py3compat
22 23
23 24 #-----------------------------------------------------------------------------
24 25 # Classes
25 26 #-----------------------------------------------------------------------------
26 27
27 class ExtractFigureTransformer(Transformer):
28 class ExtractOutputTransformer(Transformer):
28 29 """
29 Extracts all of the figures from the notebook file. The extracted
30 figures are returned in the 'resources' dictionary.
30 Extracts all of the outputs from the notebook file. The extracted
31 outputs are returned in the 'resources' dictionary.
31 32 """
32 33
33 34 figure_filename_template = Unicode(
34 35 "{unique_key}_{cell_index}_{index}.{extension}", config=True)
35 36
36 37
37 38 def transform_cell(self, cell, resources, cell_index):
38 39 """
39 40 Apply a transformation on each cell,
40 41
41 42 Parameters
42 43 ----------
43 44 cell : NotebookNode cell
44 45 Notebook cell being processed
45 46 resources : dictionary
46 47 Additional resources used in the conversion process. Allows
47 48 transformers to pass variables into the Jinja engine.
48 49 cell_index : int
49 50 Index of the cell being processed (see base.py)
50 51 """
51 52
52 53 #Get the unique key from the resource dict if it exists. If it does not
53 #exist, use 'figure' as the default.
54 #exist, use 'figure' as the default. Also, get files directory if it
55 #has been specified
54 56 unique_key = resources.get('unique_key', 'figure')
57 output_files_dir = resources.get('output_files_dir', None)
55 58
56 #Make sure figures key exists
57 if not 'figures' in resources:
58 resources['figures'] = {}
59 #Make sure outputs key exists
60 if not 'outputs' in resources:
61 resources['outputs'] = {}
59 62
60 63 #Loop through all of the outputs in the cell
61 64 for index, out in enumerate(cell.get('outputs', [])):
62 65
63 66 #Get the output in data formats that the template is interested in.
64 67 for out_type in self.display_data_priority:
65 68 if out.hasattr(out_type):
66 69 data = out[out_type]
67 70
68 71 #Binary files are base64-encoded, SVG is already XML
69 72 if out_type in ('png', 'jpg', 'jpeg', 'pdf'):
70 73 # data is b64-encoded as text (str, unicode)
71 74 # decodestring only accepts bytes
72 75 data = py3compat.cast_bytes(data)
73 76 data = base64.decodestring(data)
74 77 elif sys.platform == 'win32':
75 78 data = data.replace('\n', '\r\n').encode("UTF-8")
76 79 else:
77 80 data = data.encode("UTF-8")
78 81
79 82 #Build a figure name
80 figure_name = self.figure_filename_template.format(
83 filename = self.figure_filename_template.format(
81 84 unique_key=unique_key,
82 85 cell_index=cell_index,
83 86 index=index,
84 87 extension=out_type)
85 88
86 89 #On the cell, make the figure available via
87 90 # cell.outputs[i].svg_filename ... etc (svg in example)
88 91 # Where
89 92 # cell.outputs[i].svg contains the data
90 out[out_type + '_filename'] = figure_name
93 if output_files_dir is not None:
94 filename = os.path.join(output_files_dir, filename)
95 out[out_type + '_filename'] = filename
91 96
92 97 #In the resources, make the figure available via
93 # resources['figures']['filename'] = data
94 resources['figures'][figure_name] = data
98 # resources['outputs']['filename'] = data
99 resources['outputs'][filename] = data
95 100
96 101 return cell, resources
@@ -1,43 +1,43 b''
1 1 #!/usr/bin/env python
2 2 """
3 3 Contains debug writer.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 #Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 #Distributed under the terms of the Modified BSD License.
9 9 #
10 10 #The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from .base import WriterBase
18 18 from pprint import pprint
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23
24 24 class DebugWriter(WriterBase):
25 25 """Consumes output from nbconvert export...() methods and writes usefull
26 26 debugging information to the stdout. The information includes a list of
27 27 resources that were extracted from the notebook(s) during export."""
28 28
29 29
30 30 def write(self, output, resources, notebook_name='notebook', **kw):
31 31 """
32 32 Consume and write Jinja output.
33 33
34 34 See base for more...
35 35 """
36 36
37 if 'figures' in resources:
38 print("Figures extracted from %s" % notebook_name)
37 if 'outputs' in resources:
38 print("outputs extracted from %s" % notebook_name)
39 39 print('-' * 80)
40 pprint.pprint(resources['figures'], indent=2, width=70)
40 pprint.pprint(resources['outputs'], indent=2, width=70)
41 41 else:
42 print("No figures extracted from %s" % notebook_name)
42 print("No outputs extracted from %s" % notebook_name)
43 43 print('=' * 80)
@@ -1,98 +1,102 b''
1 1 #!/usr/bin/env python
2 2 """
3 3 Contains writer for writing nbconvert output to filesystem.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 #Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 #Distributed under the terms of the Modified BSD License.
9 9 #
10 10 #The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import io
18 18 import os
19 19 import shutil
20 20 import glob
21 21
22 22 from IPython.utils.traitlets import Unicode
23 23
24 24 from .base import WriterBase
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Classes
28 28 #-----------------------------------------------------------------------------
29 29
30 30 class FilesWriter(WriterBase):
31 31 """Consumes nbconvert output and produces files."""
32 32
33 33
34 build_directory = Unicode("nbconvert_build", config=True,
34 build_directory = Unicode(".", config=True,
35 35 help="""Directory to write output to. Leave blank
36 36 to output to the current directory""")
37 37
38 38
39 39 #Make sure that the output directory exists.
40 40 def _build_directory_changed(self, name, old, new):
41 41 if new and not os.path.isdir(new):
42 42 os.makedirs(new)
43 43
44 44
45 45 def __init__(self, **kw):
46 46 super(FilesWriter, self).__init__(**kw)
47 47 self._build_directory_changed('build_directory', self.build_directory,
48 48 self.build_directory)
49 49
50 50
51 51 def write(self, output, resources, notebook_name=None, **kw):
52 52 """
53 53 Consume and write Jinja output to the file system. Output directory
54 54 is set via the 'build_directory' variable of this instance (a
55 55 configurable).
56 56
57 57 See base for more...
58 58 """
59 59
60 #Pull the extension from the resources dict.
60 # Pull the extension and subdir from the resources dict.
61 61 output_extension = resources['output_extension']
62 62
63 63 #Write all of the extracted resources to the destination directory.
64 64 #NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
65 65 #TRANSFORMER SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
66 for filename, data in resources.get('figures', {}).items():
66 for filename, data in resources.get('outputs', {}).items():
67 67
68 68 #Determine where to write the file to
69 69 dest = os.path.join(self.build_directory, filename)
70 path = os.path.dirname(dest)
71 if not os.path.isdir(path):
72 os.makedirs(path)
70 73
71 74 #Write file
72 75 with io.open(dest, 'wb') as f:
73 76 f.write(data)
74 77
75 78 #Copy referenced files to output directory
76 79 if self.build_directory:
77 80 for filename in self.files:
78 81
79 82 #Copy files that match search pattern
80 83 for matching_filename in glob.glob(filename):
81 84
82 85 #Make sure folder exists.
83 86 dest = os.path.join(self.build_directory, filename)
84 87 path = os.path.dirname(dest)
85 88 if not os.path.isdir(path):
86 89 os.makedirs(path)
87 90
88 #Copy
91 # Copy if destination is different.
92 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
89 93 shutil.copyfile(matching_filename, dest)
90 94
91 95 #Determine where to write conversion results.
92 96 dest = notebook_name + '.' + output_extension
93 97 if self.build_directory:
94 98 dest = os.path.join(self.build_directory, dest)
95 99
96 100 #Write conversion results.
97 101 with io.open(dest, 'w') as f:
98 102 f.write(output)
@@ -1,37 +1,35 b''
1 1 #!/usr/bin/env python
2 2 """
3 3 Contains Stdout writer
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 #Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 #Distributed under the terms of the Modified BSD License.
9 9 #
10 10 #The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from .base import WriterBase
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22
23 23 class StdoutWriter(WriterBase):
24 24 """Consumes output from nbconvert export...() methods and writes to the
25 stdout stream. Allows for quick debuging of nbconvert output. Using the
26 debug flag makes the writer pretty-print the figures contained within the
27 notebook."""
25 stdout stream."""
28 26
29 27
30 28 def write(self, output, resources, **kw):
31 29 """
32 30 Consume and write Jinja output.
33 31
34 32 See base for more...
35 33 """
36 34
37 35 print(output)
General Comments 0
You need to be logged in to leave comments. Login now