##// END OF EJS Templates
Allow specifying format when getting files from contents API
Thomas Kluyver -
Show More
@@ -209,11 +209,15 b' class FileContentsManager(ContentsManager):'
209 209
210 210 return model
211 211
212 def _file_model(self, path, content=True):
212 def _file_model(self, path, content=True, format=None):
213 213 """Build a model for a file
214 214
215 215 if content is requested, include the file contents.
216 UTF-8 text files will be unicode, binary files will be base64-encoded.
216
217 format:
218 If 'text', the contents will be decoded as UTF-8.
219 If 'base64', the raw bytes contents will be encoded as base64.
220 If not specified, try to decode as UTF-8, and fall back to base64
217 221 """
218 222 model = self._base_model(path)
219 223 model['type'] = 'file'
@@ -224,13 +228,20 b' class FileContentsManager(ContentsManager):'
224 228 raise web.HTTPError(400, "Cannot get content of non-file %s" % os_path)
225 229 with io.open(os_path, 'rb') as f:
226 230 bcontent = f.read()
227 try:
228 model['content'] = bcontent.decode('utf8')
229 except UnicodeError as e:
231
232 if format != 'base64':
233 try:
234 model['content'] = bcontent.decode('utf8')
235 except UnicodeError as e:
236 if format == 'text':
237 raise web.HTTPError(400, "%s is not UTF-8 encoded" % path)
238 else:
239 model['format'] = 'text'
240
241 if model['content'] is None:
230 242 model['content'] = base64.encodestring(bcontent).decode('ascii')
231 243 model['format'] = 'base64'
232 else:
233 model['format'] = 'text'
244
234 245 return model
235 246
236 247
@@ -255,7 +266,7 b' class FileContentsManager(ContentsManager):'
255 266 self.validate_notebook_model(model)
256 267 return model
257 268
258 def get_model(self, path, content=True, type_=None):
269 def get_model(self, path, content=True, type_=None, format=None):
259 270 """ Takes a path for an entity and returns its model
260 271
261 272 Parameters
@@ -267,6 +278,9 b' class FileContentsManager(ContentsManager):'
267 278 type_ : str, optional
268 279 The requested type - 'file', 'notebook', or 'directory'.
269 280 Will raise HTTPError 406 if the content doesn't match.
281 format : str, optional
282 The requested format for file contents. 'text' or 'base64'.
283 Ignored if this returns a notebook or directory model.
270 284
271 285 Returns
272 286 -------
@@ -291,7 +305,7 b' class FileContentsManager(ContentsManager):'
291 305 if type_ == 'directory':
292 306 raise web.HTTPError(400,
293 307 u'%s is not a directory')
294 model = self._file_model(path, content=content)
308 model = self._file_model(path, content=content, format=format)
295 309 return model
296 310
297 311 def _save_notebook(self, os_path, model, path=''):
@@ -62,7 +62,11 b' class ContentsHandler(IPythonHandler):'
62 62 if type_ not in {None, 'directory', 'file', 'notebook'}:
63 63 raise web.HTTPError(400, u'Type %r is invalid' % type_)
64 64
65 model = self.contents_manager.get_model(path=path, type_=type_)
65 format = self.get_query_argument('format', default=None)#
66 if format not in {None, 'text', 'base64'}:
67 raise web.HTTPError(400, u'Format %r is invalid' % format)
68
69 model = self.contents_manager.get_model(path=path, type_=type_, format=format)
66 70 if model['type'] == 'directory':
67 71 # group listing by type, then by name (case-insensitive)
68 72 # FIXME: sorting should be done in the frontends
@@ -135,7 +135,7 b' class ContentsManager(LoggingConfigurable):'
135 135 """
136 136 return self.file_exists(path) or self.dir_exists(path)
137 137
138 def get_model(self, path, content=True, type_=None):
138 def get_model(self, path, content=True, type_=None, format=None):
139 139 """Get the model of a file or directory with or without content."""
140 140 raise NotImplementedError('must be implemented in a subclass')
141 141
@@ -46,9 +46,14 b' class API(object):'
46 46 def list(self, path='/'):
47 47 return self._req('GET', path)
48 48
49 def read(self, path, type_=None):
49 def read(self, path, type_=None, format=None):
50 query = []
50 51 if type_ is not None:
51 path += '?type=' + type_
52 query.append('type=' + type_)
53 if format is not None:
54 query.append('format=' + format)
55 if query:
56 path += '?' + '&'.join(query)
52 57 return self._req('GET', path)
53 58
54 59 def create_untitled(self, path='/', ext='.ipynb'):
@@ -245,6 +250,10 b' class APITest(NotebookTestBase):'
245 250 with assert_http_error(404):
246 251 self.api.read('foo/q.txt')
247 252
253 # Specifying format=text should fail on a non-UTF-8 file
254 with assert_http_error(400):
255 self.api.read('foo/bar/baz.blob', type_='file', format='text')
256
248 257 def test_get_binary_file_contents(self):
249 258 for d, name in self.dirs_nbs:
250 259 path = url_path_join(d, name + '.blob')
@@ -165,6 +165,9 b' class TestContentsManager(TestCase):'
165 165 self.assertEqual(nb_as_file['format'], 'text')
166 166 self.assertNotIsInstance(nb_as_file['content'], dict)
167 167
168 nb_as_bin_file = cm.get_model(path, content=True, type_='file', format='base64')
169 self.assertEqual(nb_as_bin_file['format'], 'base64')
170
168 171 # Test in sub-directory
169 172 sub_dir = '/foo/'
170 173 self.make_dir(cm.root_dir, 'foo')
General Comments 0
You need to be logged in to leave comments. Login now