##// END OF EJS Templates
teach contents service about non-notebook files
MinRK -
Show More
@@ -416,6 +416,8 b' class TrailingSlashHandler(web.RequestHandler):'
416 416 path_regex = r"(?P<path>(?:/.*)*)"
417 417 notebook_name_regex = r"(?P<name>[^/]+\.ipynb)"
418 418 notebook_path_regex = "%s/%s" % (path_regex, notebook_name_regex)
419 file_name_regex = r"(?P<name>[^/]+)"
420 file_path_regex = "%s/%s" % (path_regex, file_name_regex)
419 421
420 422 #-----------------------------------------------------------------------------
421 423 # URL to handler mappings
@@ -1,3 +1,8 b''
1 """Tornado handlers for nbconvert."""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
1 6 import io
2 7 import os
3 8 import zipfile
@@ -73,7 +78,7 b' class NbconvertFileHandler(IPythonHandler):'
73 78 exporter = get_exporter(format, config=self.config, log=self.log)
74 79
75 80 path = path.strip('/')
76 model = self.contents_manager.get(name=name, path=path)
81 model = self.contents_manager.get_model(name=name, path=path)
77 82
78 83 self.set_header('Last-Modified', model['last_modified'])
79 84
@@ -3,6 +3,7 b''
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 import base64
6 7 import io
7 8 import os
8 9 import glob
@@ -56,17 +57,29 b' class FileContentsManager(ContentsManager):'
56 57 except OSError as e:
57 58 self.log.debug("copystat on %s failed", dest, exc_info=True)
58 59
59 def get_names(self, path=''):
60 """List all filenames in the path (relative to root_dir)."""
61 path = path.strip('/')
62 if not os.path.isdir(self._get_os_path(path=path)):
63 raise web.HTTPError(404, 'Directory not found: ' + path)
64 names = glob.glob(self._get_os_path('*', path))
65 names = [ os.path.basename(name) for name in names if os.path.isfile(name)]
66 return names
60 def _get_os_path(self, name=None, path=''):
61 """Given a filename and a URL path, return its file system
62 path.
63
64 Parameters
65 ----------
66 name : string
67 A filename
68 path : string
69 The relative URL path (with '/' as separator) to the named
70 file.
71
72 Returns
73 -------
74 path : string
75 API path to be evaluated relative to root_dir.
76 """
77 if name is not None:
78 path = path + '/' + name
79 return to_os_path(path, self.root_dir)
67 80
68 81 def path_exists(self, path):
69 """Does the API-style path (directory) actually exist?
82 """Does the API-style path refer to an extant directory?
70 83
71 84 Parameters
72 85 ----------
@@ -102,29 +115,26 b' class FileContentsManager(ContentsManager):'
102 115 os_path = self._get_os_path(path=path)
103 116 return is_hidden(os_path, self.root_dir)
104 117
105 def _get_os_path(self, name=None, path=''):
106 """Given a filename and a URL path, return its file system
107 path.
118 def file_exists(self, name, path=''):
119 """Returns True if the file exists, else returns False.
108 120
109 121 Parameters
110 122 ----------
111 123 name : string
112 A filename
124 The name of the file you are checking.
113 125 path : string
114 The relative URL path (with '/' as separator) to the named
115 file.
126 The relative path to the file's directory (with '/' as separator)
116 127
117 128 Returns
118 129 -------
119 path : string
120 API path to be evaluated relative to root_dir.
130 bool
121 131 """
122 if name is not None:
123 path = path + '/' + name
124 return to_os_path(path, self.root_dir)
132 path = path.strip('/')
133 nbpath = self._get_os_path(name, path=path)
134 return os.path.isfile(nbpath)
125 135
126 def file_exists(self, name, path=''):
127 """Returns a True if the file exists, else returns False.
136 def exists(self, name=None, path=''):
137 """Returns True if the path [and name] exists, else returns False.
128 138
129 139 Parameters
130 140 ----------
@@ -138,83 +148,107 b' class FileContentsManager(ContentsManager):'
138 148 bool
139 149 """
140 150 path = path.strip('/')
141 nbpath = self._get_os_path(name, path=path)
142 return os.path.isfile(nbpath)
151 os_path = self._get_os_path(name, path=path)
152 return os.path.exists(os_path)
143 153
144 # TODO: Remove this after we create the contents web service and directories are
145 # no longer listed by the notebook web service.
146 def list_dirs(self, path):
147 """List the directories for a given API style path."""
148 path = path.strip('/')
149 os_path = self._get_os_path('', path)
150 if not os.path.isdir(os_path):
151 raise web.HTTPError(404, u'directory does not exist: %r' % os_path)
152 elif is_hidden(os_path, self.root_dir):
153 self.log.info("Refusing to serve hidden directory, via 404 Error")
154 raise web.HTTPError(404, u'directory does not exist: %r' % os_path)
155 dir_names = os.listdir(os_path)
156 dirs = []
157 for name in dir_names:
158 os_path = self._get_os_path(name, path)
159 if os.path.isdir(os_path) and not is_hidden(os_path, self.root_dir)\
160 and self.should_list(name):
161 try:
162 model = self.get_dir_model(name, path)
163 except IOError:
164 pass
165 dirs.append(model)
166 dirs = sorted(dirs, key=sort_key)
167 return dirs
168
169 # TODO: Remove this after we create the contents web service and directories are
170 # no longer listed by the notebook web service.
171 def get_dir_model(self, name, path=''):
172 """Get the directory model given a directory name and its API style path"""
173 path = path.strip('/')
154 def _base_model(self, name, path=''):
155 """Build the common base of a contents model"""
174 156 os_path = self._get_os_path(name, path)
175 if not os.path.isdir(os_path):
176 raise IOError('directory does not exist: %r' % os_path)
177 157 info = os.stat(os_path)
178 158 last_modified = tz.utcfromtimestamp(info.st_mtime)
179 159 created = tz.utcfromtimestamp(info.st_ctime)
180 160 # Create the notebook model.
181 model ={}
161 model = {}
182 162 model['name'] = name
183 163 model['path'] = path
184 164 model['last_modified'] = last_modified
185 165 model['created'] = created
166 model['content'] = None
167 model['format'] = None
168 return model
169
170 def _dir_model(self, name, path='', content=True):
171 """Build a model for a directory
172
173 if content is requested, will include a listing of the directory
174 """
175 os_path = self._get_os_path(name, path)
176
177 if not os.path.isdir(os_path):
178 raise web.HTTPError(404, u'directory does not exist: %r' % os_path)
179 elif is_hidden(os_path, self.root_dir):
180 self.log.info("Refusing to serve hidden directory, via 404 Error")
181 raise web.HTTPError(404, u'directory does not exist: %r' % os_path)
182
183 if name is None:
184 if '/' in path:
185 path, name = path.rsplit('/', 1)
186 else:
187 name = ''
188 model = self._base_model(name, path)
186 189 model['type'] = 'directory'
190 dir_path = u'{}/{}'.format(path, name)
191 if content:
192 contents = []
193 for os_path in glob.glob(self._get_os_path('*', dir_path)):
194 name = os.path.basename(os_path)
195 if self.should_list(name) and not is_hidden(os_path, self.root_dir):
196 contents.append(self.get_model(name=name, path=dir_path, content=False))
197
198 model['content'] = sorted(contents, key=sort_key)
199
187 200 return model
188 201
189 def list_files(self, path):
190 """Returns a list of dictionaries that are the standard model
191 for all notebooks in the relative 'path'.
202 def _file_model(self, name, path='', content=True):
203 """Build a model for a file
192 204
193 Parameters
194 ----------
195 path : str
196 the URL path that describes the relative path for the
197 listed notebooks
205 if content is requested, include the file contents.
206 Text files will be unicode, binary files will be base64-encoded.
207 """
208 model = self._base_model(name, path)
209 model['type'] = 'file'
210 if content:
211 os_path = self._get_os_path(name, path)
212 try:
213 with io.open(os_path, 'r', encoding='utf-8') as f:
214 model['content'] = f.read()
215 except UnicodeError as e:
216 with io.open(os_path, 'rb') as f:
217 bcontent = f.read()
218 model['content'] = base64.encodestring(bcontent).decode('ascii')
219 model['format'] = 'base64'
220 else:
221 model['format'] = 'text'
222 return model
198 223
199 Returns
200 -------
201 notebooks : list of dicts
202 a list of the notebook models without 'content'
224
225 def _notebook_model(self, name, path='', content=True):
226 """Build a notebook model
227
228 if content is requested, the notebook content will be populated
229 as a JSON structure (not double-serialized)
203 230 """
204 path = path.strip('/')
205 names = self.get_names(path)
206 notebooks = [self.get(name, path, content=False)
207 for name in names if self.should_list(name)]
208 notebooks = sorted(notebooks, key=sort_key)
209 return notebooks
231 model = self._base_model(name, path)
232 model['type'] = 'notebook'
233 if content:
234 os_path = self._get_os_path(name, path)
235 with io.open(os_path, 'r', encoding='utf-8') as f:
236 try:
237 nb = current.read(f, u'json')
238 except Exception as e:
239 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
240 self.mark_trusted_cells(nb, name, path)
241 model['content'] = nb
242 model['format'] = 'json'
243 return model
210 244
211 def get(self, name, path='', content=True):
212 """ Takes a path and name for a notebook and returns its model
245 def get_model(self, name, path='', content=True):
246 """ Takes a path and name for an entity and returns its model
213 247
214 248 Parameters
215 249 ----------
216 250 name : str
217 the name of the notebook
251 the name of the target
218 252 path : str
219 253 the URL path that describes the relative path for
220 254 the notebook
@@ -222,31 +256,21 b' class FileContentsManager(ContentsManager):'
222 256 Returns
223 257 -------
224 258 model : dict
225 the notebook model. If contents=True, returns the 'contents'
226 dict in the model as well.
259 the contents model. If content=True, returns the contents
260 of the file or directory as well.
227 261 """
228 262 path = path.strip('/')
229 if not self.file_exists(name=name, path=path):
230 raise web.HTTPError(404, u'Notebook does not exist: %s' % name)
263
264 if not self.exists(name=name, path=path):
265 raise web.HTTPError(404, u'No such file or directory: %s/%s' % (path, name))
266
231 267 os_path = self._get_os_path(name, path)
232 info = os.stat(os_path)
233 last_modified = tz.utcfromtimestamp(info.st_mtime)
234 created = tz.utcfromtimestamp(info.st_ctime)
235 # Create the notebook model.
236 model ={}
237 model['name'] = name
238 model['path'] = path
239 model['last_modified'] = last_modified
240 model['created'] = created
241 model['type'] = 'notebook'
242 if content:
243 with io.open(os_path, 'r', encoding='utf-8') as f:
244 try:
245 nb = current.read(f, u'json')
246 except Exception as e:
247 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
248 self.mark_trusted_cells(nb, name, path)
249 model['content'] = nb
268 if os.path.isdir(os_path):
269 model = self._dir_model(name, path, content)
270 elif name.endswith('.ipynb'):
271 model = self._notebook_model(name, path, content)
272 else:
273 model = self._file_model(name, path, content)
250 274 return model
251 275
252 276 def save(self, model, name='', path=''):
@@ -281,7 +305,7 b' class FileContentsManager(ContentsManager):'
281 305 except Exception as e:
282 306 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s %s' % (os_path, e))
283 307
284 model = self.get(new_name, new_path, content=False)
308 model = self.get_model(new_name, new_path, content=False)
285 309 return model
286 310
287 311 def update(self, model, name, path=''):
@@ -291,7 +315,7 b' class FileContentsManager(ContentsManager):'
291 315 new_path = model.get('path', path).strip('/')
292 316 if path != new_path or name != new_name:
293 317 self.rename(name, path, new_name, new_path)
294 model = self.get(new_name, new_path, content=False)
318 model = self.get_model(new_name, new_path, content=False)
295 319 return model
296 320
297 321 def delete(self, name, path=''):
@@ -11,15 +11,15 b' from IPython.html.utils import url_path_join, url_escape'
11 11 from IPython.utils.jsonutil import date_default
12 12
13 13 from IPython.html.base.handlers import (IPythonHandler, json_errors,
14 notebook_path_regex, path_regex,
15 notebook_name_regex)
14 file_path_regex, path_regex,
15 file_name_regex)
16 16
17 17
18 18 class ContentsHandler(IPythonHandler):
19 19
20 20 SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE')
21 21
22 def location_url(self, name, path=''):
22 def location_url(self, name, path):
23 23 """Return the full URL location of a file.
24 24
25 25 Parameters
@@ -49,25 +49,19 b' class ContentsHandler(IPythonHandler):'
49 49 * GET with path and no filename lists files in a directory
50 50 * GET with path and filename returns file contents model
51 51 """
52 cm = self.contents_manager
53 # Check to see if a filename was given
54 if name is None:
55 # TODO: Remove this after we create the contents web service and directories are
56 # no longer listed by the notebook web service. This should only handle notebooks
57 # and not directories.
58 dirs = cm.list_dirs(path)
52 path = path or ''
53 model = self.contents_manager.get_model(name=name, path=path)
54 if model['type'] == 'directory':
55 # resort listing to group directories at the top
56 dirs = []
59 57 files = []
60 index = []
61 for nb in cm.list_files(path):
62 if nb['name'].lower() == 'index.ipynb':
63 index.append(nb)
58 for entry in model['content']:
59 if entry['type'] == 'directory':
60 dirs.append(entry)
64 61 else:
65 files.append(nb)
66 files = index + dirs + files
67 self.finish(json.dumps(files, default=date_default))
68 return
69 # get and return notebook representation
70 model = cm.get(name, path)
62 # do we also want to group notebooks separate from files?
63 files.append(entry)
64 model['content'] = dirs + files
71 65 self._finish_model(model, location=False)
72 66
73 67 @web.authenticated
@@ -148,8 +142,16 b' class ContentsHandler(IPythonHandler):'
148 142 """
149 143
150 144 if name is not None:
145 path = u'{}/{}'.format(path, name)
146
147 cm = self.contents_manager
148
149 if cm.file_exists(path):
151 150 raise web.HTTPError(400, "Only POST to directories. Use PUT for full names.")
152 151
152 if not cm.path_exists(path):
153 raise web.HTTPError(404, "No such directory: %s" % path)
154
153 155 model = self.get_json_body()
154 156
155 157 if model is not None:
@@ -200,6 +202,7 b' class ContentsHandler(IPythonHandler):'
200 202 def delete(self, path='', name=None):
201 203 """delete a file in the given path"""
202 204 cm = self.contents_manager
205 self.log.warn('delete %s:%s', path, name)
203 206 cm.delete(name, path)
204 207 self.set_status(204)
205 208 self.finish()
@@ -262,9 +265,9 b' class ModifyCheckpointsHandler(IPythonHandler):'
262 265 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
263 266
264 267 default_handlers = [
265 (r"/api/contents%s/checkpoints" % notebook_path_regex, CheckpointsHandler),
266 (r"/api/contents%s/checkpoints/%s" % (notebook_path_regex, _checkpoint_id_regex),
268 (r"/api/contents%s/checkpoints" % file_path_regex, CheckpointsHandler),
269 (r"/api/contents%s/checkpoints/%s" % (file_path_regex, _checkpoint_id_regex),
267 270 ModifyCheckpointsHandler),
268 (r"/api/contents%s" % notebook_path_regex, ContentsHandler),
271 (r"/api/contents%s" % file_path_regex, ContentsHandler),
269 272 (r"/api/contents%s" % path_regex, ContentsHandler),
270 273 ]
@@ -75,27 +75,7 b' class ContentsManager(LoggingConfigurable):'
75 75 """
76 76 raise NotImplementedError('must be implemented in a subclass')
77 77
78 # TODO: Remove this after we create the contents web service and directories are
79 # no longer listed by the notebook web service.
80 def list_dirs(self, path):
81 """List the directory models for a given API style path."""
82 raise NotImplementedError('must be implemented in a subclass')
83
84 # TODO: Remove this after we create the contents web service and directories are
85 # no longer listed by the notebook web service.
86 def get_dir_model(self, name, path=''):
87 """Get the directory model given a directory name and its API style path.
88
89 The keys in the model should be:
90 * name
91 * path
92 * last_modified
93 * created
94 * type='directory'
95 """
96 raise NotImplementedError('must be implemented in a subclass')
97
98 def list_files(self, path=''):
78 def list(self, path=''):
99 79 """Return a list of contents dicts without content.
100 80
101 81 This returns a list of dicts
@@ -196,7 +176,7 b' class ContentsManager(LoggingConfigurable):'
196 176 If to_name not specified, increment `from_name-Copy#.ipynb`.
197 177 """
198 178 path = path.strip('/')
199 model = self.get(from_name, path)
179 model = self.get_model(from_name, path)
200 180 if not to_name:
201 181 base, ext = os.path.splitext(from_name)
202 182 copy_name = u'{0}-Copy{1}'.format(base, ext)
@@ -218,7 +198,7 b' class ContentsManager(LoggingConfigurable):'
218 198 path : string
219 199 The notebook's directory
220 200 """
221 model = self.get(name, path)
201 model = self.get_model(name, path)
222 202 nb = model['content']
223 203 self.log.warn("Trusting notebook %s/%s", path, name)
224 204 self.notary.mark_cells(nb, True)
@@ -1,6 +1,7 b''
1 1 # coding: utf-8
2 2 """Test the contents webservice API."""
3 3
4 import base64
4 5 import io
5 6 import json
6 7 import os
@@ -23,11 +24,11 b' from IPython.utils.data import uniq_stable'
23 24
24 25 # TODO: Remove this after we create the contents web service and directories are
25 26 # no longer listed by the notebook web service.
26 def notebooks_only(nb_list):
27 return [nb for nb in nb_list if nb['type']=='notebook']
27 def notebooks_only(dir_model):
28 return [nb for nb in dir_model['content'] if nb['type']=='notebook']
28 29
29 def dirs_only(nb_list):
30 return [x for x in nb_list if x['type']=='directory']
30 def dirs_only(dir_model):
31 return [x for x in dir_model['content'] if x['type']=='directory']
31 32
32 33
33 34 class API(object):
@@ -112,8 +113,20 b' class APITest(NotebookTestBase):'
112 113 del dirs[0] # remove ''
113 114 top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs}
114 115
116 @staticmethod
117 def _blob_for_name(name):
118 return name.encode('utf-8') + b'\xFF'
119
120 @staticmethod
121 def _txt_for_name(name):
122 return u'%s text file' % name
123
115 124 def setUp(self):
116 125 nbdir = self.notebook_dir.name
126 self.blob = os.urandom(100)
127 self.b64_blob = base64.encodestring(self.blob).decode('ascii')
128
129
117 130
118 131 for d in (self.dirs + self.hidden_dirs):
119 132 d.replace('/', os.sep)
@@ -122,11 +135,21 b' class APITest(NotebookTestBase):'
122 135
123 136 for d, name in self.dirs_nbs:
124 137 d = d.replace('/', os.sep)
138 # create a notebook
125 139 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w',
126 140 encoding='utf-8') as f:
127 141 nb = new_notebook(name=name)
128 142 write(nb, f, format='ipynb')
129 143
144 # create a text file
145 with io.open(pjoin(nbdir, d, '%s.txt' % name), 'w',
146 encoding='utf-8') as f:
147 f.write(self._txt_for_name(name))
148
149 # create a binary file
150 with io.open(pjoin(nbdir, d, '%s.blob' % name), 'wb') as f:
151 f.write(self._blob_for_name(name))
152
130 153 self.api = API(self.base_url())
131 154
132 155 def tearDown(self):
@@ -178,18 +201,49 b' class APITest(NotebookTestBase):'
178 201 with assert_http_error(404):
179 202 self.api.list('nonexistant')
180 203
181 def test_get_contents(self):
204 def test_get_nb_contents(self):
182 205 for d, name in self.dirs_nbs:
183 206 nb = self.api.read('%s.ipynb' % name, d+'/').json()
184 207 self.assertEqual(nb['name'], u'%s.ipynb' % name)
208 self.assertEqual(nb['type'], 'notebook')
209 self.assertIn('content', nb)
210 self.assertEqual(nb['format'], 'json')
185 211 self.assertIn('content', nb)
186 212 self.assertIn('metadata', nb['content'])
187 213 self.assertIsInstance(nb['content']['metadata'], dict)
188 214
215 def test_get_contents_no_such_file(self):
189 216 # Name that doesn't exist - should be a 404
190 217 with assert_http_error(404):
191 218 self.api.read('q.ipynb', 'foo')
192 219
220 def test_get_text_file_contents(self):
221 for d, name in self.dirs_nbs:
222 model = self.api.read(u'%s.txt' % name, d+'/').json()
223 self.assertEqual(model['name'], u'%s.txt' % name)
224 self.assertIn('content', model)
225 self.assertEqual(model['format'], 'text')
226 self.assertEqual(model['type'], 'file')
227 self.assertEqual(model['content'], self._txt_for_name(name))
228
229 # Name that doesn't exist - should be a 404
230 with assert_http_error(404):
231 self.api.read('q.txt', 'foo')
232
233 def test_get_binary_file_contents(self):
234 for d, name in self.dirs_nbs:
235 model = self.api.read(u'%s.blob' % name, d+'/').json()
236 self.assertEqual(model['name'], u'%s.blob' % name)
237 self.assertIn('content', model)
238 self.assertEqual(model['format'], 'base64')
239 self.assertEqual(model['type'], 'file')
240 b64_data = base64.encodestring(self._blob_for_name(name)).decode('ascii')
241 self.assertEqual(model['content'], b64_data)
242
243 # Name that doesn't exist - should be a 404
244 with assert_http_error(404):
245 self.api.read('q.txt', 'foo')
246
193 247 def _check_nb_created(self, resp, name, path):
194 248 self.assertEqual(resp.status_code, 201)
195 249 location_header = py3compat.str_to_unicode(resp.headers['Location'])
@@ -70,7 +70,7 b' class TestFileContentsManager(TestCase):'
70 70 self.assertEqual(cp_subdir, os.path.join(root, subd, fm.checkpoint_dir, cp_name))
71 71
72 72
73 class TestNotebookManager(TestCase):
73 class TestContentsManager(TestCase):
74 74
75 75 def setUp(self):
76 76 self._temp_dir = TemporaryDirectory()
@@ -105,7 +105,7 b' class TestNotebookManager(TestCase):'
105 105 name = model['name']
106 106 path = model['path']
107 107
108 full_model = cm.get(name, path)
108 full_model = cm.get_model(name, path)
109 109 nb = full_model['content']
110 110 self.add_code_cell(nb)
111 111
@@ -140,7 +140,7 b' class TestNotebookManager(TestCase):'
140 140 path = model['path']
141 141
142 142 # Check that we 'get' on the notebook we just created
143 model2 = cm.get(name, path)
143 model2 = cm.get_model(name, path)
144 144 assert isinstance(model2, dict)
145 145 self.assertIn('name', model2)
146 146 self.assertIn('path', model2)
@@ -151,7 +151,7 b' class TestNotebookManager(TestCase):'
151 151 sub_dir = '/foo/'
152 152 self.make_dir(cm.root_dir, 'foo')
153 153 model = cm.create_notebook(None, sub_dir)
154 model2 = cm.get(name, sub_dir)
154 model2 = cm.get_model(name, sub_dir)
155 155 assert isinstance(model2, dict)
156 156 self.assertIn('name', model2)
157 157 self.assertIn('path', model2)
@@ -175,7 +175,7 b' class TestNotebookManager(TestCase):'
175 175 self.assertEqual(model['name'], 'test.ipynb')
176 176
177 177 # Make sure the old name is gone
178 self.assertRaises(HTTPError, cm.get, name, path)
178 self.assertRaises(HTTPError, cm.get_model, name, path)
179 179
180 180 # Test in sub-directory
181 181 # Create a directory and notebook in that directory
@@ -195,7 +195,7 b' class TestNotebookManager(TestCase):'
195 195 self.assertEqual(model['path'], sub_dir.strip('/'))
196 196
197 197 # Make sure the old name is gone
198 self.assertRaises(HTTPError, cm.get, name, path)
198 self.assertRaises(HTTPError, cm.get_model, name, path)
199 199
200 200 def test_save(self):
201 201 cm = self.contents_manager
@@ -205,7 +205,7 b' class TestNotebookManager(TestCase):'
205 205 path = model['path']
206 206
207 207 # Get the model with 'content'
208 full_model = cm.get(name, path)
208 full_model = cm.get_model(name, path)
209 209
210 210 # Save the notebook
211 211 model = cm.save(full_model, name, path)
@@ -222,7 +222,7 b' class TestNotebookManager(TestCase):'
222 222 model = cm.create_notebook(None, sub_dir)
223 223 name = model['name']
224 224 path = model['path']
225 model = cm.get(name, path)
225 model = cm.get_model(name, path)
226 226
227 227 # Change the name in the model for rename
228 228 model = cm.save(model, name, path)
@@ -241,7 +241,7 b' class TestNotebookManager(TestCase):'
241 241 cm.delete(name, path)
242 242
243 243 # Check that a 'get' on the deleted notebook raises and error
244 self.assertRaises(HTTPError, cm.get, name, path)
244 self.assertRaises(HTTPError, cm.get_model, name, path)
245 245
246 246 def test_copy(self):
247 247 cm = self.contents_manager
@@ -262,12 +262,12 b' class TestNotebookManager(TestCase):'
262 262 cm = self.contents_manager
263 263 nb, name, path = self.new_notebook()
264 264
265 untrusted = cm.get(name, path)['content']
265 untrusted = cm.get_model(name, path)['content']
266 266 assert not cm.notary.check_cells(untrusted)
267 267
268 268 # print(untrusted)
269 269 cm.trust_notebook(name, path)
270 trusted = cm.get(name, path)['content']
270 trusted = cm.get_model(name, path)['content']
271 271 # print(trusted)
272 272 assert cm.notary.check_cells(trusted)
273 273
@@ -281,7 +281,7 b' class TestNotebookManager(TestCase):'
281 281 assert not cell.trusted
282 282
283 283 cm.trust_notebook(name, path)
284 nb = cm.get(name, path)['content']
284 nb = cm.get_model(name, path)['content']
285 285 for cell in nb.worksheets[0].cells:
286 286 if cell.cell_type == 'code':
287 287 assert cell.trusted
@@ -295,7 +295,7 b' class TestNotebookManager(TestCase):'
295 295 assert not cm.notary.check_signature(nb)
296 296
297 297 cm.trust_notebook(name, path)
298 nb = cm.get(name, path)['content']
298 nb = cm.get_model(name, path)['content']
299 299 cm.mark_trusted_cells(nb, name, path)
300 300 cm.check_and_sign(nb, name, path)
301 301 assert cm.notary.check_signature(nb)
@@ -161,7 +161,8 b' define(['
161 161 message = param.msg;
162 162 }
163 163 var item = null;
164 var len = data.length;
164 var content = data.content;
165 var len = content.length;
165 166 this.clear_list();
166 167 if (len === 0) {
167 168 item = this.new_notebook_item(0);
@@ -177,12 +178,12 b' define(['
177 178 offset = 1;
178 179 }
179 180 for (var i=0; i<len; i++) {
180 if (data[i].type === 'directory') {
181 var name = data[i].name;
181 if (content[i].type === 'directory') {
182 var name = content[i].name;
182 183 item = this.new_notebook_item(i+offset);
183 184 this.add_dir(path, name, item);
184 185 } else {
185 var name = data[i].name;
186 var name = content[i].name;
186 187 item = this.new_notebook_item(i+offset);
187 188 this.add_link(path, name, item);
188 189 name = utils.url_path_join(path, name);
@@ -1,28 +1,12 b''
1 """Tornado handlers for the tree view.
1 """Tornado handlers for the tree view."""
2 2
3 Authors:
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 5
5 * Brian Granger
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18 6 from tornado import web
19 7 from ..base.handlers import IPythonHandler, notebook_path_regex, path_regex
20 8 from ..utils import url_path_join, url_escape
21 9
22 #-----------------------------------------------------------------------------
23 # Handlers
24 #-----------------------------------------------------------------------------
25
26 10
27 11 class TreeHandler(IPythonHandler):
28 12 """Render the tree view, listing notebooks, clusters, etc."""
General Comments 0
You need to be logged in to leave comments. Login now