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