##// END OF EJS Templates
Merge pull request #7181 from jhamrick/extension-traitlet...
Min RK -
r19474:7b625264 merge
parent child Browse files
Show More
@@ -1,11 +1,11 b''
1 from .export import *
1 from .export import *
2 from .html import HTMLExporter
2 from .html import HTMLExporter
3 from .slides import SlidesExporter
3 from .slides import SlidesExporter
4 from .templateexporter import TemplateExporter
4 from .templateexporter import TemplateExporter
5 from .latex import LatexExporter
5 from .latex import LatexExporter
6 from .markdown import MarkdownExporter
6 from .markdown import MarkdownExporter
7 from .notebook import NotebookExporter
7 from .notebook import NotebookExporter
8 from .pdf import PDFExporter
8 from .pdf import PDFExporter
9 from .python import PythonExporter
9 from .python import PythonExporter
10 from .rst import RSTExporter
10 from .rst import RSTExporter
11 from .exporter import Exporter
11 from .exporter import Exporter, FilenameExtension
@@ -1,259 +1,277 b''
1 """This module defines a base Exporter class. For Jinja template-based export,
1 """This module defines a base Exporter class. For Jinja template-based export,
2 see templateexporter.py.
2 see templateexporter.py.
3 """
3 """
4
4
5
5
6 from __future__ import print_function, absolute_import
6 from __future__ import print_function, absolute_import
7
7
8 import io
8 import io
9 import os
9 import os
10 import copy
10 import copy
11 import collections
11 import collections
12 import datetime
12 import datetime
13
13
14 from IPython.config.configurable import LoggingConfigurable
14 from IPython.config.configurable import LoggingConfigurable
15 from IPython.config import Config
15 from IPython.config import Config
16 from IPython import nbformat
16 from IPython import nbformat
17 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
17 from IPython.utils.traitlets import MetaHasTraits, Unicode, List, TraitError
18 from IPython.utils.importstring import import_item
18 from IPython.utils.importstring import import_item
19 from IPython.utils import text, py3compat
19 from IPython.utils import text, py3compat
20
20
21
21
22 class ResourcesDict(collections.defaultdict):
22 class ResourcesDict(collections.defaultdict):
23 def __missing__(self, key):
23 def __missing__(self, key):
24 return ''
24 return ''
25
25
26
26
27 class FilenameExtension(Unicode):
28 """A trait for filename extensions."""
29
30 default_value = u''
31 info_text = 'a filename extension, beginning with a dot'
32
33 def validate(self, obj, value):
34 # cast to proper unicode
35 value = super(FilenameExtension, self).validate(obj, value)
36
37 # check that it starts with a dot
38 if value and not value.startswith('.'):
39 msg = "FileExtension trait '{}' does not begin with a dot: {!r}"
40 raise TraitError(msg.format(self.name, value))
41
42 return value
43
44
27 class Exporter(LoggingConfigurable):
45 class Exporter(LoggingConfigurable):
28 """
46 """
29 Class containing methods that sequentially run a list of preprocessors on a
47 Class containing methods that sequentially run a list of preprocessors on a
30 NotebookNode object and then return the modified NotebookNode object and
48 NotebookNode object and then return the modified NotebookNode object and
31 accompanying resources dict.
49 accompanying resources dict.
32 """
50 """
33
51
34 file_extension = Unicode(
52 file_extension = FilenameExtension(
35 '.txt', config=True,
53 '.txt', config=True,
36 help="Extension of the file that should be written to disk"
54 help="Extension of the file that should be written to disk"
37 )
55 )
38
56
39 # MIME type of the result file, for HTTP response headers.
57 # MIME type of the result file, for HTTP response headers.
40 # This is *not* a traitlet, because we want to be able to access it from
58 # This is *not* a traitlet, because we want to be able to access it from
41 # the class, not just on instances.
59 # the class, not just on instances.
42 output_mimetype = ''
60 output_mimetype = ''
43
61
44 #Configurability, allows the user to easily add filters and preprocessors.
62 #Configurability, allows the user to easily add filters and preprocessors.
45 preprocessors = List(config=True,
63 preprocessors = List(config=True,
46 help="""List of preprocessors, by name or namespace, to enable.""")
64 help="""List of preprocessors, by name or namespace, to enable.""")
47
65
48 _preprocessors = List()
66 _preprocessors = List()
49
67
50 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
68 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
51 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
69 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
52 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
70 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
53 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
71 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
54 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
72 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
55 'IPython.nbconvert.preprocessors.LatexPreprocessor',
73 'IPython.nbconvert.preprocessors.LatexPreprocessor',
56 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
74 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
57 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
75 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
58 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
59 config=True,
77 config=True,
60 help="""List of preprocessors available by default, by name, namespace,
78 help="""List of preprocessors available by default, by name, namespace,
61 instance, or type.""")
79 instance, or type.""")
62
80
63
81
64 def __init__(self, config=None, **kw):
82 def __init__(self, config=None, **kw):
65 """
83 """
66 Public constructor
84 Public constructor
67
85
68 Parameters
86 Parameters
69 ----------
87 ----------
70 config : config
88 config : config
71 User configuration instance.
89 User configuration instance.
72 """
90 """
73 with_default_config = self.default_config
91 with_default_config = self.default_config
74 if config:
92 if config:
75 with_default_config.merge(config)
93 with_default_config.merge(config)
76
94
77 super(Exporter, self).__init__(config=with_default_config, **kw)
95 super(Exporter, self).__init__(config=with_default_config, **kw)
78
96
79 self._init_preprocessors()
97 self._init_preprocessors()
80
98
81
99
82 @property
100 @property
83 def default_config(self):
101 def default_config(self):
84 return Config()
102 return Config()
85
103
86 def from_notebook_node(self, nb, resources=None, **kw):
104 def from_notebook_node(self, nb, resources=None, **kw):
87 """
105 """
88 Convert a notebook from a notebook node instance.
106 Convert a notebook from a notebook node instance.
89
107
90 Parameters
108 Parameters
91 ----------
109 ----------
92 nb : :class:`~IPython.nbformat.NotebookNode`
110 nb : :class:`~IPython.nbformat.NotebookNode`
93 Notebook node (dict-like with attr-access)
111 Notebook node (dict-like with attr-access)
94 resources : dict
112 resources : dict
95 Additional resources that can be accessed read/write by
113 Additional resources that can be accessed read/write by
96 preprocessors and filters.
114 preprocessors and filters.
97 **kw
115 **kw
98 Ignored (?)
116 Ignored (?)
99 """
117 """
100 nb_copy = copy.deepcopy(nb)
118 nb_copy = copy.deepcopy(nb)
101 resources = self._init_resources(resources)
119 resources = self._init_resources(resources)
102
120
103 if 'language' in nb['metadata']:
121 if 'language' in nb['metadata']:
104 resources['language'] = nb['metadata']['language'].lower()
122 resources['language'] = nb['metadata']['language'].lower()
105
123
106 # Preprocess
124 # Preprocess
107 nb_copy, resources = self._preprocess(nb_copy, resources)
125 nb_copy, resources = self._preprocess(nb_copy, resources)
108
126
109 return nb_copy, resources
127 return nb_copy, resources
110
128
111
129
112 def from_filename(self, filename, resources=None, **kw):
130 def from_filename(self, filename, resources=None, **kw):
113 """
131 """
114 Convert a notebook from a notebook file.
132 Convert a notebook from a notebook file.
115
133
116 Parameters
134 Parameters
117 ----------
135 ----------
118 filename : str
136 filename : str
119 Full filename of the notebook file to open and convert.
137 Full filename of the notebook file to open and convert.
120 """
138 """
121
139
122 # Pull the metadata from the filesystem.
140 # Pull the metadata from the filesystem.
123 if resources is None:
141 if resources is None:
124 resources = ResourcesDict()
142 resources = ResourcesDict()
125 if not 'metadata' in resources or resources['metadata'] == '':
143 if not 'metadata' in resources or resources['metadata'] == '':
126 resources['metadata'] = ResourcesDict()
144 resources['metadata'] = ResourcesDict()
127 basename = os.path.basename(filename)
145 basename = os.path.basename(filename)
128 notebook_name = basename[:basename.rfind('.')]
146 notebook_name = basename[:basename.rfind('.')]
129 resources['metadata']['name'] = notebook_name
147 resources['metadata']['name'] = notebook_name
130
148
131 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
149 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
132 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
150 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
133
151
134 with io.open(filename, encoding='utf-8') as f:
152 with io.open(filename, encoding='utf-8') as f:
135 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
153 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
136
154
137
155
138 def from_file(self, file_stream, resources=None, **kw):
156 def from_file(self, file_stream, resources=None, **kw):
139 """
157 """
140 Convert a notebook from a notebook file.
158 Convert a notebook from a notebook file.
141
159
142 Parameters
160 Parameters
143 ----------
161 ----------
144 file_stream : file-like object
162 file_stream : file-like object
145 Notebook file-like object to convert.
163 Notebook file-like object to convert.
146 """
164 """
147 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
165 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
148
166
149
167
150 def register_preprocessor(self, preprocessor, enabled=False):
168 def register_preprocessor(self, preprocessor, enabled=False):
151 """
169 """
152 Register a preprocessor.
170 Register a preprocessor.
153 Preprocessors are classes that act upon the notebook before it is
171 Preprocessors are classes that act upon the notebook before it is
154 passed into the Jinja templating engine. preprocessors are also
172 passed into the Jinja templating engine. preprocessors are also
155 capable of passing additional information to the Jinja
173 capable of passing additional information to the Jinja
156 templating engine.
174 templating engine.
157
175
158 Parameters
176 Parameters
159 ----------
177 ----------
160 preprocessor : preprocessor
178 preprocessor : preprocessor
161 """
179 """
162 if preprocessor is None:
180 if preprocessor is None:
163 raise TypeError('preprocessor')
181 raise TypeError('preprocessor')
164 isclass = isinstance(preprocessor, type)
182 isclass = isinstance(preprocessor, type)
165 constructed = not isclass
183 constructed = not isclass
166
184
167 # Handle preprocessor's registration based on it's type
185 # Handle preprocessor's registration based on it's type
168 if constructed and isinstance(preprocessor, py3compat.string_types):
186 if constructed and isinstance(preprocessor, py3compat.string_types):
169 # Preprocessor is a string, import the namespace and recursively call
187 # Preprocessor is a string, import the namespace and recursively call
170 # this register_preprocessor method
188 # this register_preprocessor method
171 preprocessor_cls = import_item(preprocessor)
189 preprocessor_cls = import_item(preprocessor)
172 return self.register_preprocessor(preprocessor_cls, enabled)
190 return self.register_preprocessor(preprocessor_cls, enabled)
173
191
174 if constructed and hasattr(preprocessor, '__call__'):
192 if constructed and hasattr(preprocessor, '__call__'):
175 # Preprocessor is a function, no need to construct it.
193 # Preprocessor is a function, no need to construct it.
176 # Register and return the preprocessor.
194 # Register and return the preprocessor.
177 if enabled:
195 if enabled:
178 preprocessor.enabled = True
196 preprocessor.enabled = True
179 self._preprocessors.append(preprocessor)
197 self._preprocessors.append(preprocessor)
180 return preprocessor
198 return preprocessor
181
199
182 elif isclass and isinstance(preprocessor, MetaHasTraits):
200 elif isclass and isinstance(preprocessor, MetaHasTraits):
183 # Preprocessor is configurable. Make sure to pass in new default for
201 # Preprocessor is configurable. Make sure to pass in new default for
184 # the enabled flag if one was specified.
202 # the enabled flag if one was specified.
185 self.register_preprocessor(preprocessor(parent=self), enabled)
203 self.register_preprocessor(preprocessor(parent=self), enabled)
186
204
187 elif isclass:
205 elif isclass:
188 # Preprocessor is not configurable, construct it
206 # Preprocessor is not configurable, construct it
189 self.register_preprocessor(preprocessor(), enabled)
207 self.register_preprocessor(preprocessor(), enabled)
190
208
191 else:
209 else:
192 # Preprocessor is an instance of something without a __call__
210 # Preprocessor is an instance of something without a __call__
193 # attribute.
211 # attribute.
194 raise TypeError('preprocessor')
212 raise TypeError('preprocessor')
195
213
196
214
197 def _init_preprocessors(self):
215 def _init_preprocessors(self):
198 """
216 """
199 Register all of the preprocessors needed for this exporter, disabled
217 Register all of the preprocessors needed for this exporter, disabled
200 unless specified explicitly.
218 unless specified explicitly.
201 """
219 """
202 self._preprocessors = []
220 self._preprocessors = []
203
221
204 # Load default preprocessors (not necessarly enabled by default).
222 # Load default preprocessors (not necessarly enabled by default).
205 for preprocessor in self.default_preprocessors:
223 for preprocessor in self.default_preprocessors:
206 self.register_preprocessor(preprocessor)
224 self.register_preprocessor(preprocessor)
207
225
208 # Load user-specified preprocessors. Enable by default.
226 # Load user-specified preprocessors. Enable by default.
209 for preprocessor in self.preprocessors:
227 for preprocessor in self.preprocessors:
210 self.register_preprocessor(preprocessor, enabled=True)
228 self.register_preprocessor(preprocessor, enabled=True)
211
229
212
230
213 def _init_resources(self, resources):
231 def _init_resources(self, resources):
214
232
215 #Make sure the resources dict is of ResourcesDict type.
233 #Make sure the resources dict is of ResourcesDict type.
216 if resources is None:
234 if resources is None:
217 resources = ResourcesDict()
235 resources = ResourcesDict()
218 if not isinstance(resources, ResourcesDict):
236 if not isinstance(resources, ResourcesDict):
219 new_resources = ResourcesDict()
237 new_resources = ResourcesDict()
220 new_resources.update(resources)
238 new_resources.update(resources)
221 resources = new_resources
239 resources = new_resources
222
240
223 #Make sure the metadata extension exists in resources
241 #Make sure the metadata extension exists in resources
224 if 'metadata' in resources:
242 if 'metadata' in resources:
225 if not isinstance(resources['metadata'], ResourcesDict):
243 if not isinstance(resources['metadata'], ResourcesDict):
226 resources['metadata'] = ResourcesDict(resources['metadata'])
244 resources['metadata'] = ResourcesDict(resources['metadata'])
227 else:
245 else:
228 resources['metadata'] = ResourcesDict()
246 resources['metadata'] = ResourcesDict()
229 if not resources['metadata']['name']:
247 if not resources['metadata']['name']:
230 resources['metadata']['name'] = 'Notebook'
248 resources['metadata']['name'] = 'Notebook'
231
249
232 #Set the output extension
250 #Set the output extension
233 resources['output_extension'] = self.file_extension
251 resources['output_extension'] = self.file_extension
234 return resources
252 return resources
235
253
236
254
237 def _preprocess(self, nb, resources):
255 def _preprocess(self, nb, resources):
238 """
256 """
239 Preprocess the notebook before passing it into the Jinja engine.
257 Preprocess the notebook before passing it into the Jinja engine.
240 To preprocess the notebook is to apply all of the
258 To preprocess the notebook is to apply all of the
241
259
242 Parameters
260 Parameters
243 ----------
261 ----------
244 nb : notebook node
262 nb : notebook node
245 notebook that is being exported.
263 notebook that is being exported.
246 resources : a dict of additional resources that
264 resources : a dict of additional resources that
247 can be accessed read/write by preprocessors
265 can be accessed read/write by preprocessors
248 """
266 """
249
267
250 # Do a copy.deepcopy first,
268 # Do a copy.deepcopy first,
251 # we are never safe enough with what the preprocessors could do.
269 # we are never safe enough with what the preprocessors could do.
252 nbc = copy.deepcopy(nb)
270 nbc = copy.deepcopy(nb)
253 resc = copy.deepcopy(resources)
271 resc = copy.deepcopy(resources)
254
272
255 #Run each preprocessor on the notebook. Carry the output along
273 #Run each preprocessor on the notebook. Carry the output along
256 #to each preprocessor
274 #to each preprocessor
257 for preprocessor in self._preprocessors:
275 for preprocessor in self._preprocessors:
258 nbc, resc = preprocessor(nbc, resc)
276 nbc, resc = preprocessor(nbc, resc)
259 return nbc, resc
277 return nbc, resc
General Comments 0
You need to be logged in to leave comments. Login now