Show More
@@ -7,6 +7,7 from fnmatch import fnmatch | |||||
7 | import itertools |
|
7 | import itertools | |
8 | import json |
|
8 | import json | |
9 | import os |
|
9 | import os | |
|
10 | import re | |||
10 |
|
11 | |||
11 | from tornado.web import HTTPError |
|
12 | from tornado.web import HTTPError | |
12 |
|
13 | |||
@@ -15,6 +16,7 from IPython.nbformat import sign, validate, ValidationError | |||||
15 | from IPython.nbformat.v4 import new_notebook |
|
16 | from IPython.nbformat.v4 import new_notebook | |
16 | from IPython.utils.traitlets import Instance, Unicode, List |
|
17 | from IPython.utils.traitlets import Instance, Unicode, List | |
17 |
|
18 | |||
|
19 | copy_pat = re.compile(r'\-Copy\d*\.') | |||
18 |
|
20 | |||
19 | class ContentsManager(LoggingConfigurable): |
|
21 | class ContentsManager(LoggingConfigurable): | |
20 | """Base class for serving files and directories. |
|
22 | """Base class for serving files and directories. | |
@@ -188,7 +190,7 class ContentsManager(LoggingConfigurable): | |||||
188 | """ |
|
190 | """ | |
189 | return path |
|
191 | return path | |
190 |
|
192 | |||
191 | def increment_filename(self, filename, path=''): |
|
193 | def increment_filename(self, filename, path='', insert=''): | |
192 | """Increment a filename until it is unique. |
|
194 | """Increment a filename until it is unique. | |
193 |
|
195 | |||
194 | Parameters |
|
196 | Parameters | |
@@ -206,8 +208,12 class ContentsManager(LoggingConfigurable): | |||||
206 | path = path.strip('/') |
|
208 | path = path.strip('/') | |
207 | basename, ext = os.path.splitext(filename) |
|
209 | basename, ext = os.path.splitext(filename) | |
208 | for i in itertools.count(): |
|
210 | for i in itertools.count(): | |
209 | name = u'{basename}{i}{ext}'.format(basename=basename, i=i, |
|
211 | if i: | |
210 | ext=ext) |
|
212 | insert_i = '{}{}'.format(insert, i) | |
|
213 | else: | |||
|
214 | insert_i = '' | |||
|
215 | name = u'{basename}{insert}{ext}'.format(basename=basename, | |||
|
216 | insert=insert_i, ext=ext) | |||
211 | if not self.exists(u'{}/{}'.format(path, name)): |
|
217 | if not self.exists(u'{}/{}'.format(path, name)): | |
212 | break |
|
218 | break | |
213 | return name |
|
219 | return name | |
@@ -244,8 +250,10 class ContentsManager(LoggingConfigurable): | |||||
244 | else: |
|
250 | else: | |
245 | model.setdefault('type', 'file') |
|
251 | model.setdefault('type', 'file') | |
246 |
|
252 | |||
|
253 | insert = '' | |||
247 | if model['type'] == 'directory': |
|
254 | if model['type'] == 'directory': | |
248 | untitled = self.untitled_directory |
|
255 | untitled = self.untitled_directory | |
|
256 | insert = ' ' | |||
249 | elif model['type'] == 'notebook': |
|
257 | elif model['type'] == 'notebook': | |
250 | untitled = self.untitled_notebook |
|
258 | untitled = self.untitled_notebook | |
251 | ext = '.ipynb' |
|
259 | ext = '.ipynb' | |
@@ -254,7 +262,7 class ContentsManager(LoggingConfigurable): | |||||
254 | else: |
|
262 | else: | |
255 | raise HTTPError(400, "Unexpected model type: %r" % model['type']) |
|
263 | raise HTTPError(400, "Unexpected model type: %r" % model['type']) | |
256 |
|
264 | |||
257 | name = self.increment_filename(untitled + ext, path) |
|
265 | name = self.increment_filename(untitled + ext, path, insert=insert) | |
258 | path = u'{0}/{1}'.format(path, name) |
|
266 | path = u'{0}/{1}'.format(path, name) | |
259 | return self.new(model, path) |
|
267 | return self.new(model, path) | |
260 |
|
268 | |||
@@ -309,9 +317,8 class ContentsManager(LoggingConfigurable): | |||||
309 | if not to_path: |
|
317 | if not to_path: | |
310 | to_path = from_dir |
|
318 | to_path = from_dir | |
311 | if self.dir_exists(to_path): |
|
319 | if self.dir_exists(to_path): | |
312 |
|
|
320 | name = copy_pat.sub(u'.', from_name) | |
313 | copy_name = u'{0}-Copy{1}'.format(base, ext) |
|
321 | to_name = self.increment_filename(name, to_path, insert='-Copy') | |
314 | to_name = self.increment_filename(copy_name, to_path) |
|
|||
315 | to_path = u'{0}/{1}'.format(to_path, to_name) |
|
322 | to_path = u'{0}/{1}'.format(to_path, to_name) | |
316 |
|
323 | |||
317 | model = self.save(model, to_path) |
|
324 | model = self.save(model, to_path) |
@@ -291,7 +291,7 class APITest(NotebookTestBase): | |||||
291 |
|
291 | |||
292 | def test_create_untitled(self): |
|
292 | def test_create_untitled(self): | |
293 | resp = self.api.create_untitled(path=u'å b') |
|
293 | resp = self.api.create_untitled(path=u'å b') | |
294 |
self._check_created(resp, u'å b/Untitled |
|
294 | self._check_created(resp, u'å b/Untitled.ipynb') | |
295 |
|
295 | |||
296 | # Second time |
|
296 | # Second time | |
297 | resp = self.api.create_untitled(path=u'å b') |
|
297 | resp = self.api.create_untitled(path=u'å b') | |
@@ -299,13 +299,13 class APITest(NotebookTestBase): | |||||
299 |
|
299 | |||
300 | # And two directories down |
|
300 | # And two directories down | |
301 | resp = self.api.create_untitled(path='foo/bar') |
|
301 | resp = self.api.create_untitled(path='foo/bar') | |
302 |
self._check_created(resp, 'foo/bar/Untitled |
|
302 | self._check_created(resp, 'foo/bar/Untitled.ipynb') | |
303 |
|
303 | |||
304 | def test_create_untitled_txt(self): |
|
304 | def test_create_untitled_txt(self): | |
305 | resp = self.api.create_untitled(path='foo/bar', ext='.txt') |
|
305 | resp = self.api.create_untitled(path='foo/bar', ext='.txt') | |
306 |
self._check_created(resp, 'foo/bar/untitled |
|
306 | self._check_created(resp, 'foo/bar/untitled.txt', type='file') | |
307 |
|
307 | |||
308 |
resp = self.api.read(path='foo/bar/untitled |
|
308 | resp = self.api.read(path='foo/bar/untitled.txt') | |
309 | model = resp.json() |
|
309 | model = resp.json() | |
310 | self.assertEqual(model['type'], 'file') |
|
310 | self.assertEqual(model['type'], 'file') | |
311 | self.assertEqual(model['format'], 'text') |
|
311 | self.assertEqual(model['format'], 'text') | |
@@ -320,15 +320,15 class APITest(NotebookTestBase): | |||||
320 |
|
320 | |||
321 | def test_mkdir_untitled(self): |
|
321 | def test_mkdir_untitled(self): | |
322 | resp = self.api.mkdir_untitled(path=u'å b') |
|
322 | resp = self.api.mkdir_untitled(path=u'å b') | |
323 |
self._check_created(resp, u'å b/Untitled Folder |
|
323 | self._check_created(resp, u'å b/Untitled Folder', type='directory') | |
324 |
|
324 | |||
325 | # Second time |
|
325 | # Second time | |
326 | resp = self.api.mkdir_untitled(path=u'å b') |
|
326 | resp = self.api.mkdir_untitled(path=u'å b') | |
327 | self._check_created(resp, u'å b/Untitled Folder1', type='directory') |
|
327 | self._check_created(resp, u'å b/Untitled Folder 1', type='directory') | |
328 |
|
328 | |||
329 | # And two directories down |
|
329 | # And two directories down | |
330 | resp = self.api.mkdir_untitled(path='foo/bar') |
|
330 | resp = self.api.mkdir_untitled(path='foo/bar') | |
331 |
self._check_created(resp, 'foo/bar/Untitled Folder |
|
331 | self._check_created(resp, 'foo/bar/Untitled Folder', type='directory') | |
332 |
|
332 | |||
333 | def test_mkdir(self): |
|
333 | def test_mkdir(self): | |
334 | path = u'å b/New ∂ir' |
|
334 | path = u'å b/New ∂ir' | |
@@ -390,15 +390,25 class APITest(NotebookTestBase): | |||||
390 | self.assertEqual(data['content']['nbformat'], 4) |
|
390 | self.assertEqual(data['content']['nbformat'], 4) | |
391 |
|
391 | |||
392 | def test_copy(self): |
|
392 | def test_copy(self): | |
393 |
resp = self.api.copy(u'å b/ç d.ipynb', u' |
|
393 | resp = self.api.copy(u'å b/ç d.ipynb', u'å b') | |
394 |
self._check_created(resp, u' |
|
394 | self._check_created(resp, u'å b/ç d-Copy1.ipynb') | |
395 |
|
395 | |||
396 | resp = self.api.copy(u'å b/ç d.ipynb', u'å b') |
|
396 | resp = self.api.copy(u'å b/ç d.ipynb', u'å b') | |
397 |
self._check_created(resp, u'å b/ç d-Copy |
|
397 | self._check_created(resp, u'å b/ç d-Copy2.ipynb') | |
398 |
|
398 | |||
|
399 | def test_copy_copy(self): | |||
|
400 | resp = self.api.copy(u'å b/ç d.ipynb', u'å b') | |||
|
401 | self._check_created(resp, u'å b/ç d-Copy1.ipynb') | |||
|
402 | ||||
|
403 | resp = self.api.copy(u'å b/ç d-Copy1.ipynb', u'å b') | |||
|
404 | self._check_created(resp, u'å b/ç d-Copy2.ipynb') | |||
|
405 | ||||
399 | def test_copy_path(self): |
|
406 | def test_copy_path(self): | |
400 | resp = self.api.copy(u'foo/a.ipynb', u'å b') |
|
407 | resp = self.api.copy(u'foo/a.ipynb', u'å b') | |
401 |
self._check_created(resp, u'å b/a |
|
408 | self._check_created(resp, u'å b/a.ipynb') | |
|
409 | ||||
|
410 | resp = self.api.copy(u'foo/a.ipynb', u'å b') | |||
|
411 | self._check_created(resp, u'å b/a-Copy1.ipynb') | |||
402 |
|
412 | |||
403 | def test_copy_put_400(self): |
|
413 | def test_copy_put_400(self): | |
404 | with assert_http_error(400): |
|
414 | with assert_http_error(400): |
@@ -121,8 +121,8 class TestContentsManager(TestCase): | |||||
121 | self.assertIn('path', model) |
|
121 | self.assertIn('path', model) | |
122 | self.assertIn('type', model) |
|
122 | self.assertIn('type', model) | |
123 | self.assertEqual(model['type'], 'notebook') |
|
123 | self.assertEqual(model['type'], 'notebook') | |
124 |
self.assertEqual(model['name'], 'Untitled |
|
124 | self.assertEqual(model['name'], 'Untitled.ipynb') | |
125 |
self.assertEqual(model['path'], 'Untitled |
|
125 | self.assertEqual(model['path'], 'Untitled.ipynb') | |
126 |
|
126 | |||
127 | # Test in sub-directory |
|
127 | # Test in sub-directory | |
128 | model = cm.new_untitled(type='directory') |
|
128 | model = cm.new_untitled(type='directory') | |
@@ -131,8 +131,8 class TestContentsManager(TestCase): | |||||
131 | self.assertIn('path', model) |
|
131 | self.assertIn('path', model) | |
132 | self.assertIn('type', model) |
|
132 | self.assertIn('type', model) | |
133 | self.assertEqual(model['type'], 'directory') |
|
133 | self.assertEqual(model['type'], 'directory') | |
134 |
self.assertEqual(model['name'], 'Untitled Folder |
|
134 | self.assertEqual(model['name'], 'Untitled Folder') | |
135 |
self.assertEqual(model['path'], 'Untitled Folder |
|
135 | self.assertEqual(model['path'], 'Untitled Folder') | |
136 | sub_dir = model['path'] |
|
136 | sub_dir = model['path'] | |
137 |
|
137 | |||
138 | model = cm.new_untitled(path=sub_dir) |
|
138 | model = cm.new_untitled(path=sub_dir) | |
@@ -141,8 +141,8 class TestContentsManager(TestCase): | |||||
141 | self.assertIn('path', model) |
|
141 | self.assertIn('path', model) | |
142 | self.assertIn('type', model) |
|
142 | self.assertIn('type', model) | |
143 | self.assertEqual(model['type'], 'file') |
|
143 | self.assertEqual(model['type'], 'file') | |
144 |
self.assertEqual(model['name'], 'untitled |
|
144 | self.assertEqual(model['name'], 'untitled') | |
145 |
self.assertEqual(model['path'], '%s/untitled |
|
145 | self.assertEqual(model['path'], '%s/untitled' % sub_dir) | |
146 |
|
146 | |||
147 | def test_get(self): |
|
147 | def test_get(self): | |
148 | cm = self.contents_manager |
|
148 | cm = self.contents_manager | |
@@ -177,7 +177,7 class TestContentsManager(TestCase): | |||||
177 | self.assertIn('name', model2) |
|
177 | self.assertIn('name', model2) | |
178 | self.assertIn('path', model2) |
|
178 | self.assertIn('path', model2) | |
179 | self.assertIn('content', model2) |
|
179 | self.assertIn('content', model2) | |
180 |
self.assertEqual(model2['name'], 'Untitled |
|
180 | self.assertEqual(model2['name'], 'Untitled.ipynb') | |
181 | self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name)) |
|
181 | self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name)) | |
182 |
|
182 | |||
183 | # Test getting directory model |
|
183 | # Test getting directory model | |
@@ -291,8 +291,8 class TestContentsManager(TestCase): | |||||
291 | assert isinstance(model, dict) |
|
291 | assert isinstance(model, dict) | |
292 | self.assertIn('name', model) |
|
292 | self.assertIn('name', model) | |
293 | self.assertIn('path', model) |
|
293 | self.assertIn('path', model) | |
294 |
self.assertEqual(model['name'], 'Untitled |
|
294 | self.assertEqual(model['name'], 'Untitled.ipynb') | |
295 |
self.assertEqual(model['path'], 'foo/Untitled |
|
295 | self.assertEqual(model['path'], 'foo/Untitled.ipynb') | |
296 |
|
296 | |||
297 | def test_delete(self): |
|
297 | def test_delete(self): | |
298 | cm = self.contents_manager |
|
298 | cm = self.contents_manager | |
@@ -315,12 +315,16 class TestContentsManager(TestCase): | |||||
315 |
|
315 | |||
316 | # copy with unspecified name |
|
316 | # copy with unspecified name | |
317 | copy = cm.copy(path) |
|
317 | copy = cm.copy(path) | |
318 |
self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy |
|
318 | self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb')) | |
319 |
|
319 | |||
320 | # copy with specified name |
|
320 | # copy with specified name | |
321 | copy2 = cm.copy(path, u'å b/copy 2.ipynb') |
|
321 | copy2 = cm.copy(path, u'å b/copy 2.ipynb') | |
322 | self.assertEqual(copy2['name'], u'copy 2.ipynb') |
|
322 | self.assertEqual(copy2['name'], u'copy 2.ipynb') | |
323 | self.assertEqual(copy2['path'], u'å b/copy 2.ipynb') |
|
323 | self.assertEqual(copy2['path'], u'å b/copy 2.ipynb') | |
|
324 | # copy with specified path | |||
|
325 | copy2 = cm.copy(path, u'/') | |||
|
326 | self.assertEqual(copy2['name'], name) | |||
|
327 | self.assertEqual(copy2['path'], name) | |||
324 |
|
328 | |||
325 | def test_trust_notebook(self): |
|
329 | def test_trust_notebook(self): | |
326 | cm = self.contents_manager |
|
330 | cm = self.contents_manager |
General Comments 0
You need to be logged in to leave comments.
Login now