##// END OF EJS Templates
Make the directory name be the default relpath
Jessica B. Hamrick -
Show More
@@ -1,279 +1,280 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, TraitError
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):
27 class FilenameExtension(Unicode):
28 """A trait for filename extensions."""
28 """A trait for filename extensions."""
29
29
30 default_value = u''
30 default_value = u''
31 info_text = 'a filename extension, beginning with a dot'
31 info_text = 'a filename extension, beginning with a dot'
32
32
33 def validate(self, obj, value):
33 def validate(self, obj, value):
34 # cast to proper unicode
34 # cast to proper unicode
35 value = super(FilenameExtension, self).validate(obj, value)
35 value = super(FilenameExtension, self).validate(obj, value)
36
36
37 # check that it starts with a dot
37 # check that it starts with a dot
38 if value and not value.startswith('.'):
38 if value and not value.startswith('.'):
39 msg = "FileExtension trait '{}' does not begin with a dot: {!r}"
39 msg = "FileExtension trait '{}' does not begin with a dot: {!r}"
40 raise TraitError(msg.format(self.name, value))
40 raise TraitError(msg.format(self.name, value))
41
41
42 return value
42 return value
43
43
44
44
45 class Exporter(LoggingConfigurable):
45 class Exporter(LoggingConfigurable):
46 """
46 """
47 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
48 NotebookNode object and then return the modified NotebookNode object and
48 NotebookNode object and then return the modified NotebookNode object and
49 accompanying resources dict.
49 accompanying resources dict.
50 """
50 """
51
51
52 file_extension = FilenameExtension(
52 file_extension = FilenameExtension(
53 '.txt', config=True,
53 '.txt', config=True,
54 help="Extension of the file that should be written to disk"
54 help="Extension of the file that should be written to disk"
55 )
55 )
56
56
57 # MIME type of the result file, for HTTP response headers.
57 # MIME type of the result file, for HTTP response headers.
58 # 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
59 # the class, not just on instances.
59 # the class, not just on instances.
60 output_mimetype = ''
60 output_mimetype = ''
61
61
62 #Configurability, allows the user to easily add filters and preprocessors.
62 #Configurability, allows the user to easily add filters and preprocessors.
63 preprocessors = List(config=True,
63 preprocessors = List(config=True,
64 help="""List of preprocessors, by name or namespace, to enable.""")
64 help="""List of preprocessors, by name or namespace, to enable.""")
65
65
66 _preprocessors = List()
66 _preprocessors = List()
67
67
68 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
68 default_preprocessors = List(['IPython.nbconvert.preprocessors.coalesce_streams',
69 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
69 'IPython.nbconvert.preprocessors.SVG2PDFPreprocessor',
70 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
70 'IPython.nbconvert.preprocessors.ExtractOutputPreprocessor',
71 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
71 'IPython.nbconvert.preprocessors.CSSHTMLHeaderPreprocessor',
72 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
72 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
73 'IPython.nbconvert.preprocessors.LatexPreprocessor',
73 'IPython.nbconvert.preprocessors.LatexPreprocessor',
74 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
74 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
75 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
75 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
76 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
77 config=True,
77 config=True,
78 help="""List of preprocessors available by default, by name, namespace,
78 help="""List of preprocessors available by default, by name, namespace,
79 instance, or type.""")
79 instance, or type.""")
80
80
81
81
82 def __init__(self, config=None, **kw):
82 def __init__(self, config=None, **kw):
83 """
83 """
84 Public constructor
84 Public constructor
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 config : config
88 config : config
89 User configuration instance.
89 User configuration instance.
90 """
90 """
91 with_default_config = self.default_config
91 with_default_config = self.default_config
92 if config:
92 if config:
93 with_default_config.merge(config)
93 with_default_config.merge(config)
94
94
95 super(Exporter, self).__init__(config=with_default_config, **kw)
95 super(Exporter, self).__init__(config=with_default_config, **kw)
96
96
97 self._init_preprocessors()
97 self._init_preprocessors()
98
98
99
99
100 @property
100 @property
101 def default_config(self):
101 def default_config(self):
102 return Config()
102 return Config()
103
103
104 def from_notebook_node(self, nb, resources=None, **kw):
104 def from_notebook_node(self, nb, resources=None, **kw):
105 """
105 """
106 Convert a notebook from a notebook node instance.
106 Convert a notebook from a notebook node instance.
107
107
108 Parameters
108 Parameters
109 ----------
109 ----------
110 nb : :class:`~IPython.nbformat.NotebookNode`
110 nb : :class:`~IPython.nbformat.NotebookNode`
111 Notebook node (dict-like with attr-access)
111 Notebook node (dict-like with attr-access)
112 resources : dict
112 resources : dict
113 Additional resources that can be accessed read/write by
113 Additional resources that can be accessed read/write by
114 preprocessors and filters.
114 preprocessors and filters.
115 **kw
115 **kw
116 Ignored (?)
116 Ignored (?)
117 """
117 """
118 nb_copy = copy.deepcopy(nb)
118 nb_copy = copy.deepcopy(nb)
119 resources = self._init_resources(resources)
119 resources = self._init_resources(resources)
120
120
121 if 'language' in nb['metadata']:
121 if 'language' in nb['metadata']:
122 resources['language'] = nb['metadata']['language'].lower()
122 resources['language'] = nb['metadata']['language'].lower()
123
123
124 # Preprocess
124 # Preprocess
125 nb_copy, resources = self._preprocess(nb_copy, resources)
125 nb_copy, resources = self._preprocess(nb_copy, resources)
126
126
127 return nb_copy, resources
127 return nb_copy, resources
128
128
129
129
130 def from_filename(self, filename, resources=None, **kw):
130 def from_filename(self, filename, resources=None, **kw):
131 """
131 """
132 Convert a notebook from a notebook file.
132 Convert a notebook from a notebook file.
133
133
134 Parameters
134 Parameters
135 ----------
135 ----------
136 filename : str
136 filename : str
137 Full filename of the notebook file to open and convert.
137 Full filename of the notebook file to open and convert.
138 """
138 """
139
139
140 # Pull the metadata from the filesystem.
140 # Pull the metadata from the filesystem.
141 if resources is None:
141 if resources is None:
142 resources = ResourcesDict()
142 resources = ResourcesDict()
143 if not 'metadata' in resources or resources['metadata'] == '':
143 if not 'metadata' in resources or resources['metadata'] == '':
144 resources['metadata'] = ResourcesDict()
144 resources['metadata'] = ResourcesDict()
145 basename = os.path.basename(filename)
145 path, basename = os.path.split(filename)
146 notebook_name = basename[:basename.rfind('.')]
146 notebook_name = basename[:basename.rfind('.')]
147 resources['metadata']['name'] = notebook_name
147 resources['metadata']['name'] = notebook_name
148 resources['metadata']['path'] = path
148
149
149 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
150 modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(filename))
150 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
151 resources['metadata']['modified_date'] = modified_date.strftime(text.date_format)
151
152
152 with io.open(filename, encoding='utf-8') as f:
153 with io.open(filename, encoding='utf-8') as f:
153 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
154 return self.from_notebook_node(nbformat.read(f, as_version=4), resources=resources, **kw)
154
155
155
156
156 def from_file(self, file_stream, resources=None, **kw):
157 def from_file(self, file_stream, resources=None, **kw):
157 """
158 """
158 Convert a notebook from a notebook file.
159 Convert a notebook from a notebook file.
159
160
160 Parameters
161 Parameters
161 ----------
162 ----------
162 file_stream : file-like object
163 file_stream : file-like object
163 Notebook file-like object to convert.
164 Notebook file-like object to convert.
164 """
165 """
165 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
166 return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
166
167
167
168
168 def register_preprocessor(self, preprocessor, enabled=False):
169 def register_preprocessor(self, preprocessor, enabled=False):
169 """
170 """
170 Register a preprocessor.
171 Register a preprocessor.
171 Preprocessors are classes that act upon the notebook before it is
172 Preprocessors are classes that act upon the notebook before it is
172 passed into the Jinja templating engine. preprocessors are also
173 passed into the Jinja templating engine. preprocessors are also
173 capable of passing additional information to the Jinja
174 capable of passing additional information to the Jinja
174 templating engine.
175 templating engine.
175
176
176 Parameters
177 Parameters
177 ----------
178 ----------
178 preprocessor : preprocessor
179 preprocessor : preprocessor
179 """
180 """
180 if preprocessor is None:
181 if preprocessor is None:
181 raise TypeError('preprocessor')
182 raise TypeError('preprocessor')
182 isclass = isinstance(preprocessor, type)
183 isclass = isinstance(preprocessor, type)
183 constructed = not isclass
184 constructed = not isclass
184
185
185 # Handle preprocessor's registration based on it's type
186 # Handle preprocessor's registration based on it's type
186 if constructed and isinstance(preprocessor, py3compat.string_types):
187 if constructed and isinstance(preprocessor, py3compat.string_types):
187 # Preprocessor is a string, import the namespace and recursively call
188 # Preprocessor is a string, import the namespace and recursively call
188 # this register_preprocessor method
189 # this register_preprocessor method
189 preprocessor_cls = import_item(preprocessor)
190 preprocessor_cls = import_item(preprocessor)
190 return self.register_preprocessor(preprocessor_cls, enabled)
191 return self.register_preprocessor(preprocessor_cls, enabled)
191
192
192 if constructed and hasattr(preprocessor, '__call__'):
193 if constructed and hasattr(preprocessor, '__call__'):
193 # Preprocessor is a function, no need to construct it.
194 # Preprocessor is a function, no need to construct it.
194 # Register and return the preprocessor.
195 # Register and return the preprocessor.
195 if enabled:
196 if enabled:
196 preprocessor.enabled = True
197 preprocessor.enabled = True
197 self._preprocessors.append(preprocessor)
198 self._preprocessors.append(preprocessor)
198 return preprocessor
199 return preprocessor
199
200
200 elif isclass and isinstance(preprocessor, MetaHasTraits):
201 elif isclass and isinstance(preprocessor, MetaHasTraits):
201 # Preprocessor is configurable. Make sure to pass in new default for
202 # Preprocessor is configurable. Make sure to pass in new default for
202 # the enabled flag if one was specified.
203 # the enabled flag if one was specified.
203 self.register_preprocessor(preprocessor(parent=self), enabled)
204 self.register_preprocessor(preprocessor(parent=self), enabled)
204
205
205 elif isclass:
206 elif isclass:
206 # Preprocessor is not configurable, construct it
207 # Preprocessor is not configurable, construct it
207 self.register_preprocessor(preprocessor(), enabled)
208 self.register_preprocessor(preprocessor(), enabled)
208
209
209 else:
210 else:
210 # Preprocessor is an instance of something without a __call__
211 # Preprocessor is an instance of something without a __call__
211 # attribute.
212 # attribute.
212 raise TypeError('preprocessor')
213 raise TypeError('preprocessor')
213
214
214
215
215 def _init_preprocessors(self):
216 def _init_preprocessors(self):
216 """
217 """
217 Register all of the preprocessors needed for this exporter, disabled
218 Register all of the preprocessors needed for this exporter, disabled
218 unless specified explicitly.
219 unless specified explicitly.
219 """
220 """
220 self._preprocessors = []
221 self._preprocessors = []
221
222
222 # Load default preprocessors (not necessarly enabled by default).
223 # Load default preprocessors (not necessarly enabled by default).
223 for preprocessor in self.default_preprocessors:
224 for preprocessor in self.default_preprocessors:
224 self.register_preprocessor(preprocessor)
225 self.register_preprocessor(preprocessor)
225
226
226 # Load user-specified preprocessors. Enable by default.
227 # Load user-specified preprocessors. Enable by default.
227 for preprocessor in self.preprocessors:
228 for preprocessor in self.preprocessors:
228 self.register_preprocessor(preprocessor, enabled=True)
229 self.register_preprocessor(preprocessor, enabled=True)
229
230
230
231
231 def _init_resources(self, resources):
232 def _init_resources(self, resources):
232
233
233 #Make sure the resources dict is of ResourcesDict type.
234 #Make sure the resources dict is of ResourcesDict type.
234 if resources is None:
235 if resources is None:
235 resources = ResourcesDict()
236 resources = ResourcesDict()
236 if not isinstance(resources, ResourcesDict):
237 if not isinstance(resources, ResourcesDict):
237 new_resources = ResourcesDict()
238 new_resources = ResourcesDict()
238 new_resources.update(resources)
239 new_resources.update(resources)
239 resources = new_resources
240 resources = new_resources
240
241
241 #Make sure the metadata extension exists in resources
242 #Make sure the metadata extension exists in resources
242 if 'metadata' in resources:
243 if 'metadata' in resources:
243 if not isinstance(resources['metadata'], ResourcesDict):
244 if not isinstance(resources['metadata'], ResourcesDict):
244 new_metadata = ResourcesDict()
245 new_metadata = ResourcesDict()
245 new_metadata.update(resources['metadata'])
246 new_metadata.update(resources['metadata'])
246 resources['metadata'] = new_metadata
247 resources['metadata'] = new_metadata
247 else:
248 else:
248 resources['metadata'] = ResourcesDict()
249 resources['metadata'] = ResourcesDict()
249 if not resources['metadata']['name']:
250 if not resources['metadata']['name']:
250 resources['metadata']['name'] = 'Notebook'
251 resources['metadata']['name'] = 'Notebook'
251
252
252 #Set the output extension
253 #Set the output extension
253 resources['output_extension'] = self.file_extension
254 resources['output_extension'] = self.file_extension
254 return resources
255 return resources
255
256
256
257
257 def _preprocess(self, nb, resources):
258 def _preprocess(self, nb, resources):
258 """
259 """
259 Preprocess the notebook before passing it into the Jinja engine.
260 Preprocess the notebook before passing it into the Jinja engine.
260 To preprocess the notebook is to apply all of the
261 To preprocess the notebook is to apply all of the
261
262
262 Parameters
263 Parameters
263 ----------
264 ----------
264 nb : notebook node
265 nb : notebook node
265 notebook that is being exported.
266 notebook that is being exported.
266 resources : a dict of additional resources that
267 resources : a dict of additional resources that
267 can be accessed read/write by preprocessors
268 can be accessed read/write by preprocessors
268 """
269 """
269
270
270 # Do a copy.deepcopy first,
271 # Do a copy.deepcopy first,
271 # we are never safe enough with what the preprocessors could do.
272 # we are never safe enough with what the preprocessors could do.
272 nbc = copy.deepcopy(nb)
273 nbc = copy.deepcopy(nb)
273 resc = copy.deepcopy(resources)
274 resc = copy.deepcopy(resources)
274
275
275 #Run each preprocessor on the notebook. Carry the output along
276 #Run each preprocessor on the notebook. Carry the output along
276 #to each preprocessor
277 #to each preprocessor
277 for preprocessor in self._preprocessors:
278 for preprocessor in self._preprocessors:
278 nbc, resc = preprocessor(nbc, resc)
279 nbc, resc = preprocessor(nbc, resc)
279 return nbc, resc
280 return nbc, resc
@@ -1,123 +1,131 b''
1 """Contains writer for writing nbconvert output to filesystem."""
1 """Contains writer for writing nbconvert output to filesystem."""
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
9
10 from IPython.utils.traitlets import Unicode
10 from IPython.utils.traitlets import Unicode
11 from IPython.utils.path import link_or_copy, ensure_dir_exists
11 from IPython.utils.path import link_or_copy, ensure_dir_exists
12 from IPython.utils.py3compat import unicode_type
12 from IPython.utils.py3compat import unicode_type
13
13
14 from .base import WriterBase
14 from .base import WriterBase
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Classes
17 # Classes
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 class FilesWriter(WriterBase):
20 class FilesWriter(WriterBase):
21 """Consumes nbconvert output and produces files."""
21 """Consumes nbconvert output and produces files."""
22
22
23
23
24 build_directory = Unicode("", config=True,
24 build_directory = Unicode("", config=True,
25 help="""Directory to write output to. Leave blank
25 help="""Directory to write output to. Leave blank
26 to output to the current directory""")
26 to output to the current directory""")
27
27
28 relpath = Unicode(
28 relpath = Unicode(
29 "", config=True,
29 "", config=True,
30 help="""When copying files that the notebook depends on, copy them in
30 help="""When copying files that the notebook depends on, copy them in
31 relation to this path, such that the destination filename will be
31 relation to this path, such that the destination filename will be
32 os.path.relpath(filename, relpath).""")
32 os.path.relpath(filename, relpath). If FilesWriter is operating on a
33 notebook that already exists elsewhere on disk, then the default will be
34 the directory containing that notebook.""")
33
35
34
36
35 # Make sure that the output directory exists.
37 # Make sure that the output directory exists.
36 def _build_directory_changed(self, name, old, new):
38 def _build_directory_changed(self, name, old, new):
37 if new:
39 if new:
38 ensure_dir_exists(new)
40 ensure_dir_exists(new)
39
41
40
42
41 def __init__(self, **kw):
43 def __init__(self, **kw):
42 super(FilesWriter, self).__init__(**kw)
44 super(FilesWriter, self).__init__(**kw)
43 self._build_directory_changed('build_directory', self.build_directory,
45 self._build_directory_changed('build_directory', self.build_directory,
44 self.build_directory)
46 self.build_directory)
45
47
46 def _makedir(self, path):
48 def _makedir(self, path):
47 """Make a directory if it doesn't already exist"""
49 """Make a directory if it doesn't already exist"""
48 if path:
50 if path:
49 self.log.info("Making directory %s", path)
51 self.log.info("Making directory %s", path)
50 ensure_dir_exists(path)
52 ensure_dir_exists(path)
51
53
52 def write(self, output, resources, notebook_name=None, **kw):
54 def write(self, output, resources, notebook_name=None, **kw):
53 """
55 """
54 Consume and write Jinja output to the file system. Output directory
56 Consume and write Jinja output to the file system. Output directory
55 is set via the 'build_directory' variable of this instance (a
57 is set via the 'build_directory' variable of this instance (a
56 configurable).
58 configurable).
57
59
58 See base for more...
60 See base for more...
59 """
61 """
60
62
61 # Verify that a notebook name is provided.
63 # Verify that a notebook name is provided.
62 if notebook_name is None:
64 if notebook_name is None:
63 raise TypeError('notebook_name')
65 raise TypeError('notebook_name')
64
66
65 # Pull the extension and subdir from the resources dict.
67 # Pull the extension and subdir from the resources dict.
66 output_extension = resources.get('output_extension', None)
68 output_extension = resources.get('output_extension', None)
67
69
70 # Get the relative path for copying files
71 if self.relpath == '':
72 relpath = resources.get('metadata', {}).get('path', '')
73 else:
74 relpath = self.relpath
75
68 # Write all of the extracted resources to the destination directory.
76 # Write all of the extracted resources to the destination directory.
69 # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
77 # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
70 # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
78 # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
71 for filename, data in resources.get('outputs', {}).items():
79 for filename, data in resources.get('outputs', {}).items():
72
80
73 # Determine where to write the file to
81 # Determine where to write the file to
74 dest = os.path.join(self.build_directory, filename)
82 dest = os.path.join(self.build_directory, filename)
75 path = os.path.dirname(dest)
83 path = os.path.dirname(dest)
76 self._makedir(path)
84 self._makedir(path)
77
85
78 # Write file
86 # Write file
79 self.log.debug("Writing %i bytes to support file %s", len(data), dest)
87 self.log.debug("Writing %i bytes to support file %s", len(data), dest)
80 with io.open(dest, 'wb') as f:
88 with io.open(dest, 'wb') as f:
81 f.write(data)
89 f.write(data)
82
90
83 # Copy referenced files to output directory
91 # Copy referenced files to output directory
84 if self.build_directory:
92 if self.build_directory:
85 for filename in self.files:
93 for filename in self.files:
86
94
87 # Copy files that match search pattern
95 # Copy files that match search pattern
88 for matching_filename in glob.glob(filename):
96 for matching_filename in glob.glob(filename):
89
97
90 # compute the relative path for the filename
98 # compute the relative path for the filename
91 if self.relpath:
99 if relpath != '':
92 dest_filename = os.path.relpath(matching_filename, self.relpath)
100 dest_filename = os.path.relpath(matching_filename, relpath)
93 else:
101 else:
94 dest_filename = matching_filename
102 dest_filename = matching_filename
95
103
96 # Make sure folder exists.
104 # Make sure folder exists.
97 dest = os.path.join(self.build_directory, dest_filename)
105 dest = os.path.join(self.build_directory, dest_filename)
98 path = os.path.dirname(dest)
106 path = os.path.dirname(dest)
99 self._makedir(path)
107 self._makedir(path)
100
108
101 # Copy if destination is different.
109 # Copy if destination is different.
102 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
110 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
103 self.log.info("Linking %s -> %s", matching_filename, dest)
111 self.log.info("Linking %s -> %s", matching_filename, dest)
104 link_or_copy(matching_filename, dest)
112 link_or_copy(matching_filename, dest)
105
113
106 # Determine where to write conversion results.
114 # Determine where to write conversion results.
107 if output_extension is not None:
115 if output_extension is not None:
108 dest = notebook_name + output_extension
116 dest = notebook_name + output_extension
109 else:
117 else:
110 dest = notebook_name
118 dest = notebook_name
111 if self.build_directory:
119 if self.build_directory:
112 dest = os.path.join(self.build_directory, dest)
120 dest = os.path.join(self.build_directory, dest)
113
121
114 # Write conversion results.
122 # Write conversion results.
115 self.log.info("Writing %i bytes to %s", len(output), dest)
123 self.log.info("Writing %i bytes to %s", len(output), dest)
116 if isinstance(output, unicode_type):
124 if isinstance(output, unicode_type):
117 with io.open(dest, 'w', encoding='utf-8') as f:
125 with io.open(dest, 'w', encoding='utf-8') as f:
118 f.write(output)
126 f.write(output)
119 else:
127 else:
120 with io.open(dest, 'wb') as f:
128 with io.open(dest, 'wb') as f:
121 f.write(output)
129 f.write(output)
122
130
123 return dest
131 return dest
@@ -1,238 +1,307 b''
1 """
1 """
2 Module with tests for files
2 Module with tests for files
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import sys
17 import sys
18 import os
18 import os
19
19
20 from ...tests.base import TestsBase
20 from ...tests.base import TestsBase
21 from ..files import FilesWriter
21 from ..files import FilesWriter
22 from IPython.utils.py3compat import PY3
22 from IPython.utils.py3compat import PY3
23
23
24 if PY3:
24 if PY3:
25 from io import StringIO
25 from io import StringIO
26 else:
26 else:
27 from StringIO import StringIO
27 from StringIO import StringIO
28
28
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Class
31 # Class
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 class Testfiles(TestsBase):
34 class Testfiles(TestsBase):
35 """Contains test functions for files.py"""
35 """Contains test functions for files.py"""
36
36
37 def test_basic_output(self):
37 def test_basic_output(self):
38 """Is FilesWriter basic output correct?"""
38 """Is FilesWriter basic output correct?"""
39
39
40 # Work in a temporary directory.
40 # Work in a temporary directory.
41 with self.create_temp_cwd():
41 with self.create_temp_cwd():
42
42
43 # Create the resoruces dictionary
43 # Create the resoruces dictionary
44 res = {}
44 res = {}
45
45
46 # Create files writer, test output
46 # Create files writer, test output
47 writer = FilesWriter()
47 writer = FilesWriter()
48 writer.write(u'y', res, notebook_name="z")
48 writer.write(u'y', res, notebook_name="z")
49
49
50 # Check the output of the file
50 # Check the output of the file
51 with open('z', 'r') as f:
51 with open('z', 'r') as f:
52 output = f.read()
52 output = f.read()
53 self.assertEqual(output, u'y')
53 self.assertEqual(output, u'y')
54
54
55 def test_ext(self):
55 def test_ext(self):
56 """Does the FilesWriter add the correct extension to the output?"""
56 """Does the FilesWriter add the correct extension to the output?"""
57
57
58 # Work in a temporary directory.
58 # Work in a temporary directory.
59 with self.create_temp_cwd():
59 with self.create_temp_cwd():
60
60
61 # Create the resoruces dictionary
61 # Create the resoruces dictionary
62 res = {'output_extension': '.txt'}
62 res = {'output_extension': '.txt'}
63
63
64 # Create files writer, test output
64 # Create files writer, test output
65 writer = FilesWriter()
65 writer = FilesWriter()
66 writer.write(u'y', res, notebook_name="z")
66 writer.write(u'y', res, notebook_name="z")
67
67
68 # Check the output of the file
68 # Check the output of the file
69 assert os.path.isfile('z.txt')
69 assert os.path.isfile('z.txt')
70 with open('z.txt', 'r') as f:
70 with open('z.txt', 'r') as f:
71 output = f.read()
71 output = f.read()
72 self.assertEqual(output, u'y')
72 self.assertEqual(output, u'y')
73
73
74
74
75 def test_extract(self):
75 def test_extract(self):
76 """Can FilesWriter write extracted figures correctly?"""
76 """Can FilesWriter write extracted figures correctly?"""
77
77
78 # Work in a temporary directory.
78 # Work in a temporary directory.
79 with self.create_temp_cwd():
79 with self.create_temp_cwd():
80
80
81 # Create the resoruces dictionary
81 # Create the resoruces dictionary
82 res = {'outputs': {os.path.join('z_files', 'a'): b'b'}}
82 res = {'outputs': {os.path.join('z_files', 'a'): b'b'}}
83
83
84 # Create files writer, test output
84 # Create files writer, test output
85 writer = FilesWriter()
85 writer = FilesWriter()
86 writer.write(u'y', res, notebook_name="z")
86 writer.write(u'y', res, notebook_name="z")
87
87
88 # Check the output of the file
88 # Check the output of the file
89 with open('z', 'r') as f:
89 with open('z', 'r') as f:
90 output = f.read()
90 output = f.read()
91 self.assertEqual(output, u'y')
91 self.assertEqual(output, u'y')
92
92
93 # Check the output of the extracted file
93 # Check the output of the extracted file
94 extracted_file_dest = os.path.join('z_files', 'a')
94 extracted_file_dest = os.path.join('z_files', 'a')
95 assert os.path.isfile(extracted_file_dest)
95 assert os.path.isfile(extracted_file_dest)
96 with open(extracted_file_dest, 'r') as f:
96 with open(extracted_file_dest, 'r') as f:
97 output = f.read()
97 output = f.read()
98 self.assertEqual(output, 'b')
98 self.assertEqual(output, 'b')
99
99
100
100
101 def test_builddir(self):
101 def test_builddir(self):
102 """Can FilesWriter write to a build dir correctly?"""
102 """Can FilesWriter write to a build dir correctly?"""
103
103
104 # Work in a temporary directory.
104 # Work in a temporary directory.
105 with self.create_temp_cwd():
105 with self.create_temp_cwd():
106
106
107 # Create the resoruces dictionary
107 # Create the resoruces dictionary
108 res = {'outputs': {os.path.join('z_files', 'a'): b'b'}}
108 res = {'outputs': {os.path.join('z_files', 'a'): b'b'}}
109
109
110 # Create files writer, test output
110 # Create files writer, test output
111 writer = FilesWriter()
111 writer = FilesWriter()
112 writer.build_directory = u'build'
112 writer.build_directory = u'build'
113 writer.write(u'y', res, notebook_name="z")
113 writer.write(u'y', res, notebook_name="z")
114
114
115 # Check the output of the file
115 # Check the output of the file
116 assert os.path.isdir(writer.build_directory)
116 assert os.path.isdir(writer.build_directory)
117 dest = os.path.join(writer.build_directory, 'z')
117 dest = os.path.join(writer.build_directory, 'z')
118 with open(dest, 'r') as f:
118 with open(dest, 'r') as f:
119 output = f.read()
119 output = f.read()
120 self.assertEqual(output, u'y')
120 self.assertEqual(output, u'y')
121
121
122 # Check the output of the extracted file
122 # Check the output of the extracted file
123 extracted_file_dest = os.path.join(writer.build_directory, 'z_files', 'a')
123 extracted_file_dest = os.path.join(writer.build_directory, 'z_files', 'a')
124 assert os.path.isfile(extracted_file_dest)
124 assert os.path.isfile(extracted_file_dest)
125 with open(extracted_file_dest, 'r') as f:
125 with open(extracted_file_dest, 'r') as f:
126 output = f.read()
126 output = f.read()
127 self.assertEqual(output, 'b')
127 self.assertEqual(output, 'b')
128
128
129
129
130 def test_links(self):
130 def test_links(self):
131 """Can the FilesWriter handle linked files correctly?"""
131 """Can the FilesWriter handle linked files correctly?"""
132
132
133 # Work in a temporary directory.
133 # Work in a temporary directory.
134 with self.create_temp_cwd():
134 with self.create_temp_cwd():
135
135
136 # Create test file
136 # Create test file
137 os.mkdir('sub')
137 os.mkdir('sub')
138 with open(os.path.join('sub', 'c'), 'w') as f:
138 with open(os.path.join('sub', 'c'), 'w') as f:
139 f.write('d')
139 f.write('d')
140
140
141 # Create the resoruces dictionary
141 # Create the resoruces dictionary
142 res = {}
142 res = {}
143
143
144 # Create files writer, test output
144 # Create files writer, test output
145 writer = FilesWriter()
145 writer = FilesWriter()
146 writer.files = [os.path.join('sub', 'c')]
146 writer.files = [os.path.join('sub', 'c')]
147 writer.build_directory = u'build'
147 writer.build_directory = u'build'
148 writer.write(u'y', res, notebook_name="z")
148 writer.write(u'y', res, notebook_name="z")
149
149
150 # Check the output of the file
150 # Check the output of the file
151 assert os.path.isdir(writer.build_directory)
151 assert os.path.isdir(writer.build_directory)
152 dest = os.path.join(writer.build_directory, 'z')
152 dest = os.path.join(writer.build_directory, 'z')
153 with open(dest, 'r') as f:
153 with open(dest, 'r') as f:
154 output = f.read()
154 output = f.read()
155 self.assertEqual(output, u'y')
155 self.assertEqual(output, u'y')
156
156
157 # Check to make sure the linked file was copied
157 # Check to make sure the linked file was copied
158 path = os.path.join(writer.build_directory, 'sub')
158 path = os.path.join(writer.build_directory, 'sub')
159 assert os.path.isdir(path)
159 assert os.path.isdir(path)
160 dest = os.path.join(path, 'c')
160 dest = os.path.join(path, 'c')
161 assert os.path.isfile(dest)
161 assert os.path.isfile(dest)
162 with open(dest, 'r') as f:
162 with open(dest, 'r') as f:
163 output = f.read()
163 output = f.read()
164 self.assertEqual(output, 'd')
164 self.assertEqual(output, 'd')
165
165
166 def test_glob(self):
166 def test_glob(self):
167 """Can the FilesWriter handle globbed files correctly?"""
167 """Can the FilesWriter handle globbed files correctly?"""
168
168
169 # Work in a temporary directory.
169 # Work in a temporary directory.
170 with self.create_temp_cwd():
170 with self.create_temp_cwd():
171
171
172 # Create test files
172 # Create test files
173 os.mkdir('sub')
173 os.mkdir('sub')
174 with open(os.path.join('sub', 'c'), 'w') as f:
174 with open(os.path.join('sub', 'c'), 'w') as f:
175 f.write('e')
175 f.write('e')
176 with open(os.path.join('sub', 'd'), 'w') as f:
176 with open(os.path.join('sub', 'd'), 'w') as f:
177 f.write('e')
177 f.write('e')
178
178
179 # Create the resoruces dictionary
179 # Create the resoruces dictionary
180 res = {}
180 res = {}
181
181
182 # Create files writer, test output
182 # Create files writer, test output
183 writer = FilesWriter()
183 writer = FilesWriter()
184 writer.files = ['sub/*']
184 writer.files = ['sub/*']
185 writer.build_directory = u'build'
185 writer.build_directory = u'build'
186 writer.write(u'y', res, notebook_name="z")
186 writer.write(u'y', res, notebook_name="z")
187
187
188 # Check the output of the file
188 # Check the output of the file
189 assert os.path.isdir(writer.build_directory)
189 assert os.path.isdir(writer.build_directory)
190 dest = os.path.join(writer.build_directory, 'z')
190 dest = os.path.join(writer.build_directory, 'z')
191 with open(dest, 'r') as f:
191 with open(dest, 'r') as f:
192 output = f.read()
192 output = f.read()
193 self.assertEqual(output, u'y')
193 self.assertEqual(output, u'y')
194
194
195 # Check to make sure the globbed files were copied
195 # Check to make sure the globbed files were copied
196 path = os.path.join(writer.build_directory, 'sub')
196 path = os.path.join(writer.build_directory, 'sub')
197 assert os.path.isdir(path)
197 assert os.path.isdir(path)
198 for filename in ['c', 'd']:
198 for filename in ['c', 'd']:
199 dest = os.path.join(path, filename)
199 dest = os.path.join(path, filename)
200 assert os.path.isfile(dest)
200 assert os.path.isfile(dest)
201 with open(dest, 'r') as f:
201 with open(dest, 'r') as f:
202 output = f.read()
202 output = f.read()
203 self.assertEqual(output, 'e')
203 self.assertEqual(output, 'e')
204
204
205 def test_relpath(self):
205 def test_relpath(self):
206 """Can the FilesWriter handle relative paths for linked files correctly?"""
206 """Can the FilesWriter handle relative paths for linked files correctly?"""
207
207
208 # Work in a temporary directory.
208 # Work in a temporary directory.
209 with self.create_temp_cwd():
209 with self.create_temp_cwd():
210
210
211 # Create test file
211 # Create test file
212 os.mkdir('sub')
212 os.mkdir('sub')
213 with open(os.path.join('sub', 'c'), 'w') as f:
213 with open(os.path.join('sub', 'c'), 'w') as f:
214 f.write('d')
214 f.write('d')
215
215
216 # Create the resoruces dictionary
216 # Create the resoruces dictionary
217 res = {}
217 res = {}
218
218
219 # Create files writer, test output
219 # Create files writer, test output
220 writer = FilesWriter()
220 writer = FilesWriter()
221 writer.files = [os.path.join('sub', 'c')]
221 writer.files = [os.path.join('sub', 'c')]
222 writer.build_directory = u'build'
222 writer.build_directory = u'build'
223 writer.relpath = 'sub'
223 writer.relpath = 'sub'
224 writer.write(u'y', res, notebook_name="z")
224 writer.write(u'y', res, notebook_name="z")
225
225
226 # Check the output of the file
226 # Check the output of the file
227 assert os.path.isdir(writer.build_directory)
227 assert os.path.isdir(writer.build_directory)
228 dest = os.path.join(writer.build_directory, 'z')
228 dest = os.path.join(writer.build_directory, 'z')
229 with open(dest, 'r') as f:
229 with open(dest, 'r') as f:
230 output = f.read()
230 output = f.read()
231 self.assertEqual(output, u'y')
231 self.assertEqual(output, u'y')
232
232
233 # Check to make sure the linked file was copied
233 # Check to make sure the linked file was copied
234 dest = os.path.join(writer.build_directory, 'c')
234 dest = os.path.join(writer.build_directory, 'c')
235 assert os.path.isfile(dest)
235 assert os.path.isfile(dest)
236 with open(dest, 'r') as f:
236 with open(dest, 'r') as f:
237 output = f.read()
237 output = f.read()
238 self.assertEqual(output, 'd')
238 self.assertEqual(output, 'd')
239
240 def test_relpath_default(self):
241 """Is the FilesWriter default relative path correct?"""
242
243 # Work in a temporary directory.
244 with self.create_temp_cwd():
245
246 # Create test file
247 os.mkdir('sub')
248 with open(os.path.join('sub', 'c'), 'w') as f:
249 f.write('d')
250
251 # Create the resoruces dictionary
252 res = dict(metadata=dict(path="sub"))
253
254 # Create files writer, test output
255 writer = FilesWriter()
256 writer.files = [os.path.join('sub', 'c')]
257 writer.build_directory = u'build'
258 writer.write(u'y', res, notebook_name="z")
259
260 # Check the output of the file
261 assert os.path.isdir(writer.build_directory)
262 dest = os.path.join(writer.build_directory, 'z')
263 with open(dest, 'r') as f:
264 output = f.read()
265 self.assertEqual(output, u'y')
266
267 # Check to make sure the linked file was copied
268 dest = os.path.join(writer.build_directory, 'c')
269 assert os.path.isfile(dest)
270 with open(dest, 'r') as f:
271 output = f.read()
272 self.assertEqual(output, 'd')
273
274 def test_relpath_default(self):
275 """Does the FilesWriter relpath option take precedence over the path?"""
276
277 # Work in a temporary directory.
278 with self.create_temp_cwd():
279
280 # Create test file
281 os.mkdir('sub')
282 with open(os.path.join('sub', 'c'), 'w') as f:
283 f.write('d')
284
285 # Create the resoruces dictionary
286 res = dict(metadata=dict(path="other_sub"))
287
288 # Create files writer, test output
289 writer = FilesWriter()
290 writer.files = [os.path.join('sub', 'c')]
291 writer.build_directory = u'build'
292 writer.relpath = 'sub'
293 writer.write(u'y', res, notebook_name="z")
294
295 # Check the output of the file
296 assert os.path.isdir(writer.build_directory)
297 dest = os.path.join(writer.build_directory, 'z')
298 with open(dest, 'r') as f:
299 output = f.read()
300 self.assertEqual(output, u'y')
301
302 # Check to make sure the linked file was copied
303 dest = os.path.join(writer.build_directory, 'c')
304 assert os.path.isfile(dest)
305 with open(dest, 'r') as f:
306 output = f.read()
307 self.assertEqual(output, 'd')
General Comments 0
You need to be logged in to leave comments. Login now