##// END OF EJS Templates
Don't use nbformat.current in nbconvert
MinRK -
Show More
@@ -1,174 +1,174 b''
1 """Module containing single call export functions."""
1 """Module containing single call export functions."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from functools import wraps
6 from functools import wraps
7
7
8 from IPython.nbformat.current import NotebookNode
8 from IPython.nbformat.v4 import NotebookNode
9 from IPython.utils.decorators import undoc
9 from IPython.utils.decorators import undoc
10 from IPython.utils.py3compat import string_types
10 from IPython.utils.py3compat import string_types
11
11
12 from .exporter import Exporter
12 from .exporter import Exporter
13 from .templateexporter import TemplateExporter
13 from .templateexporter import TemplateExporter
14 from .html import HTMLExporter
14 from .html import HTMLExporter
15 from .slides import SlidesExporter
15 from .slides import SlidesExporter
16 from .latex import LatexExporter
16 from .latex import LatexExporter
17 from .pdf import PDFExporter
17 from .pdf import PDFExporter
18 from .markdown import MarkdownExporter
18 from .markdown import MarkdownExporter
19 from .python import PythonExporter
19 from .python import PythonExporter
20 from .rst import RSTExporter
20 from .rst import RSTExporter
21 from .notebook import NotebookExporter
21 from .notebook import NotebookExporter
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Classes
24 # Classes
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 @undoc
27 @undoc
28 def DocDecorator(f):
28 def DocDecorator(f):
29
29
30 #Set docstring of function
30 #Set docstring of function
31 f.__doc__ = f.__doc__ + """
31 f.__doc__ = f.__doc__ + """
32 nb : :class:`~{nbnode_mod}.NotebookNode`
32 nb : :class:`~{nbnode_mod}.NotebookNode`
33 The notebook to export.
33 The notebook to export.
34 config : config (optional, keyword arg)
34 config : config (optional, keyword arg)
35 User configuration instance.
35 User configuration instance.
36 resources : dict (optional, keyword arg)
36 resources : dict (optional, keyword arg)
37 Resources used in the conversion process.
37 Resources used in the conversion process.
38
38
39 Returns
39 Returns
40 -------
40 -------
41 tuple- output, resources, exporter_instance
41 tuple- output, resources, exporter_instance
42 output : str
42 output : str
43 Jinja 2 output. This is the resulting converted notebook.
43 Jinja 2 output. This is the resulting converted notebook.
44 resources : dictionary
44 resources : dictionary
45 Dictionary of resources used prior to and during the conversion
45 Dictionary of resources used prior to and during the conversion
46 process.
46 process.
47 exporter_instance : Exporter
47 exporter_instance : Exporter
48 Instance of the Exporter class used to export the document. Useful
48 Instance of the Exporter class used to export the document. Useful
49 to caller because it provides a 'file_extension' property which
49 to caller because it provides a 'file_extension' property which
50 specifies what extension the output should be saved as.
50 specifies what extension the output should be saved as.
51
51
52 Notes
52 Notes
53 -----
53 -----
54 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT
54 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT
55 """.format(nbnode_mod=NotebookNode.__module__)
55 """.format(nbnode_mod=NotebookNode.__module__)
56
56
57 @wraps(f)
57 @wraps(f)
58 def decorator(*args, **kwargs):
58 def decorator(*args, **kwargs):
59 return f(*args, **kwargs)
59 return f(*args, **kwargs)
60
60
61 return decorator
61 return decorator
62
62
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Functions
65 # Functions
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67
67
68 __all__ = [
68 __all__ = [
69 'export',
69 'export',
70 'export_html',
70 'export_html',
71 'export_custom',
71 'export_custom',
72 'export_slides',
72 'export_slides',
73 'export_latex',
73 'export_latex',
74 'export_pdf',
74 'export_pdf',
75 'export_markdown',
75 'export_markdown',
76 'export_python',
76 'export_python',
77 'export_rst',
77 'export_rst',
78 'export_by_name',
78 'export_by_name',
79 'get_export_names',
79 'get_export_names',
80 'ExporterNameError'
80 'ExporterNameError'
81 ]
81 ]
82
82
83
83
84 class ExporterNameError(NameError):
84 class ExporterNameError(NameError):
85 pass
85 pass
86
86
87 @DocDecorator
87 @DocDecorator
88 def export(exporter, nb, **kw):
88 def export(exporter, nb, **kw):
89 """
89 """
90 Export a notebook object using specific exporter class.
90 Export a notebook object using specific exporter class.
91
91
92 Parameters
92 Parameters
93 ----------
93 ----------
94 exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance
94 exporter : class:`~IPython.nbconvert.exporters.exporter.Exporter` class or instance
95 Class type or instance of the exporter that should be used. If the
95 Class type or instance of the exporter that should be used. If the
96 method initializes it's own instance of the class, it is ASSUMED that
96 method initializes it's own instance of the class, it is ASSUMED that
97 the class type provided exposes a constructor (``__init__``) with the same
97 the class type provided exposes a constructor (``__init__``) with the same
98 signature as the base Exporter class.
98 signature as the base Exporter class.
99 """
99 """
100
100
101 #Check arguments
101 #Check arguments
102 if exporter is None:
102 if exporter is None:
103 raise TypeError("Exporter is None")
103 raise TypeError("Exporter is None")
104 elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter):
104 elif not isinstance(exporter, Exporter) and not issubclass(exporter, Exporter):
105 raise TypeError("exporter does not inherit from Exporter (base)")
105 raise TypeError("exporter does not inherit from Exporter (base)")
106 if nb is None:
106 if nb is None:
107 raise TypeError("nb is None")
107 raise TypeError("nb is None")
108
108
109 #Create the exporter
109 #Create the exporter
110 resources = kw.pop('resources', None)
110 resources = kw.pop('resources', None)
111 if isinstance(exporter, Exporter):
111 if isinstance(exporter, Exporter):
112 exporter_instance = exporter
112 exporter_instance = exporter
113 else:
113 else:
114 exporter_instance = exporter(**kw)
114 exporter_instance = exporter(**kw)
115
115
116 #Try to convert the notebook using the appropriate conversion function.
116 #Try to convert the notebook using the appropriate conversion function.
117 if isinstance(nb, NotebookNode):
117 if isinstance(nb, NotebookNode):
118 output, resources = exporter_instance.from_notebook_node(nb, resources)
118 output, resources = exporter_instance.from_notebook_node(nb, resources)
119 elif isinstance(nb, string_types):
119 elif isinstance(nb, string_types):
120 output, resources = exporter_instance.from_filename(nb, resources)
120 output, resources = exporter_instance.from_filename(nb, resources)
121 else:
121 else:
122 output, resources = exporter_instance.from_file(nb, resources)
122 output, resources = exporter_instance.from_file(nb, resources)
123 return output, resources
123 return output, resources
124
124
125 exporter_map = dict(
125 exporter_map = dict(
126 custom=TemplateExporter,
126 custom=TemplateExporter,
127 html=HTMLExporter,
127 html=HTMLExporter,
128 slides=SlidesExporter,
128 slides=SlidesExporter,
129 latex=LatexExporter,
129 latex=LatexExporter,
130 pdf=PDFExporter,
130 pdf=PDFExporter,
131 markdown=MarkdownExporter,
131 markdown=MarkdownExporter,
132 python=PythonExporter,
132 python=PythonExporter,
133 rst=RSTExporter,
133 rst=RSTExporter,
134 notebook=NotebookExporter,
134 notebook=NotebookExporter,
135 )
135 )
136
136
137 def _make_exporter(name, E):
137 def _make_exporter(name, E):
138 """make an export_foo function from a short key and Exporter class E"""
138 """make an export_foo function from a short key and Exporter class E"""
139 def _export(nb, **kw):
139 def _export(nb, **kw):
140 return export(E, nb, **kw)
140 return export(E, nb, **kw)
141 _export.__doc__ = """Export a notebook object to {0} format""".format(name)
141 _export.__doc__ = """Export a notebook object to {0} format""".format(name)
142 return _export
142 return _export
143
143
144 g = globals()
144 g = globals()
145
145
146 for name, E in exporter_map.items():
146 for name, E in exporter_map.items():
147 g['export_%s' % name] = DocDecorator(_make_exporter(name, E))
147 g['export_%s' % name] = DocDecorator(_make_exporter(name, E))
148
148
149 @DocDecorator
149 @DocDecorator
150 def export_by_name(format_name, nb, **kw):
150 def export_by_name(format_name, nb, **kw):
151 """
151 """
152 Export a notebook object to a template type by its name. Reflection
152 Export a notebook object to a template type by its name. Reflection
153 (Inspect) is used to find the template's corresponding explicit export
153 (Inspect) is used to find the template's corresponding explicit export
154 method defined in this module. That method is then called directly.
154 method defined in this module. That method is then called directly.
155
155
156 Parameters
156 Parameters
157 ----------
157 ----------
158 format_name : str
158 format_name : str
159 Name of the template style to export to.
159 Name of the template style to export to.
160 """
160 """
161
161
162 function_name = "export_" + format_name.lower()
162 function_name = "export_" + format_name.lower()
163
163
164 if function_name in globals():
164 if function_name in globals():
165 return globals()[function_name](nb, **kw)
165 return globals()[function_name](nb, **kw)
166 else:
166 else:
167 raise ExporterNameError("template for `%s` not found" % function_name)
167 raise ExporterNameError("template for `%s` not found" % function_name)
168
168
169
169
170 def get_export_names():
170 def get_export_names():
171 """Return a list of the currently supported export targets
171 """Return a list of the currently supported export targets
172
172
173 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT"""
173 WARNING: API WILL CHANGE IN FUTURE RELEASES OF NBCONVERT"""
174 return sorted(exporter_map.keys())
174 return sorted(exporter_map.keys())
@@ -1,276 +1,259 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 #-----------------------------------------------------------------------------
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
5
17 from __future__ import print_function, absolute_import
6 from __future__ import print_function, absolute_import
18
7
19 # Stdlib imports
20 import io
8 import io
21 import os
9 import os
22 import copy
10 import copy
23 import collections
11 import collections
24 import datetime
12 import datetime
25
13
26
27 # IPython imports
28 from IPython.config.configurable import LoggingConfigurable
14 from IPython.config.configurable import LoggingConfigurable
29 from IPython.config import Config
15 from IPython.config import Config
30 from IPython.nbformat import current as nbformat
16 from IPython import nbformat
31 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
17 from IPython.utils.traitlets import MetaHasTraits, Unicode, List
32 from IPython.utils.importstring import import_item
18 from IPython.utils.importstring import import_item
33 from IPython.utils import text, py3compat
19 from IPython.utils import text, py3compat
34
20
35 #-----------------------------------------------------------------------------
36 # Class
37 #-----------------------------------------------------------------------------
38
21
39 class ResourcesDict(collections.defaultdict):
22 class ResourcesDict(collections.defaultdict):
40 def __missing__(self, key):
23 def __missing__(self, key):
41 return ''
24 return ''
42
25
43
26
44 class Exporter(LoggingConfigurable):
27 class Exporter(LoggingConfigurable):
45 """
28 """
46 Class containing methods that sequentially run a list of preprocessors on a
29 Class containing methods that sequentially run a list of preprocessors on a
47 NotebookNode object and then return the modified NotebookNode object and
30 NotebookNode object and then return the modified NotebookNode object and
48 accompanying resources dict.
31 accompanying resources dict.
49 """
32 """
50
33
51 file_extension = Unicode(
34 file_extension = Unicode(
52 'txt', config=True,
35 'txt', config=True,
53 help="Extension of the file that should be written to disk"
36 help="Extension of the file that should be written to disk"
54 )
37 )
55
38
56 # MIME type of the result file, for HTTP response headers.
39 # MIME type of the result file, for HTTP response headers.
57 # This is *not* a traitlet, because we want to be able to access it from
40 # This is *not* a traitlet, because we want to be able to access it from
58 # the class, not just on instances.
41 # the class, not just on instances.
59 output_mimetype = ''
42 output_mimetype = ''
60
43
61 #Configurability, allows the user to easily add filters and preprocessors.
44 #Configurability, allows the user to easily add filters and preprocessors.
62 preprocessors = List(config=True,
45 preprocessors = List(config=True,
63 help="""List of preprocessors, by name or namespace, to enable.""")
46 help="""List of preprocessors, by name or namespace, to enable.""")
64
47
65 _preprocessors = List()
48 _preprocessors = List()
66
49
67 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
50 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
68 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
51 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
69 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
52 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
70 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
53 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
71 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
54 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
72 'IPython.nbconvert.preprocessors.LatexPreprocessor',
55 'IPython.nbconvert.preprocessors.LatexPreprocessor',
73 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
56 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
74 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
57 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
75 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
58 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
76 config=True,
59 config=True,
77 help="""List of preprocessors available by default, by name, namespace,
60 help="""List of preprocessors available by default, by name, namespace,
78 instance, or type.""")
61 instance, or type.""")
79
62
80
63
81 def __init__(self, config=None, **kw):
64 def __init__(self, config=None, **kw):
82 """
65 """
83 Public constructor
66 Public constructor
84
67
85 Parameters
68 Parameters
86 ----------
69 ----------
87 config : config
70 config : config
88 User configuration instance.
71 User configuration instance.
89 """
72 """
90 with_default_config = self.default_config
73 with_default_config = self.default_config
91 if config:
74 if config:
92 with_default_config.merge(config)
75 with_default_config.merge(config)
93
76
94 super(Exporter, self).__init__(config=with_default_config, **kw)
77 super(Exporter, self).__init__(config=with_default_config, **kw)
95
78
96 self._init_preprocessors()
79 self._init_preprocessors()
97
80
98
81
99 @property
82 @property
100 def default_config(self):
83 def default_config(self):
101 return Config()
84 return Config()
102
85
103 def from_notebook_node(self, nb, resources=None, **kw):
86 def from_notebook_node(self, nb, resources=None, **kw):
104 """
87 """
105 Convert a notebook from a notebook node instance.
88 Convert a notebook from a notebook node instance.
106
89
107 Parameters
90 Parameters
108 ----------
91 ----------
109 nb : :class:`~IPython.nbformat.current.NotebookNode`
92 nb : :class:`~IPython.nbformat.current.NotebookNode`
110 Notebook node
93 Notebook node (dict-like with attr-access)
111 resources : dict
94 resources : dict
112 Additional resources that can be accessed read/write by
95 Additional resources that can be accessed read/write by
113 preprocessors and filters.
96 preprocessors and filters.
114 **kw
97 **kw
115 Ignored (?)
98 Ignored (?)
116 """
99 """
117 nb_copy = copy.deepcopy(nb)
100 nb_copy = copy.deepcopy(nb)
118 resources = self._init_resources(resources)
101 resources = self._init_resources(resources)
119
102
120 if 'language' in nb['metadata']:
103 if 'language' in nb['metadata']:
121 resources['language'] = nb['metadata']['language'].lower()
104 resources['language'] = nb['metadata']['language'].lower()
122
105
123 # Preprocess
106 # Preprocess
124 nb_copy, resources = self._preprocess(nb_copy, resources)
107 nb_copy, resources = self._preprocess(nb_copy, resources)
125
108
126 return nb_copy, resources
109 return nb_copy, resources
127
110
128
111
129 def from_filename(self, filename, resources=None, **kw):
112 def from_filename(self, filename, resources=None, **kw):
130 """
113 """
131 Convert a notebook from a notebook file.
114 Convert a notebook from a notebook file.
132
115
133 Parameters
116 Parameters
134 ----------
117 ----------
135 filename : str
118 filename : str
136 Full filename of the notebook file to open and convert.
119 Full filename of the notebook file to open and convert.
137 """
120 """
138
121
139 # Pull the metadata from the filesystem.
122 # Pull the metadata from the filesystem.
140 if resources is None:
123 if resources is None:
141 resources = ResourcesDict()
124 resources = ResourcesDict()
142 if not 'metadata' in resources or resources['metadata'] == '':
125 if not 'metadata' in resources or resources['metadata'] == '':
143 resources['metadata'] = ResourcesDict()
126 resources['metadata'] = ResourcesDict()
144 basename = os.path.basename(filename)
127 basename = os.path.basename(filename)
145 notebook_name = basename[:basename.rfind('.')]
128 notebook_name = basename[:basename.rfind('.')]
146 resources['metadata']['name'] = notebook_name
129 resources['metadata']['name'] = notebook_name
147
130
148 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
131 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
149 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
132 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
150
133
151 with io.open(filename, encoding='utf-8') as f:
134 with io.open(filename, encoding='utf-8') as f:
152 return self.from_notebook_node(nbformat.read(f, 'json'), resources=resources, **kw)
135 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
153
136
154
137
155 def from_file(self, file_stream, resources=None, **kw):
138 def from_file(self, file_stream, resources=None, **kw):
156 """
139 """
157 Convert a notebook from a notebook file.
140 Convert a notebook from a notebook file.
158
141
159 Parameters
142 Parameters
160 ----------
143 ----------
161 file_stream : file-like object
144 file_stream : file-like object
162 Notebook file-like object to convert.
145 Notebook file-like object to convert.
163 """
146 """
164 return self.from_notebook_node(nbformat.read(file_stream, 'json'), resources=resources, **kw)
147 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
165
148
166
149
167 def register_preprocessor(self, preprocessor, enabled=False):
150 def register_preprocessor(self, preprocessor, enabled=False):
168 """
151 """
169 Register a preprocessor.
152 Register a preprocessor.
170 Preprocessors are classes that act upon the notebook before it is
153 Preprocessors are classes that act upon the notebook before it is
171 passed into the Jinja templating engine. preprocessors are also
154 passed into the Jinja templating engine. preprocessors are also
172 capable of passing additional information to the Jinja
155 capable of passing additional information to the Jinja
173 templating engine.
156 templating engine.
174
157
175 Parameters
158 Parameters
176 ----------
159 ----------
177 preprocessor : preprocessor
160 preprocessor : preprocessor
178 """
161 """
179 if preprocessor is None:
162 if preprocessor is None:
180 raise TypeError('preprocessor')
163 raise TypeError('preprocessor')
181 isclass = isinstance(preprocessor, type)
164 isclass = isinstance(preprocessor, type)
182 constructed = not isclass
165 constructed = not isclass
183
166
184 # Handle preprocessor's registration based on it's type
167 # Handle preprocessor's registration based on it's type
185 if constructed and isinstance(preprocessor, py3compat.string_types):
168 if constructed and isinstance(preprocessor, py3compat.string_types):
186 # Preprocessor is a string, import the namespace and recursively call
169 # Preprocessor is a string, import the namespace and recursively call
187 # this register_preprocessor method
170 # this register_preprocessor method
188 preprocessor_cls = import_item(preprocessor)
171 preprocessor_cls = import_item(preprocessor)
189 return self.register_preprocessor(preprocessor_cls, enabled)
172 return self.register_preprocessor(preprocessor_cls, enabled)
190
173
191 if constructed and hasattr(preprocessor, '__call__'):
174 if constructed and hasattr(preprocessor, '__call__'):
192 # Preprocessor is a function, no need to construct it.
175 # Preprocessor is a function, no need to construct it.
193 # Register and return the preprocessor.
176 # Register and return the preprocessor.
194 if enabled:
177 if enabled:
195 preprocessor.enabled = True
178 preprocessor.enabled = True
196 self._preprocessors.append(preprocessor)
179 self._preprocessors.append(preprocessor)
197 return preprocessor
180 return preprocessor
198
181
199 elif isclass and isinstance(preprocessor, MetaHasTraits):
182 elif isclass and isinstance(preprocessor, MetaHasTraits):
200 # Preprocessor is configurable. Make sure to pass in new default for
183 # Preprocessor is configurable. Make sure to pass in new default for
201 # the enabled flag if one was specified.
184 # the enabled flag if one was specified.
202 self.register_preprocessor(preprocessor(parent=self), enabled)
185 self.register_preprocessor(preprocessor(parent=self), enabled)
203
186
204 elif isclass:
187 elif isclass:
205 # Preprocessor is not configurable, construct it
188 # Preprocessor is not configurable, construct it
206 self.register_preprocessor(preprocessor(), enabled)
189 self.register_preprocessor(preprocessor(), enabled)
207
190
208 else:
191 else:
209 # Preprocessor is an instance of something without a __call__
192 # Preprocessor is an instance of something without a __call__
210 # attribute.
193 # attribute.
211 raise TypeError('preprocessor')
194 raise TypeError('preprocessor')
212
195
213
196
214 def _init_preprocessors(self):
197 def _init_preprocessors(self):
215 """
198 """
216 Register all of the preprocessors needed for this exporter, disabled
199 Register all of the preprocessors needed for this exporter, disabled
217 unless specified explicitly.
200 unless specified explicitly.
218 """
201 """
219 self._preprocessors = []
202 self._preprocessors = []
220
203
221 # Load default preprocessors (not necessarly enabled by default).
204 # Load default preprocessors (not necessarly enabled by default).
222 for preprocessor in self.default_preprocessors:
205 for preprocessor in self.default_preprocessors:
223 self.register_preprocessor(preprocessor)
206 self.register_preprocessor(preprocessor)
224
207
225 # Load user-specified preprocessors. Enable by default.
208 # Load user-specified preprocessors. Enable by default.
226 for preprocessor in self.preprocessors:
209 for preprocessor in self.preprocessors:
227 self.register_preprocessor(preprocessor, enabled=True)
210 self.register_preprocessor(preprocessor, enabled=True)
228
211
229
212
230 def _init_resources(self, resources):
213 def _init_resources(self, resources):
231
214
232 #Make sure the resources dict is of ResourcesDict type.
215 #Make sure the resources dict is of ResourcesDict type.
233 if resources is None:
216 if resources is None:
234 resources = ResourcesDict()
217 resources = ResourcesDict()
235 if not isinstance(resources, ResourcesDict):
218 if not isinstance(resources, ResourcesDict):
236 new_resources = ResourcesDict()
219 new_resources = ResourcesDict()
237 new_resources.update(resources)
220 new_resources.update(resources)
238 resources = new_resources
221 resources = new_resources
239
222
240 #Make sure the metadata extension exists in resources
223 #Make sure the metadata extension exists in resources
241 if 'metadata' in resources:
224 if 'metadata' in resources:
242 if not isinstance(resources['metadata'], ResourcesDict):
225 if not isinstance(resources['metadata'], ResourcesDict):
243 resources['metadata'] = ResourcesDict(resources['metadata'])
226 resources['metadata'] = ResourcesDict(resources['metadata'])
244 else:
227 else:
245 resources['metadata'] = ResourcesDict()
228 resources['metadata'] = ResourcesDict()
246 if not resources['metadata']['name']:
229 if not resources['metadata']['name']:
247 resources['metadata']['name'] = 'Notebook'
230 resources['metadata']['name'] = 'Notebook'
248
231
249 #Set the output extension
232 #Set the output extension
250 resources['output_extension'] = self.file_extension
233 resources['output_extension'] = self.file_extension
251 return resources
234 return resources
252
235
253
236
254 def _preprocess(self, nb, resources):
237 def _preprocess(self, nb, resources):
255 """
238 """
256 Preprocess the notebook before passing it into the Jinja engine.
239 Preprocess the notebook before passing it into the Jinja engine.
257 To preprocess the notebook is to apply all of the
240 To preprocess the notebook is to apply all of the
258
241
259 Parameters
242 Parameters
260 ----------
243 ----------
261 nb : notebook node
244 nb : notebook node
262 notebook that is being exported.
245 notebook that is being exported.
263 resources : a dict of additional resources that
246 resources : a dict of additional resources that
264 can be accessed read/write by preprocessors
247 can be accessed read/write by preprocessors
265 """
248 """
266
249
267 # Do a copy.deepcopy first,
250 # Do a copy.deepcopy first,
268 # we are never safe enough with what the preprocessors could do.
251 # we are never safe enough with what the preprocessors could do.
269 nbc = copy.deepcopy(nb)
252 nbc = copy.deepcopy(nb)
270 resc = copy.deepcopy(resources)
253 resc = copy.deepcopy(resources)
271
254
272 #Run each preprocessor on the notebook. Carry the output along
255 #Run each preprocessor on the notebook. Carry the output along
273 #to each preprocessor
256 #to each preprocessor
274 for preprocessor in self._preprocessors:
257 for preprocessor in self._preprocessors:
275 nbc, resc = preprocessor(nbc, resc)
258 nbc, resc = preprocessor(nbc, resc)
276 return nbc, resc
259 return nbc, resc
@@ -1,32 +1,32 b''
1 """NotebookExporter class"""
1 """NotebookExporter class"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from .exporter import Exporter
6 from .exporter import Exporter
7 from IPython.nbformat import current as nbformat
7 from IPython import nbformat
8 from IPython.utils.traitlets import Enum
8 from IPython.utils.traitlets import Enum
9
9
10 class NotebookExporter(Exporter):
10 class NotebookExporter(Exporter):
11 """Exports to an IPython notebook."""
11 """Exports to an IPython notebook."""
12
12
13 nbformat_version = Enum(list(range(2, nbformat.current_nbformat + 1)),
13 nbformat_version = Enum(list(nbformat.versions),
14 default_value=nbformat.current_nbformat,
14 default_value=nbformat.current_nbformat,
15 config=True,
15 config=True,
16 help="""The nbformat version to write.
16 help="""The nbformat version to write.
17 Use this to downgrade notebooks.
17 Use this to downgrade notebooks.
18 """
18 """
19 )
19 )
20 def _file_extension_default(self):
20 def _file_extension_default(self):
21 return 'ipynb'
21 return 'ipynb'
22
22
23 output_mimetype = 'application/json'
23 output_mimetype = 'application/json'
24
24
25 def from_notebook_node(self, nb, resources=None, **kw):
25 def from_notebook_node(self, nb, resources=None, **kw):
26 nb_copy, resources = super(NotebookExporter, self).from_notebook_node(nb, resources, **kw)
26 nb_copy, resources = super(NotebookExporter, self).from_notebook_node(nb, resources, **kw)
27 if self.nbformat_version != nbformat.current_nbformat:
27 if self.nbformat_version != nb_copy.nbformat:
28 resources['output_suffix'] = '.v%i' % self.nbformat_version
28 resources['output_suffix'] = '.v%i' % self.nbformat_version
29 else:
29 else:
30 resources['output_suffix'] = '.nbconvert'
30 resources['output_suffix'] = '.nbconvert'
31 output = nbformat.writes(nb_copy, version=self.nbformat_version)
31 output = nbformat.writes(nb_copy, version=self.nbformat_version)
32 return output, resources
32 return output, resources
@@ -1,102 +1,90 b''
1 """
1 """
2 Module with tests for export.py
2 Module with tests for export.py
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 # Copyright (c) IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
6 # 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
7
17 import os
8 import os
18
9
19 from IPython.nbformat import current as nbformat
10 from IPython import nbformat
20
11
21 from .base import ExportersTestsBase
12 from .base import ExportersTestsBase
22 from ..export import *
13 from ..export import *
23 from ..python import PythonExporter
14 from ..python import PythonExporter
24
15
25 #-----------------------------------------------------------------------------
26 # Class
27 #-----------------------------------------------------------------------------
28
16
29 class TestExport(ExportersTestsBase):
17 class TestExport(ExportersTestsBase):
30 """Contains test functions for export.py"""
18 """Contains test functions for export.py"""
31
19
32
20
33 def test_export_wrong_name(self):
21 def test_export_wrong_name(self):
34 """
22 """
35 Is the right error thrown when a bad template name is used?
23 Is the right error thrown when a bad template name is used?
36 """
24 """
37 try:
25 try:
38 export_by_name('not_a_name', self._get_notebook())
26 export_by_name('not_a_name', self._get_notebook())
39 except ExporterNameError as e:
27 except ExporterNameError as e:
40 pass
28 pass
41
29
42
30
43 def test_export_filename(self):
31 def test_export_filename(self):
44 """
32 """
45 Can a notebook be exported by filename?
33 Can a notebook be exported by filename?
46 """
34 """
47 (output, resources) = export_by_name('python', self._get_notebook())
35 (output, resources) = export_by_name('python', self._get_notebook())
48 assert len(output) > 0
36 assert len(output) > 0
49
37
50
38
51 def test_export_nbnode(self):
39 def test_export_nbnode(self):
52 """
40 """
53 Can a notebook be exported by a notebook node handle?
41 Can a notebook be exported by a notebook node handle?
54 """
42 """
55 with open(self._get_notebook(), 'r') as f:
43 with open(self._get_notebook(), 'r') as f:
56 notebook = nbformat.read(f, 'json')
44 notebook = nbformat.read(f, 4)
57 (output, resources) = export_by_name('python', notebook)
45 (output, resources) = export_by_name('python', notebook)
58 assert len(output) > 0
46 assert len(output) > 0
59
47
60
48
61 def test_export_filestream(self):
49 def test_export_filestream(self):
62 """
50 """
63 Can a notebook be exported by a filesteam?
51 Can a notebook be exported by a filesteam?
64 """
52 """
65 with open(self._get_notebook(), 'r') as f:
53 with open(self._get_notebook(), 'r') as f:
66 (output, resources) = export_by_name('python', f)
54 (output, resources) = export_by_name('python', f)
67 assert len(output) > 0
55 assert len(output) > 0
68
56
69
57
70 def test_export_using_exporter(self):
58 def test_export_using_exporter(self):
71 """
59 """
72 Can a notebook be exported using an instanciated exporter?
60 Can a notebook be exported using an instanciated exporter?
73 """
61 """
74 (output, resources) = export(PythonExporter(), self._get_notebook())
62 (output, resources) = export(PythonExporter(), self._get_notebook())
75 assert len(output) > 0
63 assert len(output) > 0
76
64
77
65
78 def test_export_using_exporter_class(self):
66 def test_export_using_exporter_class(self):
79 """
67 """
80 Can a notebook be exported using an exporter class type?
68 Can a notebook be exported using an exporter class type?
81 """
69 """
82 (output, resources) = export(PythonExporter, self._get_notebook())
70 (output, resources) = export(PythonExporter, self._get_notebook())
83 assert len(output) > 0
71 assert len(output) > 0
84
72
85
73
86 def test_export_resources(self):
74 def test_export_resources(self):
87 """
75 """
88 Can a notebook be exported along with a custom resources dict?
76 Can a notebook be exported along with a custom resources dict?
89 """
77 """
90 (output, resources) = export(PythonExporter, self._get_notebook(), resources={})
78 (output, resources) = export(PythonExporter, self._get_notebook(), resources={})
91 assert len(output) > 0
79 assert len(output) > 0
92
80
93
81
94 def test_no_exporter(self):
82 def test_no_exporter(self):
95 """
83 """
96 Is the right error thrown if no exporter is provided?
84 Is the right error thrown if no exporter is provided?
97 """
85 """
98 try:
86 try:
99 (output, resources) = export(None, self._get_notebook())
87 (output, resources) = export(None, self._get_notebook())
100 except TypeError:
88 except TypeError:
101 pass
89 pass
102
90
@@ -1,116 +1,117 b''
1 """Tests for Latex exporter"""
1 """Tests for Latex exporter"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import os.path
6 import os.path
7 import textwrap
7 import textwrap
8 import re
8 import re
9
9
10 from .base import ExportersTestsBase
10 from .base import ExportersTestsBase
11 from ..latex import LatexExporter
11 from ..latex import LatexExporter
12 from IPython.nbformat import current
12 from IPython.nbformat import write
13 from IPython.nbformat import v4
13 from IPython.testing.decorators import onlyif_cmds_exist
14 from IPython.testing.decorators import onlyif_cmds_exist
14 from IPython.utils.tempdir import TemporaryDirectory
15 from IPython.utils.tempdir import TemporaryDirectory
15
16
16
17
17 class TestLatexExporter(ExportersTestsBase):
18 class TestLatexExporter(ExportersTestsBase):
18 """Contains test functions for latex.py"""
19 """Contains test functions for latex.py"""
19
20
20 exporter_class = LatexExporter
21 exporter_class = LatexExporter
21 should_include_raw = ['latex']
22 should_include_raw = ['latex']
22
23
23 def test_constructor(self):
24 def test_constructor(self):
24 """
25 """
25 Can a LatexExporter be constructed?
26 Can a LatexExporter be constructed?
26 """
27 """
27 LatexExporter()
28 LatexExporter()
28
29
29
30
30 @onlyif_cmds_exist('pandoc')
31 @onlyif_cmds_exist('pandoc')
31 def test_export(self):
32 def test_export(self):
32 """
33 """
33 Can a LatexExporter export something?
34 Can a LatexExporter export something?
34 """
35 """
35 (output, resources) = LatexExporter().from_filename(self._get_notebook())
36 (output, resources) = LatexExporter().from_filename(self._get_notebook())
36 assert len(output) > 0
37 assert len(output) > 0
37
38
38
39
39 @onlyif_cmds_exist('pandoc')
40 @onlyif_cmds_exist('pandoc')
40 def test_export_book(self):
41 def test_export_book(self):
41 """
42 """
42 Can a LatexExporter export using 'report' template?
43 Can a LatexExporter export using 'report' template?
43 """
44 """
44 (output, resources) = LatexExporter(template_file='report').from_filename(self._get_notebook())
45 (output, resources) = LatexExporter(template_file='report').from_filename(self._get_notebook())
45 assert len(output) > 0
46 assert len(output) > 0
46
47
47
48
48 @onlyif_cmds_exist('pandoc')
49 @onlyif_cmds_exist('pandoc')
49 def test_export_basic(self):
50 def test_export_basic(self):
50 """
51 """
51 Can a LatexExporter export using 'article' template?
52 Can a LatexExporter export using 'article' template?
52 """
53 """
53 (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook())
54 (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook())
54 assert len(output) > 0
55 assert len(output) > 0
55
56
56
57
57 @onlyif_cmds_exist('pandoc')
58 @onlyif_cmds_exist('pandoc')
58 def test_export_article(self):
59 def test_export_article(self):
59 """
60 """
60 Can a LatexExporter export using 'article' template?
61 Can a LatexExporter export using 'article' template?
61 """
62 """
62 (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook())
63 (output, resources) = LatexExporter(template_file='article').from_filename(self._get_notebook())
63 assert len(output) > 0
64 assert len(output) > 0
64
65
65 @onlyif_cmds_exist('pandoc')
66 @onlyif_cmds_exist('pandoc')
66 def test_very_long_cells(self):
67 def test_very_long_cells(self):
67 """
68 """
68 Torture test that long cells do not cause issues
69 Torture test that long cells do not cause issues
69 """
70 """
70 lorem_ipsum_text = textwrap.dedent("""\
71 lorem_ipsum_text = textwrap.dedent("""\
71 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
72 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec
72 dignissim, ipsum non facilisis tempus, dui felis tincidunt metus,
73 dignissim, ipsum non facilisis tempus, dui felis tincidunt metus,
73 nec pulvinar neque odio eget risus. Nulla nisi lectus, cursus
74 nec pulvinar neque odio eget risus. Nulla nisi lectus, cursus
74 suscipit interdum at, ultrices sit amet orci. Mauris facilisis
75 suscipit interdum at, ultrices sit amet orci. Mauris facilisis
75 imperdiet elit, vitae scelerisque ipsum dignissim non. Integer
76 imperdiet elit, vitae scelerisque ipsum dignissim non. Integer
76 consequat malesuada neque sit amet pulvinar. Curabitur pretium
77 consequat malesuada neque sit amet pulvinar. Curabitur pretium
77 ut turpis eget aliquet. Maecenas sagittis lacus sed lectus
78 ut turpis eget aliquet. Maecenas sagittis lacus sed lectus
78 volutpat, eu adipiscing purus pulvinar. Maecenas consequat
79 volutpat, eu adipiscing purus pulvinar. Maecenas consequat
79 luctus urna, eget cursus quam mollis a. Aliquam vitae ornare
80 luctus urna, eget cursus quam mollis a. Aliquam vitae ornare
80 erat, non hendrerit urna. Sed eu diam nec massa egestas pharetra
81 erat, non hendrerit urna. Sed eu diam nec massa egestas pharetra
81 at nec tellus. Fusce feugiat lacus quis urna sollicitudin volutpat.
82 at nec tellus. Fusce feugiat lacus quis urna sollicitudin volutpat.
82 Quisque at sapien non nibh feugiat tempus ac ultricies purus.
83 Quisque at sapien non nibh feugiat tempus ac ultricies purus.
83 """)
84 """)
84 lorem_ipsum_text = lorem_ipsum_text.replace("\n"," ") + "\n\n"
85 lorem_ipsum_text = lorem_ipsum_text.replace("\n"," ") + "\n\n"
85 large_lorem_ipsum_text = "".join([lorem_ipsum_text]*3000)
86 large_lorem_ipsum_text = "".join([lorem_ipsum_text]*3000)
86
87
87 notebook_name = "lorem_ipsum_long.ipynb"
88 notebook_name = "lorem_ipsum_long.ipynb"
88 nb = current.new_notebook(
89 nb = v4.new_notebook(
89 cells=[
90 cells=[
90 current.new_markdown_cell(source=large_lorem_ipsum_text)
91 v4.new_markdown_cell(source=large_lorem_ipsum_text)
91 ]
92 ]
92 )
93 )
93
94
94 with TemporaryDirectory() as td:
95 with TemporaryDirectory() as td:
95 nbfile = os.path.join(td, notebook_name)
96 nbfile = os.path.join(td, notebook_name)
96 with open(nbfile, 'w') as f:
97 with open(nbfile, 'w') as f:
97 current.write(nb, f, 'ipynb')
98 write(f, nb, 4)
98
99
99 (output, resources) = LatexExporter(template_file='article').from_filename(nbfile)
100 (output, resources) = LatexExporter(template_file='article').from_filename(nbfile)
100 assert len(output) > 0
101 assert len(output) > 0
101
102
102 @onlyif_cmds_exist('pandoc')
103 @onlyif_cmds_exist('pandoc')
103 def test_prompt_number_color(self):
104 def test_prompt_number_color(self):
104 """
105 """
105 Does LatexExporter properly format input and output prompts in color?
106 Does LatexExporter properly format input and output prompts in color?
106 """
107 """
107 (output, resources) = LatexExporter().from_filename(
108 (output, resources) = LatexExporter().from_filename(
108 self._get_notebook(nb_name="prompt_numbers.ipynb"))
109 self._get_notebook(nb_name="prompt_numbers.ipynb"))
109 in_regex = r"In \[\{\\color\{incolor\}(.*)\}\]:"
110 in_regex = r"In \[\{\\color\{incolor\}(.*)\}\]:"
110 out_regex = r"Out\[\{\\color\{outcolor\}(.*)\}\]:"
111 out_regex = r"Out\[\{\\color\{outcolor\}(.*)\}\]:"
111
112
112 ins = ["2", "10", " ", " ", "*", "0"]
113 ins = ["2", "10", " ", " ", "*", "0"]
113 outs = ["10"]
114 outs = ["10"]
114
115
115 assert re.findall(in_regex, output) == ins
116 assert re.findall(in_regex, output) == ins
116 assert re.findall(out_regex, output) == outs
117 assert re.findall(out_regex, output) == outs
@@ -1,39 +1,39 b''
1 """Tests for notebook.py"""
1 """Tests for notebook.py"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import json
6 import json
7
7
8 from .base import ExportersTestsBase
8 from .base import ExportersTestsBase
9 from ..notebook import NotebookExporter
9 from ..notebook import NotebookExporter
10
10
11 from IPython.nbformat.current import validate
11 from IPython.nbformat import validate
12 from IPython.testing.tools import assert_big_text_equal
12 from IPython.testing.tools import assert_big_text_equal
13
13
14 class TestNotebookExporter(ExportersTestsBase):
14 class TestNotebookExporter(ExportersTestsBase):
15 """Contains test functions for notebook.py"""
15 """Contains test functions for notebook.py"""
16
16
17 exporter_class = NotebookExporter
17 exporter_class = NotebookExporter
18
18
19 def test_export(self):
19 def test_export(self):
20 """
20 """
21 Does the NotebookExporter return the file unchanged?
21 Does the NotebookExporter return the file unchanged?
22 """
22 """
23 with open(self._get_notebook()) as f:
23 with open(self._get_notebook()) as f:
24 file_contents = f.read()
24 file_contents = f.read()
25 (output, resources) = self.exporter_class().from_filename(self._get_notebook())
25 (output, resources) = self.exporter_class().from_filename(self._get_notebook())
26 assert len(output) > 0
26 assert len(output) > 0
27 assert_big_text_equal(output, file_contents)
27 assert_big_text_equal(output, file_contents)
28
28
29 def test_downgrade_3(self):
29 def test_downgrade_3(self):
30 exporter = self.exporter_class(nbformat_version=3)
30 exporter = self.exporter_class(nbformat_version=3)
31 (output, resources) = exporter.from_filename(self._get_notebook())
31 (output, resources) = exporter.from_filename(self._get_notebook())
32 nb = json.loads(output)
32 nb = json.loads(output)
33 validate(nb)
33 validate(nb)
34
34
35 def test_downgrade_2(self):
35 def test_downgrade_2(self):
36 exporter = self.exporter_class(nbformat_version=2)
36 exporter = self.exporter_class(nbformat_version=2)
37 (output, resources) = exporter.from_filename(self._get_notebook())
37 (output, resources) = exporter.from_filename(self._get_notebook())
38 nb = json.loads(output)
38 nb = json.loads(output)
39 self.assertEqual(nb['nbformat'], 2)
39 self.assertEqual(nb['nbformat'], 2)
@@ -1,62 +1,54 b''
1 """Tests for RSTExporter"""
1 """Tests for RSTExporter"""
2
2
3 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Copyright (c) 2013, the IPython Development Team.
5 #
6 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
5
15 import io
6 import io
16
7
17 from IPython.nbformat import current
8 from IPython import nbformat
9 from IPython.nbformat import v4
18
10
19 from .base import ExportersTestsBase
11 from .base import ExportersTestsBase
20 from ..rst import RSTExporter
12 from ..rst import RSTExporter
21 from IPython.testing.decorators import onlyif_cmds_exist
13 from IPython.testing.decorators import onlyif_cmds_exist
22
14
23
15
24 class TestRSTExporter(ExportersTestsBase):
16 class TestRSTExporter(ExportersTestsBase):
25 """Tests for RSTExporter"""
17 """Tests for RSTExporter"""
26
18
27 exporter_class = RSTExporter
19 exporter_class = RSTExporter
28 should_include_raw = ['rst']
20 should_include_raw = ['rst']
29
21
30 def test_constructor(self):
22 def test_constructor(self):
31 """
23 """
32 Can a RSTExporter be constructed?
24 Can a RSTExporter be constructed?
33 """
25 """
34 RSTExporter()
26 RSTExporter()
35
27
36
28
37 @onlyif_cmds_exist('pandoc')
29 @onlyif_cmds_exist('pandoc')
38 def test_export(self):
30 def test_export(self):
39 """
31 """
40 Can a RSTExporter export something?
32 Can a RSTExporter export something?
41 """
33 """
42 (output, resources) = RSTExporter().from_filename(self._get_notebook())
34 (output, resources) = RSTExporter().from_filename(self._get_notebook())
43 assert len(output) > 0
35 assert len(output) > 0
44
36
45 @onlyif_cmds_exist('pandoc')
37 @onlyif_cmds_exist('pandoc')
46 def test_empty_code_cell(self):
38 def test_empty_code_cell(self):
47 """No empty code cells in rst"""
39 """No empty code cells in rst"""
48 nbname = self._get_notebook()
40 nbname = self._get_notebook()
49 with io.open(nbname, encoding='utf8') as f:
41 with io.open(nbname, encoding='utf8') as f:
50 nb = current.read(f, 'json')
42 nb = nbformat.read(f, 4)
51
43
52 exporter = self.exporter_class()
44 exporter = self.exporter_class()
53
45
54 (output, resources) = exporter.from_notebook_node(nb)
46 (output, resources) = exporter.from_notebook_node(nb)
55 # add an empty code cell
47 # add an empty code cell
56 nb.cells.append(
48 nb.cells.append(
57 current.new_code_cell(source="")
49 v4.new_code_cell(source="")
58 )
50 )
59 (output2, resources) = exporter.from_notebook_node(nb)
51 (output2, resources) = exporter.from_notebook_node(nb)
60 # adding an empty code cell shouldn't change output
52 # adding an empty code cell shouldn't change output
61 self.assertEqual(output.strip(), output2.strip())
53 self.assertEqual(output.strip(), output2.strip())
62
54
@@ -1,111 +1,111 b''
1 """Module containing a preprocessor that removes the outputs from code cells"""
1 """Module containing a preprocessor that removes the outputs from code cells"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import os
6 import os
7 import sys
8
7
9 try:
8 try:
10 from queue import Empty # Py 3
9 from queue import Empty # Py 3
11 except ImportError:
10 except ImportError:
12 from Queue import Empty # Py 2
11 from Queue import Empty # Py 2
13
12
14 from IPython.utils.traitlets import List, Unicode
13 from IPython.utils.traitlets import List, Unicode
15
14
16 from IPython.nbformat.current import reads, writes, output_from_msg
15 from IPython.nbformat.v4 import output_from_msg
17 from .base import Preprocessor
16 from .base import Preprocessor
18 from IPython.utils.traitlets import Integer
17 from IPython.utils.traitlets import Integer
19
18
19
20 class ExecutePreprocessor(Preprocessor):
20 class ExecutePreprocessor(Preprocessor):
21 """
21 """
22 Executes all the cells in a notebook
22 Executes all the cells in a notebook
23 """
23 """
24
24
25 timeout = Integer(30, config=True,
25 timeout = Integer(30, config=True,
26 help="The time to wait (in seconds) for output from executions."
26 help="The time to wait (in seconds) for output from executions."
27 )
27 )
28
28
29 extra_arguments = List(Unicode)
29 extra_arguments = List(Unicode)
30
30
31 def preprocess(self, nb, resources):
31 def preprocess(self, nb, resources):
32 from IPython.kernel import run_kernel
32 from IPython.kernel import run_kernel
33 kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python')
33 kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python')
34 self.log.info("Executing notebook with kernel: %s" % kernel_name)
34 self.log.info("Executing notebook with kernel: %s" % kernel_name)
35 with run_kernel(kernel_name=kernel_name,
35 with run_kernel(kernel_name=kernel_name,
36 extra_arguments=self.extra_arguments,
36 extra_arguments=self.extra_arguments,
37 stderr=open(os.devnull, 'w')) as kc:
37 stderr=open(os.devnull, 'w')) as kc:
38 self.kc = kc
38 self.kc = kc
39 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
39 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
40 return nb, resources
40 return nb, resources
41
41
42 def preprocess_cell(self, cell, resources, cell_index):
42 def preprocess_cell(self, cell, resources, cell_index):
43 """
43 """
44 Apply a transformation on each code cell. See base.py for details.
44 Apply a transformation on each code cell. See base.py for details.
45 """
45 """
46 if cell.cell_type != 'code':
46 if cell.cell_type != 'code':
47 return cell, resources
47 return cell, resources
48 try:
48 try:
49 outputs = self.run_cell(self.kc.shell_channel, self.kc.iopub_channel, cell)
49 outputs = self.run_cell(self.kc.shell_channel, self.kc.iopub_channel, cell)
50 except Exception as e:
50 except Exception as e:
51 self.log.error("failed to run cell: " + repr(e))
51 self.log.error("failed to run cell: " + repr(e))
52 self.log.error(str(cell.source))
52 self.log.error(str(cell.source))
53 raise
53 raise
54 cell.outputs = outputs
54 cell.outputs = outputs
55 return cell, resources
55 return cell, resources
56
56
57 def run_cell(self, shell, iopub, cell):
57 def run_cell(self, shell, iopub, cell):
58 msg_id = shell.execute(cell.source)
58 msg_id = shell.execute(cell.source)
59 self.log.debug("Executing cell:\n%s", cell.source)
59 self.log.debug("Executing cell:\n%s", cell.source)
60 # wait for finish, with timeout
60 # wait for finish, with timeout
61 while True:
61 while True:
62 try:
62 try:
63 msg = shell.get_msg(timeout=self.timeout)
63 msg = shell.get_msg(timeout=self.timeout)
64 except Empty:
64 except Empty:
65 self.log.error("Timeout waiting for execute reply")
65 self.log.error("Timeout waiting for execute reply")
66 raise
66 raise
67 if msg['parent_header'].get('msg_id') == msg_id:
67 if msg['parent_header'].get('msg_id') == msg_id:
68 break
68 break
69 else:
69 else:
70 # not our reply
70 # not our reply
71 continue
71 continue
72
72
73 outs = []
73 outs = []
74
74
75 while True:
75 while True:
76 try:
76 try:
77 msg = iopub.get_msg(timeout=self.timeout)
77 msg = iopub.get_msg(timeout=self.timeout)
78 except Empty:
78 except Empty:
79 self.log.warn("Timeout waiting for IOPub output")
79 self.log.warn("Timeout waiting for IOPub output")
80 break
80 break
81 if msg['parent_header'].get('msg_id') != msg_id:
81 if msg['parent_header'].get('msg_id') != msg_id:
82 # not an output from our execution
82 # not an output from our execution
83 continue
83 continue
84
84
85 msg_type = msg['msg_type']
85 msg_type = msg['msg_type']
86 self.log.debug("output: %s", msg_type)
86 self.log.debug("output: %s", msg_type)
87 content = msg['content']
87 content = msg['content']
88
88
89 # set the prompt number for the input and the output
89 # set the prompt number for the input and the output
90 if 'execution_count' in content:
90 if 'execution_count' in content:
91 cell['execution_count'] = content['execution_count']
91 cell['execution_count'] = content['execution_count']
92
92
93 if msg_type == 'status':
93 if msg_type == 'status':
94 if content['execution_state'] == 'idle':
94 if content['execution_state'] == 'idle':
95 break
95 break
96 else:
96 else:
97 continue
97 continue
98 elif msg_type == 'execute_input':
98 elif msg_type == 'execute_input':
99 continue
99 continue
100 elif msg_type == 'clear_output':
100 elif msg_type == 'clear_output':
101 outs = []
101 outs = []
102 continue
102 continue
103
103
104 try:
104 try:
105 out = output_from_msg(msg)
105 out = output_from_msg(msg)
106 except ValueError:
106 except ValueError:
107 self.log.error("unhandled iopub msg: " + msg_type)
107 self.log.error("unhandled iopub msg: " + msg_type)
108 else:
108 else:
109 outs.append(out)
109 outs.append(out)
110
110
111 return outs
111 return outs
@@ -1,41 +1,41 b''
1 """utility functions for preprocessor tests"""
1 """utility functions for preprocessor tests"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from IPython.nbformat import current as nbformat
6 from IPython.nbformat import v4 as nbformat
7
7
8 from ...tests.base import TestsBase
8 from ...tests.base import TestsBase
9 from ...exporters.exporter import ResourcesDict
9 from ...exporters.exporter import ResourcesDict
10
10
11
11
12 class PreprocessorTestsBase(TestsBase):
12 class PreprocessorTestsBase(TestsBase):
13 """Contains test functions preprocessor tests"""
13 """Contains test functions preprocessor tests"""
14
14
15
15
16 def build_notebook(self):
16 def build_notebook(self):
17 """Build a notebook in memory for use with preprocessor tests"""
17 """Build a notebook in memory for use with preprocessor tests"""
18
18
19 outputs = [
19 outputs = [
20 nbformat.new_output("stream", name="stdout", text="a"),
20 nbformat.new_output("stream", name="stdout", text="a"),
21 nbformat.new_output("display_data", data={'text/plain': 'b'}),
21 nbformat.new_output("display_data", data={'text/plain': 'b'}),
22 nbformat.new_output("stream", name="stdout", text="c"),
22 nbformat.new_output("stream", name="stdout", text="c"),
23 nbformat.new_output("stream", name="stdout", text="d"),
23 nbformat.new_output("stream", name="stdout", text="d"),
24 nbformat.new_output("stream", name="stderr", text="e"),
24 nbformat.new_output("stream", name="stderr", text="e"),
25 nbformat.new_output("stream", name="stderr", text="f"),
25 nbformat.new_output("stream", name="stderr", text="f"),
26 nbformat.new_output("display_data", data={'image/png': 'Zw=='}), # g
26 nbformat.new_output("display_data", data={'image/png': 'Zw=='}), # g
27 nbformat.new_output("display_data", data={'application/pdf': 'aA=='}), # h
27 nbformat.new_output("display_data", data={'application/pdf': 'aA=='}), # h
28 ]
28 ]
29
29
30 cells=[nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs),
30 cells=[nbformat.new_code_cell(source="$ e $", execution_count=1, outputs=outputs),
31 nbformat.new_markdown_cell(source="$ e $")]
31 nbformat.new_markdown_cell(source="$ e $")]
32
32
33 return nbformat.new_notebook(cells=cells)
33 return nbformat.new_notebook(cells=cells)
34
34
35
35
36 def build_resources(self):
36 def build_resources(self):
37 """Build an empty resources dictionary."""
37 """Build an empty resources dictionary."""
38
38
39 res = ResourcesDict()
39 res = ResourcesDict()
40 res['metadata'] = ResourcesDict()
40 res['metadata'] = ResourcesDict()
41 return res
41 return res
@@ -1,35 +1,33 b''
1 """
1 """
2 Module with tests for the clearoutput preprocessor.
2 Module with tests for the clearoutput preprocessor.
3 """
3 """
4
4
5 # Copyright (c) IPython Development Team.
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7
7
8 from IPython.nbformat import current as nbformat
9
10 from .base import PreprocessorTestsBase
8 from .base import PreprocessorTestsBase
11 from ..clearoutput import ClearOutputPreprocessor
9 from ..clearoutput import ClearOutputPreprocessor
12
10
13
11
14 class TestClearOutput(PreprocessorTestsBase):
12 class TestClearOutput(PreprocessorTestsBase):
15 """Contains test functions for clearoutput.py"""
13 """Contains test functions for clearoutput.py"""
16
14
17
15
18 def build_preprocessor(self):
16 def build_preprocessor(self):
19 """Make an instance of a preprocessor"""
17 """Make an instance of a preprocessor"""
20 preprocessor = ClearOutputPreprocessor()
18 preprocessor = ClearOutputPreprocessor()
21 preprocessor.enabled = True
19 preprocessor.enabled = True
22 return preprocessor
20 return preprocessor
23
21
24 def test_constructor(self):
22 def test_constructor(self):
25 """Can a ClearOutputPreprocessor be constructed?"""
23 """Can a ClearOutputPreprocessor be constructed?"""
26 self.build_preprocessor()
24 self.build_preprocessor()
27
25
28 def test_output(self):
26 def test_output(self):
29 """Test the output of the ClearOutputPreprocessor"""
27 """Test the output of the ClearOutputPreprocessor"""
30 nb = self.build_notebook()
28 nb = self.build_notebook()
31 res = self.build_resources()
29 res = self.build_resources()
32 preprocessor = self.build_preprocessor()
30 preprocessor = self.build_preprocessor()
33 nb, res = preprocessor(nb, res)
31 nb, res = preprocessor(nb, res)
34 assert nb.cells[0].outputs == []
32 assert nb.cells[0].outputs == []
35 assert nb.cells[0].execution_count is None
33 assert nb.cells[0].execution_count is None
@@ -1,58 +1,58 b''
1 """Tests for the coalescestreams preprocessor"""
1 """Tests for the coalescestreams preprocessor"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from IPython.nbformat import current as nbformat
6 from IPython.nbformat import v4 as nbformat
7
7
8 from .base import PreprocessorTestsBase
8 from .base import PreprocessorTestsBase
9 from ..coalescestreams import coalesce_streams
9 from ..coalescestreams import coalesce_streams
10
10
11
11
12 class TestCoalesceStreams(PreprocessorTestsBase):
12 class TestCoalesceStreams(PreprocessorTestsBase):
13 """Contains test functions for coalescestreams.py"""
13 """Contains test functions for coalescestreams.py"""
14
14
15 def test_coalesce_streams(self):
15 def test_coalesce_streams(self):
16 """coalesce_streams preprocessor output test"""
16 """coalesce_streams preprocessor output test"""
17 nb = self.build_notebook()
17 nb = self.build_notebook()
18 res = self.build_resources()
18 res = self.build_resources()
19 nb, res = coalesce_streams(nb, res)
19 nb, res = coalesce_streams(nb, res)
20 outputs = nb.cells[0].outputs
20 outputs = nb.cells[0].outputs
21 self.assertEqual(outputs[0].text, "a")
21 self.assertEqual(outputs[0].text, "a")
22 self.assertEqual(outputs[1].output_type, "display_data")
22 self.assertEqual(outputs[1].output_type, "display_data")
23 self.assertEqual(outputs[2].text, "cd")
23 self.assertEqual(outputs[2].text, "cd")
24 self.assertEqual(outputs[3].text, "ef")
24 self.assertEqual(outputs[3].text, "ef")
25
25
26 def test_coalesce_sequenced_streams(self):
26 def test_coalesce_sequenced_streams(self):
27 """Can the coalesce streams preprocessor merge a sequence of streams?"""
27 """Can the coalesce streams preprocessor merge a sequence of streams?"""
28 outputs = [nbformat.new_output(output_type="stream", name="stdout", text="0"),
28 outputs = [nbformat.new_output(output_type="stream", name="stdout", text="0"),
29 nbformat.new_output(output_type="stream", name="stdout", text="1"),
29 nbformat.new_output(output_type="stream", name="stdout", text="1"),
30 nbformat.new_output(output_type="stream", name="stdout", text="2"),
30 nbformat.new_output(output_type="stream", name="stdout", text="2"),
31 nbformat.new_output(output_type="stream", name="stdout", text="3"),
31 nbformat.new_output(output_type="stream", name="stdout", text="3"),
32 nbformat.new_output(output_type="stream", name="stdout", text="4"),
32 nbformat.new_output(output_type="stream", name="stdout", text="4"),
33 nbformat.new_output(output_type="stream", name="stdout", text="5"),
33 nbformat.new_output(output_type="stream", name="stdout", text="5"),
34 nbformat.new_output(output_type="stream", name="stdout", text="6"),
34 nbformat.new_output(output_type="stream", name="stdout", text="6"),
35 nbformat.new_output(output_type="stream", name="stdout", text="7")]
35 nbformat.new_output(output_type="stream", name="stdout", text="7")]
36 cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)]
36 cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)]
37
37
38 nb = nbformat.new_notebook(cells=cells)
38 nb = nbformat.new_notebook(cells=cells)
39 res = self.build_resources()
39 res = self.build_resources()
40 nb, res = coalesce_streams(nb, res)
40 nb, res = coalesce_streams(nb, res)
41 outputs = nb.cells[0].outputs
41 outputs = nb.cells[0].outputs
42 self.assertEqual(outputs[0].text, u'01234567')
42 self.assertEqual(outputs[0].text, u'01234567')
43
43
44 def test_coalesce_replace_streams(self):
44 def test_coalesce_replace_streams(self):
45 """Are \\r characters handled?"""
45 """Are \\r characters handled?"""
46 outputs = [nbformat.new_output(output_type="stream", name="stdout", text="z"),
46 outputs = [nbformat.new_output(output_type="stream", name="stdout", text="z"),
47 nbformat.new_output(output_type="stream", name="stdout", text="\ra"),
47 nbformat.new_output(output_type="stream", name="stdout", text="\ra"),
48 nbformat.new_output(output_type="stream", name="stdout", text="\nz\rb"),
48 nbformat.new_output(output_type="stream", name="stdout", text="\nz\rb"),
49 nbformat.new_output(output_type="stream", name="stdout", text="\nz"),
49 nbformat.new_output(output_type="stream", name="stdout", text="\nz"),
50 nbformat.new_output(output_type="stream", name="stdout", text="\rc\n"),
50 nbformat.new_output(output_type="stream", name="stdout", text="\rc\n"),
51 nbformat.new_output(output_type="stream", name="stdout", text="z\rz\rd")]
51 nbformat.new_output(output_type="stream", name="stdout", text="z\rz\rd")]
52 cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)]
52 cells=[nbformat.new_code_cell(source="# None", execution_count=1,outputs=outputs)]
53
53
54 nb = nbformat.new_notebook(cells=cells)
54 nb = nbformat.new_notebook(cells=cells)
55 res = self.build_resources()
55 res = self.build_resources()
56 nb, res = coalesce_streams(nb, res)
56 nb, res = coalesce_streams(nb, res)
57 outputs = nb.cells[0].outputs
57 outputs = nb.cells[0].outputs
58 self.assertEqual(outputs[0].text, u'a\nb\nc\nd')
58 self.assertEqual(outputs[0].text, u'a\nb\nc\nd')
@@ -1,91 +1,92 b''
1 """
1 """
2 Module with tests for the execute preprocessor.
2 Module with tests for the execute preprocessor.
3 """
3 """
4
4
5 # Copyright (c) IPython Development Team.
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7
7
8 import copy
8 import copy
9 import glob
9 import glob
10 import io
10 import os
11 import os
11 import re
12 import re
12
13
13 from IPython.nbformat import current as nbformat
14 from IPython import nbformat
14
15
15 from .base import PreprocessorTestsBase
16 from .base import PreprocessorTestsBase
16 from ..execute import ExecutePreprocessor
17 from ..execute import ExecutePreprocessor
17
18
18 from IPython.nbconvert.filters import strip_ansi
19 from IPython.nbconvert.filters import strip_ansi
19
20
20 addr_pat = re.compile(r'0x[0-9a-f]{7,9}')
21 addr_pat = re.compile(r'0x[0-9a-f]{7,9}')
21
22
22 class TestExecute(PreprocessorTestsBase):
23 class TestExecute(PreprocessorTestsBase):
23 """Contains test functions for execute.py"""
24 """Contains test functions for execute.py"""
24
25
25 @staticmethod
26 @staticmethod
26 def normalize_output(output):
27 def normalize_output(output):
27 """
28 """
28 Normalizes outputs for comparison.
29 Normalizes outputs for comparison.
29 """
30 """
30 output = dict(output)
31 output = dict(output)
31 if 'metadata' in output:
32 if 'metadata' in output:
32 del output['metadata']
33 del output['metadata']
33 if 'text' in output:
34 if 'text' in output:
34 output['text'] = re.sub(addr_pat, '<HEXADDR>', output['text'])
35 output['text'] = re.sub(addr_pat, '<HEXADDR>', output['text'])
35 if 'text/plain' in output.get('data', {}):
36 if 'text/plain' in output.get('data', {}):
36 output['data']['text/plain'] = \
37 output['data']['text/plain'] = \
37 re.sub(addr_pat, '<HEXADDR>', output['data']['text/plain'])
38 re.sub(addr_pat, '<HEXADDR>', output['data']['text/plain'])
38 if 'traceback' in output:
39 if 'traceback' in output:
39 tb = []
40 tb = []
40 for line in output['traceback']:
41 for line in output['traceback']:
41 tb.append(strip_ansi(line))
42 tb.append(strip_ansi(line))
42 output['traceback'] = tb
43 output['traceback'] = tb
43
44
44 return output
45 return output
45
46
46
47
47 def assert_notebooks_equal(self, expected, actual):
48 def assert_notebooks_equal(self, expected, actual):
48 expected_cells = expected['cells']
49 expected_cells = expected['cells']
49 actual_cells = actual['cells']
50 actual_cells = actual['cells']
50 self.assertEqual(len(expected_cells), len(actual_cells))
51 self.assertEqual(len(expected_cells), len(actual_cells))
51
52
52 for expected_cell, actual_cell in zip(expected_cells, actual_cells):
53 for expected_cell, actual_cell in zip(expected_cells, actual_cells):
53 expected_outputs = expected_cell.get('outputs', [])
54 expected_outputs = expected_cell.get('outputs', [])
54 actual_outputs = actual_cell.get('outputs', [])
55 actual_outputs = actual_cell.get('outputs', [])
55 normalized_expected_outputs = list(map(self.normalize_output, expected_outputs))
56 normalized_expected_outputs = list(map(self.normalize_output, expected_outputs))
56 normalized_actual_outputs = list(map(self.normalize_output, actual_outputs))
57 normalized_actual_outputs = list(map(self.normalize_output, actual_outputs))
57 self.assertEqual(normalized_expected_outputs, normalized_actual_outputs)
58 self.assertEqual(normalized_expected_outputs, normalized_actual_outputs)
58
59
59 expected_execution_count = expected_cell.get('execution_count', None)
60 expected_execution_count = expected_cell.get('execution_count', None)
60 actual_execution_count = actual_cell.get('execution_count', None)
61 actual_execution_count = actual_cell.get('execution_count', None)
61 self.assertEqual(expected_execution_count, actual_execution_count)
62 self.assertEqual(expected_execution_count, actual_execution_count)
62
63
63
64
64 def build_preprocessor(self):
65 def build_preprocessor(self):
65 """Make an instance of a preprocessor"""
66 """Make an instance of a preprocessor"""
66 preprocessor = ExecutePreprocessor()
67 preprocessor = ExecutePreprocessor()
67 preprocessor.enabled = True
68 preprocessor.enabled = True
68 return preprocessor
69 return preprocessor
69
70
70
71
71 def test_constructor(self):
72 def test_constructor(self):
72 """Can a ExecutePreprocessor be constructed?"""
73 """Can a ExecutePreprocessor be constructed?"""
73 self.build_preprocessor()
74 self.build_preprocessor()
74
75
75
76
76 def test_run_notebooks(self):
77 def test_run_notebooks(self):
77 """Runs a series of test notebooks and compares them to their actual output"""
78 """Runs a series of test notebooks and compares them to their actual output"""
78 current_dir = os.path.dirname(__file__)
79 current_dir = os.path.dirname(__file__)
79 input_files = glob.glob(os.path.join(current_dir, 'files', '*.ipynb'))
80 input_files = glob.glob(os.path.join(current_dir, 'files', '*.ipynb'))
80 for filename in input_files:
81 for filename in input_files:
81 with open(os.path.join(current_dir, 'files', filename)) as f:
82 with io.open(os.path.join(current_dir, 'files', filename)) as f:
82 input_nb = nbformat.read(f, 'ipynb')
83 input_nb = nbformat.read(f, 4)
83 res = self.build_resources()
84 res = self.build_resources()
84 preprocessor = self.build_preprocessor()
85 preprocessor = self.build_preprocessor()
85 cleaned_input_nb = copy.deepcopy(input_nb)
86 cleaned_input_nb = copy.deepcopy(input_nb)
86 for cell in cleaned_input_nb.cells:
87 for cell in cleaned_input_nb.cells:
87 if 'execution_count' in cell:
88 if 'execution_count' in cell:
88 del cell['execution_count']
89 del cell['execution_count']
89 cell['outputs'] = []
90 cell['outputs'] = []
90 output_nb, _ = preprocessor(cleaned_input_nb, res)
91 output_nb, _ = preprocessor(cleaned_input_nb, res)
91 self.assert_notebooks_equal(output_nb, input_nb)
92 self.assert_notebooks_equal(output_nb, input_nb)
@@ -1,78 +1,78 b''
1 """Tests for the revealhelp preprocessor"""
1 """Tests for the revealhelp preprocessor"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from IPython.nbformat import current as nbformat
6 from IPython.nbformat import v4 as nbformat
7
7
8 from .base import PreprocessorTestsBase
8 from .base import PreprocessorTestsBase
9 from ..revealhelp import RevealHelpPreprocessor
9 from ..revealhelp import RevealHelpPreprocessor
10
10
11
11
12 class Testrevealhelp(PreprocessorTestsBase):
12 class Testrevealhelp(PreprocessorTestsBase):
13 """Contains test functions for revealhelp.py"""
13 """Contains test functions for revealhelp.py"""
14
14
15 def build_notebook(self):
15 def build_notebook(self):
16 """Build a reveal slides notebook in memory for use with tests.
16 """Build a reveal slides notebook in memory for use with tests.
17 Overrides base in PreprocessorTestsBase"""
17 Overrides base in PreprocessorTestsBase"""
18
18
19 outputs = [nbformat.new_output(output_type="stream", name="stdout", text="a")]
19 outputs = [nbformat.new_output(output_type="stream", name="stdout", text="a")]
20
20
21 slide_metadata = {'slideshow' : {'slide_type': 'slide'}}
21 slide_metadata = {'slideshow' : {'slide_type': 'slide'}}
22 subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}}
22 subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}}
23
23
24 cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs),
24 cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs),
25 nbformat.new_markdown_cell(source="", metadata=slide_metadata),
25 nbformat.new_markdown_cell(source="", metadata=slide_metadata),
26 nbformat.new_code_cell(source="", execution_count=2, outputs=outputs),
26 nbformat.new_code_cell(source="", execution_count=2, outputs=outputs),
27 nbformat.new_markdown_cell(source="", metadata=slide_metadata),
27 nbformat.new_markdown_cell(source="", metadata=slide_metadata),
28 nbformat.new_markdown_cell(source="", metadata=subslide_metadata)]
28 nbformat.new_markdown_cell(source="", metadata=subslide_metadata)]
29
29
30 return nbformat.new_notebook(cells=cells)
30 return nbformat.new_notebook(cells=cells)
31
31
32
32
33 def build_preprocessor(self):
33 def build_preprocessor(self):
34 """Make an instance of a preprocessor"""
34 """Make an instance of a preprocessor"""
35 preprocessor = RevealHelpPreprocessor()
35 preprocessor = RevealHelpPreprocessor()
36 preprocessor.enabled = True
36 preprocessor.enabled = True
37 return preprocessor
37 return preprocessor
38
38
39
39
40 def test_constructor(self):
40 def test_constructor(self):
41 """Can a RevealHelpPreprocessor be constructed?"""
41 """Can a RevealHelpPreprocessor be constructed?"""
42 self.build_preprocessor()
42 self.build_preprocessor()
43
43
44
44
45 def test_reveal_attribute(self):
45 def test_reveal_attribute(self):
46 """Make sure the reveal url_prefix resources is set"""
46 """Make sure the reveal url_prefix resources is set"""
47 nb = self.build_notebook()
47 nb = self.build_notebook()
48 res = self.build_resources()
48 res = self.build_resources()
49 preprocessor = self.build_preprocessor()
49 preprocessor = self.build_preprocessor()
50 nb, res = preprocessor(nb, res)
50 nb, res = preprocessor(nb, res)
51 assert 'reveal' in res
51 assert 'reveal' in res
52 assert 'url_prefix' in res['reveal']
52 assert 'url_prefix' in res['reveal']
53
53
54
54
55 def test_reveal_output(self):
55 def test_reveal_output(self):
56 """Make sure that the reveal preprocessor """
56 """Make sure that the reveal preprocessor """
57 nb = self.build_notebook()
57 nb = self.build_notebook()
58 res = self.build_resources()
58 res = self.build_resources()
59 preprocessor = self.build_preprocessor()
59 preprocessor = self.build_preprocessor()
60 nb, res = preprocessor(nb, res)
60 nb, res = preprocessor(nb, res)
61 cells = nb.cells
61 cells = nb.cells
62
62
63 # Make sure correct metadata tags are available on every cell.
63 # Make sure correct metadata tags are available on every cell.
64 for cell in cells:
64 for cell in cells:
65 assert 'slide_type' in cell.metadata
65 assert 'slide_type' in cell.metadata
66
66
67 # Make sure slide end is only applied to the cells preceeding slide
67 # Make sure slide end is only applied to the cells preceeding slide
68 # cells.
68 # cells.
69 assert 'slide_helper' in cells[1].metadata
69 assert 'slide_helper' in cells[1].metadata
70 self.assertEqual(cells[1].metadata['slide_helper'], '-')
70 self.assertEqual(cells[1].metadata['slide_helper'], '-')
71
71
72 # Verify 'slide-end'
72 # Verify 'slide-end'
73 assert 'slide_helper' in cells[0].metadata
73 assert 'slide_helper' in cells[0].metadata
74 self.assertEqual(cells[0].metadata['slide_helper'], 'slide_end')
74 self.assertEqual(cells[0].metadata['slide_helper'], 'slide_end')
75 assert 'slide_helper' in cells[2].metadata
75 assert 'slide_helper' in cells[2].metadata
76 self.assertEqual(cells[2].metadata['slide_helper'], 'slide_end')
76 self.assertEqual(cells[2].metadata['slide_helper'], 'slide_end')
77 assert 'slide_helper' in cells[3].metadata
77 assert 'slide_helper' in cells[3].metadata
78 self.assertEqual(cells[3].metadata['slide_helper'], 'subslide_end')
78 self.assertEqual(cells[3].metadata['slide_helper'], 'subslide_end')
@@ -1,74 +1,74 b''
1 """Tests for the svg2pdf preprocessor"""
1 """Tests for the svg2pdf preprocessor"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 from IPython.testing import decorators as dec
6 from IPython.testing import decorators as dec
7 from IPython.nbformat import current as nbformat
7 from IPython.nbformat import v4 as nbformat
8
8
9 from .base import PreprocessorTestsBase
9 from .base import PreprocessorTestsBase
10 from ..svg2pdf import SVG2PDFPreprocessor
10 from ..svg2pdf import SVG2PDFPreprocessor
11
11
12
12
13 class Testsvg2pdf(PreprocessorTestsBase):
13 class Testsvg2pdf(PreprocessorTestsBase):
14 """Contains test functions for svg2pdf.py"""
14 """Contains test functions for svg2pdf.py"""
15
15
16 simple_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
16 simple_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
17 <!-- Created with Inkscape (http://www.inkscape.org/) -->
17 <!-- Created with Inkscape (http://www.inkscape.org/) -->
18 <svg
18 <svg
19 xmlns:svg="http://www.w3.org/2000/svg"
19 xmlns:svg="http://www.w3.org/2000/svg"
20 xmlns="http://www.w3.org/2000/svg"
20 xmlns="http://www.w3.org/2000/svg"
21 version="1.0"
21 version="1.0"
22 x="0.00000000"
22 x="0.00000000"
23 y="0.00000000"
23 y="0.00000000"
24 width="500.00000"
24 width="500.00000"
25 height="500.00000"
25 height="500.00000"
26 id="svg2">
26 id="svg2">
27 <defs
27 <defs
28 id="defs4" />
28 id="defs4" />
29 <g
29 <g
30 id="layer1">
30 id="layer1">
31 <rect
31 <rect
32 width="300.00000"
32 width="300.00000"
33 height="300.00000"
33 height="300.00000"
34 x="100.00000"
34 x="100.00000"
35 y="100.00000"
35 y="100.00000"
36 style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:8.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000"
36 style="opacity:1.0000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:8.0000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-dashoffset:0.00000000;stroke-opacity:1.0000000"
37 id="rect5719" />
37 id="rect5719" />
38 </g>
38 </g>
39 </svg>"""
39 </svg>"""
40
40
41 def build_notebook(self):
41 def build_notebook(self):
42 """Build a reveal slides notebook in memory for use with tests.
42 """Build a reveal slides notebook in memory for use with tests.
43 Overrides base in PreprocessorTestsBase"""
43 Overrides base in PreprocessorTestsBase"""
44
44
45 outputs = [nbformat.new_output(output_type="image/svg+xml", output_svg=self.simple_svg)]
45 outputs = [nbformat.new_output(output_type="image/svg+xml", output_svg=self.simple_svg)]
46
46
47 slide_metadata = {'slideshow' : {'slide_type': 'slide'}}
47 slide_metadata = {'slideshow' : {'slide_type': 'slide'}}
48 subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}}
48 subslide_metadata = {'slideshow' : {'slide_type': 'subslide'}}
49
49
50 cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs)]
50 cells=[nbformat.new_code_cell(source="", execution_count=1, outputs=outputs)]
51
51
52 return nbformat.new_notebook(cells=cells)
52 return nbformat.new_notebook(cells=cells)
53
53
54
54
55 def build_preprocessor(self):
55 def build_preprocessor(self):
56 """Make an instance of a preprocessor"""
56 """Make an instance of a preprocessor"""
57 preprocessor = SVG2PDFPreprocessor()
57 preprocessor = SVG2PDFPreprocessor()
58 preprocessor.enabled = True
58 preprocessor.enabled = True
59 return preprocessor
59 return preprocessor
60
60
61
61
62 def test_constructor(self):
62 def test_constructor(self):
63 """Can a SVG2PDFPreprocessor be constructed?"""
63 """Can a SVG2PDFPreprocessor be constructed?"""
64 self.build_preprocessor()
64 self.build_preprocessor()
65
65
66
66
67 @dec.onlyif_cmds_exist('inkscape')
67 @dec.onlyif_cmds_exist('inkscape')
68 def test_output(self):
68 def test_output(self):
69 """Test the output of the SVG2PDFPreprocessor"""
69 """Test the output of the SVG2PDFPreprocessor"""
70 nb = self.build_notebook()
70 nb = self.build_notebook()
71 res = self.build_resources()
71 res = self.build_resources()
72 preprocessor = self.build_preprocessor()
72 preprocessor = self.build_preprocessor()
73 nb, res = preprocessor(nb, res)
73 nb, res = preprocessor(nb, res)
74 self.assertIn('application/pdf', nb.cells[0].outputs[0])
74 self.assertIn('application/pdf', nb.cells[0].outputs[0])
@@ -1,149 +1,148 b''
1 """Base test class for nbconvert"""
1 """Base test class for nbconvert"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import io
6 import io
7 import os
7 import os
8 import glob
8 import glob
9 import shutil
9 import shutil
10 import unittest
10 import unittest
11
11
12 import IPython
12 import IPython
13 from IPython.nbformat import current
13 from IPython.nbformat import v4, write
14 from IPython.utils.tempdir import TemporaryWorkingDirectory
14 from IPython.utils.tempdir import TemporaryWorkingDirectory
15 from IPython.utils.path import get_ipython_package_dir
15 from IPython.utils.path import get_ipython_package_dir
16 from IPython.utils.process import get_output_error_code
16 from IPython.utils.process import get_output_error_code
17 from IPython.testing.tools import get_ipython_cmd
17 from IPython.testing.tools import get_ipython_cmd
18
18
19 # a trailing space allows for simpler concatenation with the other arguments
19 # a trailing space allows for simpler concatenation with the other arguments
20 ipy_cmd = get_ipython_cmd(as_string=True) + " "
20 ipy_cmd = get_ipython_cmd(as_string=True) + " "
21
21
22
22
23 class TestsBase(unittest.TestCase):
23 class TestsBase(unittest.TestCase):
24 """Base tests class. Contains useful fuzzy comparison and nbconvert
24 """Base tests class. Contains useful fuzzy comparison and nbconvert
25 functions."""
25 functions."""
26
26
27
27
28 def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True,
28 def fuzzy_compare(self, a, b, newlines_are_spaces=True, tabs_are_spaces=True,
29 fuzzy_spacing=True, ignore_spaces=False,
29 fuzzy_spacing=True, ignore_spaces=False,
30 ignore_newlines=False, case_sensitive=False, leave_padding=False):
30 ignore_newlines=False, case_sensitive=False, leave_padding=False):
31 """
31 """
32 Performs a fuzzy comparison of two strings. A fuzzy comparison is a
32 Performs a fuzzy comparison of two strings. A fuzzy comparison is a
33 comparison that ignores insignificant differences in the two comparands.
33 comparison that ignores insignificant differences in the two comparands.
34 The significance of certain differences can be specified via the keyword
34 The significance of certain differences can be specified via the keyword
35 parameters of this method.
35 parameters of this method.
36 """
36 """
37
37
38 if not leave_padding:
38 if not leave_padding:
39 a = a.strip()
39 a = a.strip()
40 b = b.strip()
40 b = b.strip()
41
41
42 if ignore_newlines:
42 if ignore_newlines:
43 a = a.replace('\n', '')
43 a = a.replace('\n', '')
44 b = b.replace('\n', '')
44 b = b.replace('\n', '')
45
45
46 if newlines_are_spaces:
46 if newlines_are_spaces:
47 a = a.replace('\n', ' ')
47 a = a.replace('\n', ' ')
48 b = b.replace('\n', ' ')
48 b = b.replace('\n', ' ')
49
49
50 if tabs_are_spaces:
50 if tabs_are_spaces:
51 a = a.replace('\t', ' ')
51 a = a.replace('\t', ' ')
52 b = b.replace('\t', ' ')
52 b = b.replace('\t', ' ')
53
53
54 if ignore_spaces:
54 if ignore_spaces:
55 a = a.replace(' ', '')
55 a = a.replace(' ', '')
56 b = b.replace(' ', '')
56 b = b.replace(' ', '')
57
57
58 if fuzzy_spacing:
58 if fuzzy_spacing:
59 a = self.recursive_replace(a, ' ', ' ')
59 a = self.recursive_replace(a, ' ', ' ')
60 b = self.recursive_replace(b, ' ', ' ')
60 b = self.recursive_replace(b, ' ', ' ')
61
61
62 if not case_sensitive:
62 if not case_sensitive:
63 a = a.lower()
63 a = a.lower()
64 b = b.lower()
64 b = b.lower()
65
65
66 self.assertEqual(a, b)
66 self.assertEqual(a, b)
67
67
68
68
69 def recursive_replace(self, text, search, replacement):
69 def recursive_replace(self, text, search, replacement):
70 """
70 """
71 Performs a recursive replacement operation. Replaces all instances
71 Performs a recursive replacement operation. Replaces all instances
72 of a search string in a text string with a replacement string until
72 of a search string in a text string with a replacement string until
73 the search string no longer exists. Recursion is needed because the
73 the search string no longer exists. Recursion is needed because the
74 replacement string may generate additional search strings.
74 replacement string may generate additional search strings.
75
75
76 For example:
76 For example:
77 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
77 Replace "ii" with "i" in the string "Hiiii" yields "Hii"
78 Another replacement cds "Hi" (the desired output)
78 Another replacement cds "Hi" (the desired output)
79
79
80 Parameters
80 Parameters
81 ----------
81 ----------
82 text : string
82 text : string
83 Text to replace in.
83 Text to replace in.
84 search : string
84 search : string
85 String to search for within "text"
85 String to search for within "text"
86 replacement : string
86 replacement : string
87 String to replace "search" with
87 String to replace "search" with
88 """
88 """
89 while search in text:
89 while search in text:
90 text = text.replace(search, replacement)
90 text = text.replace(search, replacement)
91 return text
91 return text
92
92
93 def create_temp_cwd(self, copy_filenames=None):
93 def create_temp_cwd(self, copy_filenames=None):
94 temp_dir = TemporaryWorkingDirectory()
94 temp_dir = TemporaryWorkingDirectory()
95
95
96 #Copy the files if requested.
96 #Copy the files if requested.
97 if copy_filenames is not None:
97 if copy_filenames is not None:
98 self.copy_files_to(copy_filenames, dest=temp_dir.name)
98 self.copy_files_to(copy_filenames, dest=temp_dir.name)
99
99
100 #Return directory handler
100 #Return directory handler
101 return temp_dir
101 return temp_dir
102
102
103 def create_empty_notebook(self, path):
103 def create_empty_notebook(self, path):
104 nb = current.new_notebook()
104 nb = v4.new_notebook()
105 with io.open(path, 'w', encoding='utf-8') as f:
105 with io.open(path, 'w', encoding='utf-8') as f:
106 current.write(nb, f, 'json')
106 write(f, nb, 4)
107
108
107
109 def copy_files_to(self, copy_filenames, dest='.'):
108 def copy_files_to(self, copy_filenames, dest='.'):
110 "Copy test files into the destination directory"
109 "Copy test files into the destination directory"
111 if not os.path.isdir(dest):
110 if not os.path.isdir(dest):
112 os.makedirs(dest)
111 os.makedirs(dest)
113 files_path = self._get_files_path()
112 files_path = self._get_files_path()
114 for pattern in copy_filenames:
113 for pattern in copy_filenames:
115 for match in glob.glob(os.path.join(files_path, pattern)):
114 for match in glob.glob(os.path.join(files_path, pattern)):
116 shutil.copyfile(match, os.path.join(dest, os.path.basename(match)))
115 shutil.copyfile(match, os.path.join(dest, os.path.basename(match)))
117
116
118
117
119 def _get_files_path(self):
118 def _get_files_path(self):
120
119
121 #Get the relative path to this module in the IPython directory.
120 #Get the relative path to this module in the IPython directory.
122 names = self.__module__.split('.')[1:-1]
121 names = self.__module__.split('.')[1:-1]
123 names.append('files')
122 names.append('files')
124
123
125 #Build a path using the IPython directory and the relative path we just
124 #Build a path using the IPython directory and the relative path we just
126 #found.
125 #found.
127 path = get_ipython_package_dir()
126 path = get_ipython_package_dir()
128 for name in names:
127 for name in names:
129 path = os.path.join(path, name)
128 path = os.path.join(path, name)
130 return path
129 return path
131
130
132
131
133 def call(self, parameters, ignore_return_code=False):
132 def call(self, parameters, ignore_return_code=False):
134 """
133 """
135 Execute a, IPython shell command, listening for both Errors and non-zero
134 Execute a, IPython shell command, listening for both Errors and non-zero
136 return codes.
135 return codes.
137
136
138 Parameters
137 Parameters
139 ----------
138 ----------
140 parameters : str
139 parameters : str
141 List of parameters to pass to IPython.
140 List of parameters to pass to IPython.
142 ignore_return_code : optional bool (default False)
141 ignore_return_code : optional bool (default False)
143 Throw an OSError if the return code
142 Throw an OSError if the return code
144 """
143 """
145
144
146 stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters)
145 stdout, stderr, retcode = get_output_error_code(ipy_cmd + parameters)
147 if not (retcode == 0 or ignore_return_code):
146 if not (retcode == 0 or ignore_return_code):
148 raise OSError(stderr)
147 raise OSError(stderr)
149 return stdout, stderr
148 return stdout, stderr
General Comments 0
You need to be logged in to leave comments. Login now