From f66a9ec4906d20cc6dc8badcf7fa5b71ba433b6c 2014-11-11 20:17:36 From: Thomas Kluyver Date: 2014-11-11 20:17:36 Subject: [PATCH] Add type parameter for contents GET requests --- diff --git a/IPython/html/services/contents/filemanager.py b/IPython/html/services/contents/filemanager.py index e994829..89ae995 100644 --- a/IPython/html/services/contents/filemanager.py +++ b/IPython/html/services/contents/filemanager.py @@ -255,13 +255,18 @@ class FileContentsManager(ContentsManager): self.validate_notebook_model(model) return model - def get_model(self, path, content=True): + def get_model(self, path, content=True, type_=None): """ Takes a path for an entity and returns its model Parameters ---------- path : str the API path that describes the relative path for the target + content : bool + Whether to include the contents in the reply + type_ : str, optional + The requested type - 'file', 'notebook', or 'directory'. + Will raise HTTPError 406 if the content doesn't match. Returns ------- @@ -276,10 +281,16 @@ class FileContentsManager(ContentsManager): os_path = self._get_os_path(path) if os.path.isdir(os_path): + if type_ not in (None, 'directory'): + raise web.HTTPError(400, + u'%s is a directory, not a %s' % (path, type_)) model = self._dir_model(path, content=content) - elif path.endswith('.ipynb'): + elif type_ == 'notebook' or (type_ is None and path.endswith('.ipynb')): model = self._notebook_model(path, content=content) else: + if type_ == 'directory': + raise web.HTTPError(400, + u'%s is not a directory') model = self._file_model(path, content=content) return model diff --git a/IPython/html/services/contents/handlers.py b/IPython/html/services/contents/handlers.py index 162e890..d097596 100644 --- a/IPython/html/services/contents/handlers.py +++ b/IPython/html/services/contents/handlers.py @@ -58,7 +58,11 @@ class ContentsHandler(IPythonHandler): of the files and directories it contains. """ path = path or '' - model = self.contents_manager.get_model(path=path) + type_ = self.get_query_argument('type', default=None) + if type_ not in {None, 'directory', 'file', 'notebook'}: + raise web.HTTPError(400, u'Type %r is invalid' % type_) + + model = self.contents_manager.get_model(path=path, type_=type_) if model['type'] == 'directory': # group listing by type, then by name (case-insensitive) # FIXME: sorting should be done in the frontends diff --git a/IPython/html/services/contents/manager.py b/IPython/html/services/contents/manager.py index 359cbd1..ea1a080 100644 --- a/IPython/html/services/contents/manager.py +++ b/IPython/html/services/contents/manager.py @@ -135,7 +135,7 @@ class ContentsManager(LoggingConfigurable): """ return self.file_exists(path) or self.dir_exists(path) - def get_model(self, path, content=True): + def get_model(self, path, content=True, type_=None): """Get the model of a file or directory with or without content.""" raise NotImplementedError('must be implemented in a subclass') diff --git a/IPython/html/services/contents/tests/test_contents_api.py b/IPython/html/services/contents/tests/test_contents_api.py index 80ab663..8c6ce15 100644 --- a/IPython/html/services/contents/tests/test_contents_api.py +++ b/IPython/html/services/contents/tests/test_contents_api.py @@ -46,7 +46,9 @@ class API(object): def list(self, path='/'): return self._req('GET', path) - def read(self, path): + def read(self, path, type_=None): + if type_ is not None: + path += '?type=' + type_ return self._req('GET', path) def create_untitled(self, path='/', ext='.ipynb'): @@ -259,6 +261,13 @@ class APITest(NotebookTestBase): with assert_http_error(404): self.api.read('foo/q.txt') + def test_get_bad_type(self): + with assert_http_error(400): + self.api.read(u'unicodé', type_='file') # this is a directory + + with assert_http_error(400): + self.api.read(u'unicodé/innonascii.ipynb', type_='directory') + def _check_created(self, resp, path, type='notebook'): self.assertEqual(resp.status_code, 201) location_header = py3compat.str_to_unicode(resp.headers['Location']) diff --git a/IPython/html/services/contents/tests/test_manager.py b/IPython/html/services/contents/tests/test_manager.py index 11a75e0..b0aa933 100644 --- a/IPython/html/services/contents/tests/test_manager.py +++ b/IPython/html/services/contents/tests/test_manager.py @@ -159,6 +159,12 @@ class TestContentsManager(TestCase): self.assertEqual(model['name'], name) self.assertEqual(model['path'], path) + nb_as_file = cm.get_model(path, content=True, type_='file') + self.assertEqual(nb_as_file['path'], path) + self.assertEqual(nb_as_file['type'], 'file') + self.assertEqual(nb_as_file['format'], 'text') + self.assertNotIsInstance(nb_as_file['content'], dict) + # Test in sub-directory sub_dir = '/foo/' self.make_dir(cm.root_dir, 'foo') @@ -170,6 +176,14 @@ class TestContentsManager(TestCase): self.assertIn('content', model2) self.assertEqual(model2['name'], 'Untitled0.ipynb') self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name)) + + # Test getting directory model + dirmodel = cm.get_model('foo') + self.assertEqual(dirmodel['type'], 'directory') + + with self.assertRaises(HTTPError): + cm.get_model('foo', type_='file') + @dec.skip_win32 def test_bad_symlink(self):