##// END OF EJS Templates
Split exporter in to for ipynb->ipynb
Matthias BUSSONNIER -
Show More
@@ -0,0 +1,282 b''
1 """This module defines Exporter, a highly configurable converter
2 that uses Jinja2 to export notebook files into different formats.
3 """
4
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 from __future__ import print_function, absolute_import
18
19 # Stdlib imports
20 import io
21 import os
22 import copy
23 import collections
24 import datetime
25
26
27 # IPython imports
28 from IPython.config.configurable import LoggingConfigurable
29 from IPython.config import Config
30 from IPython.nbformat import current as nbformat
31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
32 from IPython.utils.importstring import import_item
33 from IPython.utils import py3compat
34
35 from IPython.nbconvert import transformers as nbtransformers
36
37
38 #-----------------------------------------------------------------------------
39 # Class
40 #-----------------------------------------------------------------------------
41
42 class ResourcesDict(collections.defaultdict):
43 def __missing__(self, key):
44 return ''
45
46
47 class BaseExporter(LoggingConfigurable):
48 """
49 Base Exporter Class that only conver notebook to notebook
50 and apply the transformers and provide basic methods for
51 reading a notebook from different sources.
52
53 """
54
55 # finish the docstring
56
57 file_extension = Unicode(
58 'txt', config=True,
59 help="Extension of the file that should be written to disk"
60 )
61
62 #Configurability, allows the user to easily add transformers.
63 transformers = List(config=True,
64 help="""List of transformers, by name or namespace, to enable.""")
65
66 _transformers = None
67
68 default_transformers = List([nbtransformers.coalesce_streams,
69 nbtransformers.SVG2PDFTransformer,
70 nbtransformers.ExtractOutputTransformer,
71 nbtransformers.CSSHTMLHeaderTransformer,
72 nbtransformers.RevealHelpTransformer,
73 nbtransformers.LatexTransformer,
74 nbtransformers.SphinxTransformer],
75 config=True,
76 help="""List of transformers available by default, by name, namespace,
77 instance, or type.""")
78
79
80 def __init__(self, config=None, **kw):
81 """
82 Public constructor
83
84 Parameters
85 ----------
86 config : config
87 User configuration instance.
88 """
89 if not config:
90 config = self.default_config
91
92 super(BaseExporter, self).__init__(config=config, **kw)
93
94 #Init
95 self._init_transformers()
96
97
98 @property
99 def default_config(self):
100 return Config()
101
102 def _config_changed(self, name, old, new):
103 """When setting config, make sure to start with our default_config"""
104 c = self.default_config
105 if new:
106 c.merge(new)
107 if c != old:
108 self.config = c
109 super(BaseExporter, self)._config_changed(name, old, c)
110
111
112 def from_notebook_node(self, nb, resources=None):
113 """
114 Convert a notebook from a notebook node instance.
115
116 Parameters
117 ----------
118 nb : Notebook node
119 resources : dict (**kw)
120 of additional resources that can be accessed read/write by
121 transformers.
122 """
123 nb_copy = copy.deepcopy(nb)
124 resources = self._init_resources(resources)
125
126 # Preprocess
127 nb_copy, resources = self._transform(nb_copy, resources)
128
129 return nb_copy, resources
130
131
132 def from_filename(self, filename, resources=None, **kw):
133 """
134 Convert a notebook from a notebook file.
135
136 Parameters
137 ----------
138 filename : str
139 Full filename of the notebook file to open and convert.
140 """
141
142 #Pull the metadata from the filesystem.
143 if resources is None:
144 resources = ResourcesDict()
145 if not 'metadata' in resources or resources['metadata'] == '':
146 resources['metadata'] = ResourcesDict()
147 basename = os.path.basename(filename)
148 notebook_name = basename[:basename.rfind('.')]
149 resources['metadata']['name'] = notebook_name
150
151 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
152 resources['metadata']['modified_date'] = modified_date.strftime("%B %d, %Y")
153
154 with io.open(filename) as f:
155 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
156
157
158 def from_file(self, file_stream, resources=None, **kw):
159 """
160 Convert a notebook from a notebook file.
161
162 Parameters
163 ----------
164 file_stream : file-like object
165 Notebook file-like object to convert.
166 """
167 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
168
169
170 def register_transformer(self, transformer, enabled=False):
171 """
172 Register a transformer.
173 Transformers are classes that act upon the notebook before it is
174 passed into the Jinja templating engine. Transformers are also
175 capable of passing additional information to the Jinja
176 templating engine.
177
178 Parameters
179 ----------
180 transformer : transformer
181 """
182 if transformer is None:
183 raise TypeError('transformer')
184 isclass = isinstance(transformer, type)
185 constructed = not isclass
186
187 #Handle transformer's registration based on it's type
188 if constructed and isinstance(transformer, py3compat.string_types):
189 #Transformer is a string, import the namespace and recursively call
190 #this register_transformer method
191 transformer_cls = import_item(transformer)
192 return self.register_transformer(transformer_cls, enabled)
193
194 if constructed and hasattr(transformer, '__call__'):
195 #Transformer is a function, no need to construct it.
196 #Register and return the transformer.
197 if enabled:
198 transformer.enabled = True
199 self._transformers.append(transformer)
200 return transformer
201
202 elif isclass and isinstance(transformer, MetaHasTraits):
203 #Transformer is configurable. Make sure to pass in new default for
204 #the enabled flag if one was specified.
205 self.register_transformer(transformer(parent=self), enabled)
206
207 elif isclass:
208 #Transformer is not configurable, construct it
209 self.register_transformer(transformer(), enabled)
210
211 else:
212 #Transformer is an instance of something without a __call__
213 #attribute.
214 raise TypeError('transformer')
215
216
217 def _init_transformers(self):
218 """
219 Register all of the transformers needed for this exporter, disabled
220 unless specified explicitly.
221 """
222 if self._transformers is None:
223 self._transformers = []
224
225 #Load default transformers (not necessarly enabled by default).
226 if self.default_transformers:
227 for transformer in self.default_transformers:
228 self.register_transformer(transformer)
229
230 #Load user transformers. Enable by default.
231 if self.transformers:
232 for transformer in self.transformers:
233 self.register_transformer(transformer, enabled=True)
234
235
236 def _init_resources(self, resources):
237
238 #Make sure the resources dict is of ResourcesDict type.
239 if resources is None:
240 resources = ResourcesDict()
241 if not isinstance(resources, ResourcesDict):
242 new_resources = ResourcesDict()
243 new_resources.update(resources)
244 resources = new_resources
245
246 #Make sure the metadata extension exists in resources
247 if 'metadata' in resources:
248 if not isinstance(resources['metadata'], ResourcesDict):
249 resources['metadata'] = ResourcesDict(resources['metadata'])
250 else:
251 resources['metadata'] = ResourcesDict()
252 if not resources['metadata']['name']:
253 resources['metadata']['name'] = 'Notebook'
254
255 #Set the output extension
256 resources['output_extension'] = self.file_extension
257 return resources
258
259
260 def _transform(self, nb, resources):
261 """
262 Preprocess the notebook before passing it into the Jinja engine.
263 To preprocess the notebook is to apply all of the
264
265 Parameters
266 ----------
267 nb : notebook node
268 notebook that is being exported.
269 resources : a dict of additional resources that
270 can be accessed read/write by transformers
271 """
272
273 # Do a copy.deepcopy first,
274 # we are never safe enough with what the transformers could do.
275 nbc = copy.deepcopy(nb)
276 resc = copy.deepcopy(resources)
277
278 #Run each transformer on the notebook. Carry the output along
279 #to each transformer
280 for transformer in self._transformers:
281 nbc, resc = transformer(nbc, resc)
282 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now