##// 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 return model
210 return model
211
211
212 def _file_model(self, path, content=True):
212 def _file_model(self, path, content=True, format=None):
213 """Build a model for a file
213 """Build a model for a file
214
214
215 if content is requested, include the file contents.
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 model = self._base_model(path)
222 model = self._base_model(path)
219 model['type'] = 'file'
223 model['type'] = 'file'
@@ -224,13 +228,20 b' class FileContentsManager(ContentsManager):'
224 raise web.HTTPError(400, "Cannot get content of non-file %s" % os_path)
228 raise web.HTTPError(400, "Cannot get content of non-file %s" % os_path)
225 with io.open(os_path, 'rb') as f:
229 with io.open(os_path, 'rb') as f:
226 bcontent = f.read()
230 bcontent = f.read()
227 try:
231
228 model['content'] = bcontent.decode('utf8')
232 if format != 'base64':
229 except UnicodeError as e:
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 model['content'] = base64.encodestring(bcontent).decode('ascii')
242 model['content'] = base64.encodestring(bcontent).decode('ascii')
231 model['format'] = 'base64'
243 model['format'] = 'base64'
232 else:
244
233 model['format'] = 'text'
234 return model
245 return model
235
246
236
247
@@ -255,7 +266,7 b' class FileContentsManager(ContentsManager):'
255 self.validate_notebook_model(model)
266 self.validate_notebook_model(model)
256 return model
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 """ Takes a path for an entity and returns its model
270 """ Takes a path for an entity and returns its model
260
271
261 Parameters
272 Parameters
@@ -267,6 +278,9 b' class FileContentsManager(ContentsManager):'
267 type_ : str, optional
278 type_ : str, optional
268 The requested type - 'file', 'notebook', or 'directory'.
279 The requested type - 'file', 'notebook', or 'directory'.
269 Will raise HTTPError 406 if the content doesn't match.
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 Returns
285 Returns
272 -------
286 -------
@@ -291,7 +305,7 b' class FileContentsManager(ContentsManager):'
291 if type_ == 'directory':
305 if type_ == 'directory':
292 raise web.HTTPError(400,
306 raise web.HTTPError(400,
293 u'%s is not a directory')
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 return model
309 return model
296
310
297 def _save_notebook(self, os_path, model, path=''):
311 def _save_notebook(self, os_path, model, path=''):
@@ -62,7 +62,11 b' class ContentsHandler(IPythonHandler):'
62 if type_ not in {None, 'directory', 'file', 'notebook'}:
62 if type_ not in {None, 'directory', 'file', 'notebook'}:
63 raise web.HTTPError(400, u'Type %r is invalid' % type_)
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 if model['type'] == 'directory':
70 if model['type'] == 'directory':
67 # group listing by type, then by name (case-insensitive)
71 # group listing by type, then by name (case-insensitive)
68 # FIXME: sorting should be done in the frontends
72 # FIXME: sorting should be done in the frontends
@@ -135,7 +135,7 b' class ContentsManager(LoggingConfigurable):'
135 """
135 """
136 return self.file_exists(path) or self.dir_exists(path)
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 """Get the model of a file or directory with or without content."""
139 """Get the model of a file or directory with or without content."""
140 raise NotImplementedError('must be implemented in a subclass')
140 raise NotImplementedError('must be implemented in a subclass')
141
141
@@ -46,9 +46,14 b' class API(object):'
46 def list(self, path='/'):
46 def list(self, path='/'):
47 return self._req('GET', path)
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 if type_ is not None:
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 return self._req('GET', path)
57 return self._req('GET', path)
53
58
54 def create_untitled(self, path='/', ext='.ipynb'):
59 def create_untitled(self, path='/', ext='.ipynb'):
@@ -245,6 +250,10 b' class APITest(NotebookTestBase):'
245 with assert_http_error(404):
250 with assert_http_error(404):
246 self.api.read('foo/q.txt')
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 def test_get_binary_file_contents(self):
257 def test_get_binary_file_contents(self):
249 for d, name in self.dirs_nbs:
258 for d, name in self.dirs_nbs:
250 path = url_path_join(d, name + '.blob')
259 path = url_path_join(d, name + '.blob')
@@ -165,6 +165,9 b' class TestContentsManager(TestCase):'
165 self.assertEqual(nb_as_file['format'], 'text')
165 self.assertEqual(nb_as_file['format'], 'text')
166 self.assertNotIsInstance(nb_as_file['content'], dict)
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 # Test in sub-directory
171 # Test in sub-directory
169 sub_dir = '/foo/'
172 sub_dir = '/foo/'
170 self.make_dir(cm.root_dir, 'foo')
173 self.make_dir(cm.root_dir, 'foo')
General Comments 0
You need to be logged in to leave comments. Login now