diff --git a/IPython/html/services/contents/manager.py b/IPython/html/services/contents/manager.py index bbd6ede..491401f 100644 --- a/IPython/html/services/contents/manager.py +++ b/IPython/html/services/contents/manager.py @@ -7,6 +7,7 @@ from fnmatch import fnmatch import itertools import json import os +import re from tornado.web import HTTPError @@ -15,6 +16,7 @@ from IPython.nbformat import sign, validate, ValidationError from IPython.nbformat.v4 import new_notebook from IPython.utils.traitlets import Instance, Unicode, List +copy_pat = re.compile(r'\-Copy\d*\.') class ContentsManager(LoggingConfigurable): """Base class for serving files and directories. @@ -188,7 +190,7 @@ class ContentsManager(LoggingConfigurable): """ return path - def increment_filename(self, filename, path=''): + def increment_filename(self, filename, path='', insert=''): """Increment a filename until it is unique. Parameters @@ -206,8 +208,12 @@ class ContentsManager(LoggingConfigurable): path = path.strip('/') basename, ext = os.path.splitext(filename) for i in itertools.count(): - name = u'{basename}{i}{ext}'.format(basename=basename, i=i, - ext=ext) + if i: + insert_i = '{}{}'.format(insert, i) + else: + insert_i = '' + name = u'{basename}{insert}{ext}'.format(basename=basename, + insert=insert_i, ext=ext) if not self.exists(u'{}/{}'.format(path, name)): break return name @@ -244,8 +250,10 @@ class ContentsManager(LoggingConfigurable): else: model.setdefault('type', 'file') + insert = '' if model['type'] == 'directory': untitled = self.untitled_directory + insert = ' ' elif model['type'] == 'notebook': untitled = self.untitled_notebook ext = '.ipynb' @@ -254,7 +262,7 @@ class ContentsManager(LoggingConfigurable): else: raise HTTPError(400, "Unexpected model type: %r" % model['type']) - name = self.increment_filename(untitled + ext, path) + name = self.increment_filename(untitled + ext, path, insert=insert) path = u'{0}/{1}'.format(path, name) return self.new(model, path) @@ -309,9 +317,8 @@ class ContentsManager(LoggingConfigurable): if not to_path: to_path = from_dir if self.dir_exists(to_path): - base, ext = os.path.splitext(from_name) - copy_name = u'{0}-Copy{1}'.format(base, ext) - to_name = self.increment_filename(copy_name, to_path) + name = copy_pat.sub(u'.', from_name) + to_name = self.increment_filename(name, to_path, insert='-Copy') to_path = u'{0}/{1}'.format(to_path, to_name) model = self.save(model, to_path) diff --git a/IPython/html/services/contents/tests/test_contents_api.py b/IPython/html/services/contents/tests/test_contents_api.py index d73a4c2..bda7c77 100644 --- a/IPython/html/services/contents/tests/test_contents_api.py +++ b/IPython/html/services/contents/tests/test_contents_api.py @@ -291,7 +291,7 @@ class APITest(NotebookTestBase): def test_create_untitled(self): resp = self.api.create_untitled(path=u'å b') - self._check_created(resp, u'å b/Untitled0.ipynb') + self._check_created(resp, u'å b/Untitled.ipynb') # Second time resp = self.api.create_untitled(path=u'å b') @@ -299,13 +299,13 @@ class APITest(NotebookTestBase): # And two directories down resp = self.api.create_untitled(path='foo/bar') - self._check_created(resp, 'foo/bar/Untitled0.ipynb') + self._check_created(resp, 'foo/bar/Untitled.ipynb') def test_create_untitled_txt(self): resp = self.api.create_untitled(path='foo/bar', ext='.txt') - self._check_created(resp, 'foo/bar/untitled0.txt', type='file') + self._check_created(resp, 'foo/bar/untitled.txt', type='file') - resp = self.api.read(path='foo/bar/untitled0.txt') + resp = self.api.read(path='foo/bar/untitled.txt') model = resp.json() self.assertEqual(model['type'], 'file') self.assertEqual(model['format'], 'text') @@ -320,15 +320,15 @@ class APITest(NotebookTestBase): def test_mkdir_untitled(self): resp = self.api.mkdir_untitled(path=u'å b') - self._check_created(resp, u'å b/Untitled Folder0', type='directory') + self._check_created(resp, u'å b/Untitled Folder', type='directory') # Second time resp = self.api.mkdir_untitled(path=u'å b') - self._check_created(resp, u'å b/Untitled Folder1', type='directory') + self._check_created(resp, u'å b/Untitled Folder 1', type='directory') # And two directories down resp = self.api.mkdir_untitled(path='foo/bar') - self._check_created(resp, 'foo/bar/Untitled Folder0', type='directory') + self._check_created(resp, 'foo/bar/Untitled Folder', type='directory') def test_mkdir(self): path = u'å b/New ∂ir' @@ -390,15 +390,25 @@ class APITest(NotebookTestBase): self.assertEqual(data['content']['nbformat'], 4) def test_copy(self): - resp = self.api.copy(u'å b/ç d.ipynb', u'unicodé') - self._check_created(resp, u'unicodé/ç d-Copy0.ipynb') + resp = self.api.copy(u'å b/ç d.ipynb', u'å b') + self._check_created(resp, u'å b/ç d-Copy1.ipynb') resp = self.api.copy(u'å b/ç d.ipynb', u'å b') - self._check_created(resp, u'å b/ç d-Copy0.ipynb') - + self._check_created(resp, u'å b/ç d-Copy2.ipynb') + + def test_copy_copy(self): + resp = self.api.copy(u'å b/ç d.ipynb', u'å b') + self._check_created(resp, u'å b/ç d-Copy1.ipynb') + + resp = self.api.copy(u'å b/ç d-Copy1.ipynb', u'å b') + self._check_created(resp, u'å b/ç d-Copy2.ipynb') + def test_copy_path(self): resp = self.api.copy(u'foo/a.ipynb', u'å b') - self._check_created(resp, u'å b/a-Copy0.ipynb') + self._check_created(resp, u'å b/a.ipynb') + + resp = self.api.copy(u'foo/a.ipynb', u'å b') + self._check_created(resp, u'å b/a-Copy1.ipynb') def test_copy_put_400(self): with assert_http_error(400): diff --git a/IPython/html/services/contents/tests/test_manager.py b/IPython/html/services/contents/tests/test_manager.py index be78499..647578c 100644 --- a/IPython/html/services/contents/tests/test_manager.py +++ b/IPython/html/services/contents/tests/test_manager.py @@ -121,8 +121,8 @@ class TestContentsManager(TestCase): self.assertIn('path', model) self.assertIn('type', model) self.assertEqual(model['type'], 'notebook') - self.assertEqual(model['name'], 'Untitled0.ipynb') - self.assertEqual(model['path'], 'Untitled0.ipynb') + self.assertEqual(model['name'], 'Untitled.ipynb') + self.assertEqual(model['path'], 'Untitled.ipynb') # Test in sub-directory model = cm.new_untitled(type='directory') @@ -131,8 +131,8 @@ class TestContentsManager(TestCase): self.assertIn('path', model) self.assertIn('type', model) self.assertEqual(model['type'], 'directory') - self.assertEqual(model['name'], 'Untitled Folder0') - self.assertEqual(model['path'], 'Untitled Folder0') + self.assertEqual(model['name'], 'Untitled Folder') + self.assertEqual(model['path'], 'Untitled Folder') sub_dir = model['path'] model = cm.new_untitled(path=sub_dir) @@ -141,8 +141,8 @@ class TestContentsManager(TestCase): self.assertIn('path', model) self.assertIn('type', model) self.assertEqual(model['type'], 'file') - self.assertEqual(model['name'], 'untitled0') - self.assertEqual(model['path'], '%s/untitled0' % sub_dir) + self.assertEqual(model['name'], 'untitled') + self.assertEqual(model['path'], '%s/untitled' % sub_dir) def test_get(self): cm = self.contents_manager @@ -177,7 +177,7 @@ class TestContentsManager(TestCase): self.assertIn('name', model2) self.assertIn('path', model2) self.assertIn('content', model2) - self.assertEqual(model2['name'], 'Untitled0.ipynb') + self.assertEqual(model2['name'], 'Untitled.ipynb') self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name)) # Test getting directory model @@ -291,8 +291,8 @@ class TestContentsManager(TestCase): assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) - self.assertEqual(model['name'], 'Untitled0.ipynb') - self.assertEqual(model['path'], 'foo/Untitled0.ipynb') + self.assertEqual(model['name'], 'Untitled.ipynb') + self.assertEqual(model['path'], 'foo/Untitled.ipynb') def test_delete(self): cm = self.contents_manager @@ -315,12 +315,16 @@ class TestContentsManager(TestCase): # copy with unspecified name copy = cm.copy(path) - self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy0.ipynb')) + self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb')) # copy with specified name copy2 = cm.copy(path, u'å b/copy 2.ipynb') self.assertEqual(copy2['name'], u'copy 2.ipynb') self.assertEqual(copy2['path'], u'å b/copy 2.ipynb') + # copy with specified path + copy2 = cm.copy(path, u'/') + self.assertEqual(copy2['name'], name) + self.assertEqual(copy2['path'], name) def test_trust_notebook(self): cm = self.contents_manager