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