##// END OF EJS Templates
Merge pull request #4300 from Carreau/fix-4299...
Matthias Bussonnier -
r12828:89e59140 merge
parent child Browse files
Show More
@@ -1,279 +1,279 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 copy
23 23 import collections
24 24 import datetime
25 25
26 26
27 27 # IPython imports
28 28 from IPython.config.configurable import LoggingConfigurable
29 29 from IPython.config import Config
30 30 from IPython.nbformat import current as nbformat
31 31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
32 32 from IPython.utils.importstring import import_item
33 33 from IPython.utils import text, py3compat
34 34
35 35 from IPython.nbconvert import preprocessors as nbpreprocessors
36 36
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Class
40 40 #-----------------------------------------------------------------------------
41 41
42 42 class ResourcesDict(collections.defaultdict):
43 43 def __missing__(self, key):
44 44 return ''
45 45
46 46
47 47 class Exporter(LoggingConfigurable):
48 48 """
49 49 Class containing methods that sequentially run a list of preprocessors on a
50 50 NotebookNode object and then return the modified NotebookNode object and
51 51 accompanying resources dict.
52 52 """
53 53
54 54 file_extension = Unicode(
55 55 'txt', config=True,
56 56 help="Extension of the file that should be written to disk"
57 57 )
58 58
59 59 #Configurability, allows the user to easily add filters and preprocessors.
60 60 preprocessors = List(config=True,
61 61 help="""List of preprocessors, by name or namespace, to enable.""")
62 62
63 63 _preprocessors = None
64 64
65 default_preprocessors = List([nbpreprocessors.coalesce_streams,
66 nbpreprocessors.SVG2PDFPreprocessor,
67 nbpreprocessors.ExtractOutputPreprocessor,
68 nbpreprocessors.CSSHTMLHeaderPreprocessor,
69 nbpreprocessors.RevealHelpPreprocessor,
70 nbpreprocessors.LatexPreprocessor,
71 nbpreprocessors.HighlightMagicsPreprocessor],
65 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
66 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
67 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
68 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
69 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
70 'IPython.nbconvert.preprocessors.LatexPreprocessor',
71 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
72 72 config=True,
73 73 help="""List of preprocessors available by default, by name, namespace,
74 74 instance, or type.""")
75 75
76 76
77 77 def __init__(self, config=None, **kw):
78 78 """
79 79 Public constructor
80 80
81 81 Parameters
82 82 ----------
83 83 config : config
84 84 User configuration instance.
85 85 """
86 86 if not config:
87 87 config = self.default_config
88 88
89 89 super(Exporter, self).__init__(config=config, **kw)
90 90
91 91 #Init
92 92 self._init_preprocessors()
93 93
94 94
95 95 @property
96 96 def default_config(self):
97 97 return Config()
98 98
99 99 def _config_changed(self, name, old, new):
100 100 """When setting config, make sure to start with our default_config"""
101 101 c = self.default_config
102 102 if new:
103 103 c.merge(new)
104 104 if c != old:
105 105 self.config = c
106 106 super(Exporter, self)._config_changed(name, old, c)
107 107
108 108
109 109 def from_notebook_node(self, nb, resources=None, **kw):
110 110 """
111 111 Convert a notebook from a notebook node instance.
112 112
113 113 Parameters
114 114 ----------
115 115 nb : Notebook node
116 116 resources : dict (**kw)
117 117 of additional resources that can be accessed read/write by
118 118 preprocessors.
119 119 """
120 120 nb_copy = copy.deepcopy(nb)
121 121 resources = self._init_resources(resources)
122 122
123 123 # Preprocess
124 124 nb_copy, resources = self._preprocess(nb_copy, resources)
125 125
126 126 return nb_copy, resources
127 127
128 128
129 129 def from_filename(self, filename, resources=None, **kw):
130 130 """
131 131 Convert a notebook from a notebook file.
132 132
133 133 Parameters
134 134 ----------
135 135 filename : str
136 136 Full filename of the notebook file to open and convert.
137 137 """
138 138
139 139 # Pull the metadata from the filesystem.
140 140 if resources is None:
141 141 resources = ResourcesDict()
142 142 if not 'metadata' in resources or resources['metadata'] == '':
143 143 resources['metadata'] = ResourcesDict()
144 144 basename = os.path.basename(filename)
145 145 notebook_name = basename[:basename.rfind('.')]
146 146 resources['metadata']['name'] = notebook_name
147 147
148 148 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
149 149 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
150 150
151 151 with io.open(filename) as f:
152 152 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
153 153
154 154
155 155 def from_file(self, file_stream, resources=None, **kw):
156 156 """
157 157 Convert a notebook from a notebook file.
158 158
159 159 Parameters
160 160 ----------
161 161 file_stream : file-like object
162 162 Notebook file-like object to convert.
163 163 """
164 164 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
165 165
166 166
167 167 def register_preprocessor(self, preprocessor, enabled=False):
168 168 """
169 169 Register a preprocessor.
170 170 Preprocessors are classes that act upon the notebook before it is
171 171 passed into the Jinja templating engine. preprocessors are also
172 172 capable of passing additional information to the Jinja
173 173 templating engine.
174 174
175 175 Parameters
176 176 ----------
177 177 preprocessor : preprocessor
178 178 """
179 179 if preprocessor is None:
180 180 raise TypeError('preprocessor')
181 181 isclass = isinstance(preprocessor, type)
182 182 constructed = not isclass
183 183
184 184 # Handle preprocessor's registration based on it's type
185 185 if constructed and isinstance(preprocessor, py3compat.string_types):
186 186 # Preprocessor is a string, import the namespace and recursively call
187 187 # this register_preprocessor method
188 188 preprocessor_cls = import_item(preprocessor)
189 189 return self.register_preprocessor(preprocessor_cls, enabled)
190 190
191 191 if constructed and hasattr(preprocessor, '__call__'):
192 192 # Preprocessor is a function, no need to construct it.
193 193 # Register and return the preprocessor.
194 194 if enabled:
195 195 preprocessor.enabled = True
196 196 self._preprocessors.append(preprocessor)
197 197 return preprocessor
198 198
199 199 elif isclass and isinstance(preprocessor, MetaHasTraits):
200 200 # Preprocessor is configurable. Make sure to pass in new default for
201 201 # the enabled flag if one was specified.
202 202 self.register_preprocessor(preprocessor(parent=self), enabled)
203 203
204 204 elif isclass:
205 205 # Preprocessor is not configurable, construct it
206 206 self.register_preprocessor(preprocessor(), enabled)
207 207
208 208 else:
209 209 # Preprocessor is an instance of something without a __call__
210 210 # attribute.
211 211 raise TypeError('preprocessor')
212 212
213 213
214 214 def _init_preprocessors(self):
215 215 """
216 216 Register all of the preprocessors needed for this exporter, disabled
217 217 unless specified explicitly.
218 218 """
219 219 if self._preprocessors is None:
220 220 self._preprocessors = []
221 221
222 222 #Load default preprocessors (not necessarly enabled by default).
223 223 if self.default_preprocessors:
224 224 for preprocessor in self.default_preprocessors:
225 225 self.register_preprocessor(preprocessor)
226 226
227 227 #Load user preprocessors. Enable by default.
228 228 if self.preprocessors:
229 229 for preprocessor in self.preprocessors:
230 230 self.register_preprocessor(preprocessor, enabled=True)
231 231
232 232
233 233 def _init_resources(self, resources):
234 234
235 235 #Make sure the resources dict is of ResourcesDict type.
236 236 if resources is None:
237 237 resources = ResourcesDict()
238 238 if not isinstance(resources, ResourcesDict):
239 239 new_resources = ResourcesDict()
240 240 new_resources.update(resources)
241 241 resources = new_resources
242 242
243 243 #Make sure the metadata extension exists in resources
244 244 if 'metadata' in resources:
245 245 if not isinstance(resources['metadata'], ResourcesDict):
246 246 resources['metadata'] = ResourcesDict(resources['metadata'])
247 247 else:
248 248 resources['metadata'] = ResourcesDict()
249 249 if not resources['metadata']['name']:
250 250 resources['metadata']['name'] = 'Notebook'
251 251
252 252 #Set the output extension
253 253 resources['output_extension'] = self.file_extension
254 254 return resources
255 255
256 256
257 257 def _preprocess(self, nb, resources):
258 258 """
259 259 Preprocess the notebook before passing it into the Jinja engine.
260 260 To preprocess the notebook is to apply all of the
261 261
262 262 Parameters
263 263 ----------
264 264 nb : notebook node
265 265 notebook that is being exported.
266 266 resources : a dict of additional resources that
267 267 can be accessed read/write by preprocessors
268 268 """
269 269
270 270 # Do a copy.deepcopy first,
271 271 # we are never safe enough with what the preprocessors could do.
272 272 nbc = copy.deepcopy(nb)
273 273 resc = copy.deepcopy(resources)
274 274
275 275 #Run each preprocessor on the notebook. Carry the output along
276 276 #to each preprocessor
277 277 for preprocessor in self._preprocessors:
278 278 nbc, resc = preprocessor(nbc, resc)
279 279 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now