##// END OF EJS Templates
Add relpath option to FilesWriter
Jessica B. Hamrick -
Show More
@@ -1,111 +1,123 b''
1 1 """Contains writer for writing nbconvert output to filesystem."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import io
7 7 import os
8 8 import glob
9 9
10 10 from IPython.utils.traitlets import Unicode
11 11 from IPython.utils.path import link_or_copy, ensure_dir_exists
12 12 from IPython.utils.py3compat import unicode_type
13 13
14 14 from .base import WriterBase
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Classes
18 18 #-----------------------------------------------------------------------------
19 19
20 20 class FilesWriter(WriterBase):
21 21 """Consumes nbconvert output and produces files."""
22 22
23 23
24 24 build_directory = Unicode("", config=True,
25 25 help="""Directory to write output to. Leave blank
26 26 to output to the current directory""")
27 27
28 relpath = Unicode(
29 "", config=True,
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
32 os.path.relpath(filename, relpath).""")
33
28 34
29 35 # Make sure that the output directory exists.
30 36 def _build_directory_changed(self, name, old, new):
31 37 if new:
32 38 ensure_dir_exists(new)
33 39
34 40
35 41 def __init__(self, **kw):
36 42 super(FilesWriter, self).__init__(**kw)
37 43 self._build_directory_changed('build_directory', self.build_directory,
38 44 self.build_directory)
39 45
40 46 def _makedir(self, path):
41 47 """Make a directory if it doesn't already exist"""
42 48 if path:
43 49 self.log.info("Making directory %s", path)
44 50 ensure_dir_exists(path)
45 51
46 52 def write(self, output, resources, notebook_name=None, **kw):
47 53 """
48 54 Consume and write Jinja output to the file system. Output directory
49 55 is set via the 'build_directory' variable of this instance (a
50 56 configurable).
51 57
52 58 See base for more...
53 59 """
54 60
55 61 # Verify that a notebook name is provided.
56 62 if notebook_name is None:
57 63 raise TypeError('notebook_name')
58 64
59 65 # Pull the extension and subdir from the resources dict.
60 66 output_extension = resources.get('output_extension', None)
61 67
62 68 # Write all of the extracted resources to the destination directory.
63 69 # NOTE: WE WRITE EVERYTHING AS-IF IT'S BINARY. THE EXTRACT FIG
64 70 # PREPROCESSOR SHOULD HANDLE UNIX/WINDOWS LINE ENDINGS...
65 71 for filename, data in resources.get('outputs', {}).items():
66 72
67 73 # Determine where to write the file to
68 74 dest = os.path.join(self.build_directory, filename)
69 75 path = os.path.dirname(dest)
70 76 self._makedir(path)
71 77
72 78 # Write file
73 79 self.log.debug("Writing %i bytes to support file %s", len(data), dest)
74 80 with io.open(dest, 'wb') as f:
75 81 f.write(data)
76 82
77 83 # Copy referenced files to output directory
78 84 if self.build_directory:
79 85 for filename in self.files:
80 86
81 87 # Copy files that match search pattern
82 88 for matching_filename in glob.glob(filename):
83 89
90 # compute the relative path for the filename
91 if self.relpath:
92 dest_filename = os.path.relpath(matching_filename, self.relpath)
93 else:
94 dest_filename = matching_filename
95
84 96 # Make sure folder exists.
85 dest = os.path.join(self.build_directory, matching_filename)
97 dest = os.path.join(self.build_directory, dest_filename)
86 98 path = os.path.dirname(dest)
87 99 self._makedir(path)
88 100
89 101 # Copy if destination is different.
90 102 if not os.path.normpath(dest) == os.path.normpath(matching_filename):
91 103 self.log.info("Linking %s -> %s", matching_filename, dest)
92 104 link_or_copy(matching_filename, dest)
93 105
94 106 # Determine where to write conversion results.
95 107 if output_extension is not None:
96 108 dest = notebook_name + output_extension
97 109 else:
98 110 dest = notebook_name
99 111 if self.build_directory:
100 112 dest = os.path.join(self.build_directory, dest)
101 113
102 114 # Write conversion results.
103 115 self.log.info("Writing %i bytes to %s", len(output), dest)
104 116 if isinstance(output, unicode_type):
105 117 with io.open(dest, 'w', encoding='utf-8') as f:
106 118 f.write(output)
107 119 else:
108 120 with io.open(dest, 'wb') as f:
109 121 f.write(output)
110 122
111 123 return dest
@@ -1,203 +1,238 b''
1 1 """
2 2 Module with tests for files
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import sys
18 18 import os
19 19
20 20 from ...tests.base import TestsBase
21 21 from ..files import FilesWriter
22 22 from IPython.utils.py3compat import PY3
23 23
24 24 if PY3:
25 25 from io import StringIO
26 26 else:
27 27 from StringIO import StringIO
28 28
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Class
32 32 #-----------------------------------------------------------------------------
33 33
34 34 class Testfiles(TestsBase):
35 35 """Contains test functions for files.py"""
36 36
37 37 def test_basic_output(self):
38 38 """Is FilesWriter basic output correct?"""
39 39
40 40 # Work in a temporary directory.
41 41 with self.create_temp_cwd():
42 42
43 43 # Create the resoruces dictionary
44 44 res = {}
45 45
46 46 # Create files writer, test output
47 47 writer = FilesWriter()
48 48 writer.write(u'y', res, notebook_name="z")
49 49
50 50 # Check the output of the file
51 51 with open('z', 'r') as f:
52 52 output = f.read()
53 53 self.assertEqual(output, u'y')
54 54
55 55 def test_ext(self):
56 56 """Does the FilesWriter add the correct extension to the output?"""
57 57
58 58 # Work in a temporary directory.
59 59 with self.create_temp_cwd():
60 60
61 61 # Create the resoruces dictionary
62 62 res = {'output_extension': '.txt'}
63 63
64 64 # Create files writer, test output
65 65 writer = FilesWriter()
66 66 writer.write(u'y', res, notebook_name="z")
67 67
68 68 # Check the output of the file
69 69 assert os.path.isfile('z.txt')
70 70 with open('z.txt', 'r') as f:
71 71 output = f.read()
72 72 self.assertEqual(output, u'y')
73 73
74 74
75 75 def test_extract(self):
76 76 """Can FilesWriter write extracted figures correctly?"""
77 77
78 78 # Work in a temporary directory.
79 79 with self.create_temp_cwd():
80 80
81 81 # Create the resoruces dictionary
82 82 res = {'outputs': {os.path.join('z_files', 'a'): b'b'}}
83 83
84 84 # Create files writer, test output
85 85 writer = FilesWriter()
86 86 writer.write(u'y', res, notebook_name="z")
87 87
88 88 # Check the output of the file
89 89 with open('z', 'r') as f:
90 90 output = f.read()
91 91 self.assertEqual(output, u'y')
92 92
93 93 # Check the output of the extracted file
94 94 extracted_file_dest = os.path.join('z_files', 'a')
95 95 assert os.path.isfile(extracted_file_dest)
96 96 with open(extracted_file_dest, 'r') as f:
97 97 output = f.read()
98 98 self.assertEqual(output, 'b')
99 99
100 100
101 101 def test_builddir(self):
102 102 """Can FilesWriter write to a build dir correctly?"""
103 103
104 104 # Work in a temporary directory.
105 105 with self.create_temp_cwd():
106 106
107 107 # Create the resoruces dictionary
108 108 res = {'outputs': {os.path.join('z_files', 'a'): b'b'}}
109 109
110 110 # Create files writer, test output
111 111 writer = FilesWriter()
112 112 writer.build_directory = u'build'
113 113 writer.write(u'y', res, notebook_name="z")
114 114
115 115 # Check the output of the file
116 116 assert os.path.isdir(writer.build_directory)
117 117 dest = os.path.join(writer.build_directory, 'z')
118 118 with open(dest, 'r') as f:
119 119 output = f.read()
120 120 self.assertEqual(output, u'y')
121 121
122 122 # Check the output of the extracted file
123 123 extracted_file_dest = os.path.join(writer.build_directory, 'z_files', 'a')
124 124 assert os.path.isfile(extracted_file_dest)
125 125 with open(extracted_file_dest, 'r') as f:
126 126 output = f.read()
127 127 self.assertEqual(output, 'b')
128 128
129 129
130 130 def test_links(self):
131 131 """Can the FilesWriter handle linked files correctly?"""
132 132
133 133 # Work in a temporary directory.
134 134 with self.create_temp_cwd():
135 135
136 136 # Create test file
137 137 os.mkdir('sub')
138 138 with open(os.path.join('sub', 'c'), 'w') as f:
139 139 f.write('d')
140 140
141 141 # Create the resoruces dictionary
142 142 res = {}
143 143
144 144 # Create files writer, test output
145 145 writer = FilesWriter()
146 146 writer.files = [os.path.join('sub', 'c')]
147 147 writer.build_directory = u'build'
148 148 writer.write(u'y', res, notebook_name="z")
149 149
150 150 # Check the output of the file
151 151 assert os.path.isdir(writer.build_directory)
152 152 dest = os.path.join(writer.build_directory, 'z')
153 153 with open(dest, 'r') as f:
154 154 output = f.read()
155 155 self.assertEqual(output, u'y')
156 156
157 157 # Check to make sure the linked file was copied
158 158 path = os.path.join(writer.build_directory, 'sub')
159 159 assert os.path.isdir(path)
160 160 dest = os.path.join(path, 'c')
161 161 assert os.path.isfile(dest)
162 162 with open(dest, 'r') as f:
163 163 output = f.read()
164 164 self.assertEqual(output, 'd')
165 165
166 166 def test_glob(self):
167 167 """Can the FilesWriter handle globbed files correctly?"""
168 168
169 169 # Work in a temporary directory.
170 170 with self.create_temp_cwd():
171 171
172 172 # Create test files
173 173 os.mkdir('sub')
174 174 with open(os.path.join('sub', 'c'), 'w') as f:
175 175 f.write('e')
176 176 with open(os.path.join('sub', 'd'), 'w') as f:
177 177 f.write('e')
178 178
179 179 # Create the resoruces dictionary
180 180 res = {}
181 181
182 182 # Create files writer, test output
183 183 writer = FilesWriter()
184 184 writer.files = ['sub/*']
185 185 writer.build_directory = u'build'
186 186 writer.write(u'y', res, notebook_name="z")
187 187
188 188 # Check the output of the file
189 189 assert os.path.isdir(writer.build_directory)
190 190 dest = os.path.join(writer.build_directory, 'z')
191 191 with open(dest, 'r') as f:
192 192 output = f.read()
193 193 self.assertEqual(output, u'y')
194 194
195 195 # Check to make sure the globbed files were copied
196 196 path = os.path.join(writer.build_directory, 'sub')
197 197 assert os.path.isdir(path)
198 198 for filename in ['c', 'd']:
199 199 dest = os.path.join(path, filename)
200 200 assert os.path.isfile(dest)
201 201 with open(dest, 'r') as f:
202 202 output = f.read()
203 203 self.assertEqual(output, 'e')
204
205 def test_relpath(self):
206 """Can the FilesWriter handle relative paths for linked files correctly?"""
207
208 # Work in a temporary directory.
209 with self.create_temp_cwd():
210
211 # Create test file
212 os.mkdir('sub')
213 with open(os.path.join('sub', 'c'), 'w') as f:
214 f.write('d')
215
216 # Create the resoruces dictionary
217 res = {}
218
219 # Create files writer, test output
220 writer = FilesWriter()
221 writer.files = [os.path.join('sub', 'c')]
222 writer.build_directory = u'build'
223 writer.relpath = 'sub'
224 writer.write(u'y', res, notebook_name="z")
225
226 # Check the output of the file
227 assert os.path.isdir(writer.build_directory)
228 dest = os.path.join(writer.build_directory, 'z')
229 with open(dest, 'r') as f:
230 output = f.read()
231 self.assertEqual(output, u'y')
232
233 # Check to make sure the linked file was copied
234 dest = os.path.join(writer.build_directory, 'c')
235 assert os.path.isfile(dest)
236 with open(dest, 'r') as f:
237 output = f.read()
238 self.assertEqual(output, 'd')
General Comments 0
You need to be logged in to leave comments. Login now