##// END OF EJS Templates
support deleting empty directories...
MinRK -
Show More
@@ -357,7 +357,13 b' class FileContentsManager(ContentsManager):'
357 """Delete file by name and path."""
357 """Delete file by name and path."""
358 path = path.strip('/')
358 path = path.strip('/')
359 os_path = self._get_os_path(name, path)
359 os_path = self._get_os_path(name, path)
360 if not os.path.isfile(os_path):
360 rm = os.unlink
361 if os.path.isdir(os_path):
362 listing = os.listdir(os_path)
363 # don't delete non-empty directories (checkpoints dir doesn't count)
364 if listing and listing != ['.ipynb_checkpoints']:
365 raise web.HTTPError(400, u'Directory %s not empty' % os_path)
366 elif not os.path.isfile(os_path):
361 raise web.HTTPError(404, u'File does not exist: %s' % os_path)
367 raise web.HTTPError(404, u'File does not exist: %s' % os_path)
362
368
363 # clear checkpoints
369 # clear checkpoints
@@ -368,8 +374,12 b' class FileContentsManager(ContentsManager):'
368 self.log.debug("Unlinking checkpoint %s", cp_path)
374 self.log.debug("Unlinking checkpoint %s", cp_path)
369 os.unlink(cp_path)
375 os.unlink(cp_path)
370
376
371 self.log.debug("Unlinking file %s", os_path)
377 if os.path.isdir(os_path):
372 os.unlink(os_path)
378 self.log.debug("Removing directory %s", os_path)
379 shutil.rmtree(os_path)
380 else:
381 self.log.debug("Unlinking file %s", os_path)
382 rm(os_path)
373
383
374 def rename(self, old_name, old_path, new_name, new_path):
384 def rename(self, old_name, old_path, new_name, new_path):
375 """Rename a file."""
385 """Rename a file."""
@@ -7,6 +7,8 b' from fnmatch import fnmatch'
7 import itertools
7 import itertools
8 import os
8 import os
9
9
10 from tornado.web import HTTPError
11
10 from IPython.config.configurable import LoggingConfigurable
12 from IPython.config.configurable import LoggingConfigurable
11 from IPython.nbformat import current, sign
13 from IPython.nbformat import current, sign
12 from IPython.utils.traitlets import Instance, Unicode, List
14 from IPython.utils.traitlets import Instance, Unicode, List
@@ -187,6 +189,8 b' class ContentsManager(LoggingConfigurable):'
187 """
189 """
188 path = path.strip('/')
190 path = path.strip('/')
189 model = self.get_model(from_name, path)
191 model = self.get_model(from_name, path)
192 if model['type'] == 'directory':
193 raise HTTPError(400, "Can't copy directories")
190 if not to_name:
194 if not to_name:
191 base, ext = os.path.splitext(from_name)
195 base, ext = os.path.splitext(from_name)
192 copy_name = u'{0}-Copy{1}'.format(base, ext)
196 copy_name = u'{0}-Copy{1}'.format(base, ext)
@@ -69,6 +69,9 b' class API(object):'
69 def upload(self, name, body, path='/'):
69 def upload(self, name, body, path='/'):
70 return self._req('PUT', url_path_join(path, name), body)
70 return self._req('PUT', url_path_join(path, name), body)
71
71
72 def mkdir(self, name, path='/'):
73 return self._req('PUT', url_path_join(path, name), json.dumps({'type': 'directory'}))
74
72 def copy(self, copy_from, copy_to, path='/'):
75 def copy(self, copy_from, copy_to, path='/'):
73 body = json.dumps({'copy_from':copy_from})
76 body = json.dumps({'copy_from':copy_from})
74 return self._req('PUT', url_path_join(path, copy_to), body)
77 return self._req('PUT', url_path_join(path, copy_to), body)
@@ -299,9 +302,7 b' class APITest(NotebookTestBase):'
299 self._check_created(resp, u'Upload tést.ipynb', u'å b')
302 self._check_created(resp, u'Upload tést.ipynb', u'å b')
300
303
301 def test_mkdir(self):
304 def test_mkdir(self):
302 model = {'type': 'directory'}
305 resp = self.api.mkdir(u'New ∂ir', path=u'å b')
303 resp = self.api.upload(u'New ∂ir', path=u'å b',
304 body=json.dumps(model))
305 self._check_created(resp, u'New ∂ir', u'å b', type='directory')
306 self._check_created(resp, u'New ∂ir', u'å b', type='directory')
306
307
307 def test_upload_txt(self):
308 def test_upload_txt(self):
@@ -362,6 +363,11 b' class APITest(NotebookTestBase):'
362 resp = self.api.copy(u'ç d.ipynb', u'cøpy.ipynb', path=u'å b')
363 resp = self.api.copy(u'ç d.ipynb', u'cøpy.ipynb', path=u'å b')
363 self._check_created(resp, u'cøpy.ipynb', u'å b')
364 self._check_created(resp, u'cøpy.ipynb', u'å b')
364
365
366 def test_copy_dir_400(self):
367 # can't copy directories
368 with assert_http_error(400):
369 resp = self.api.copy(u'å b', u'å c')
370
365 def test_delete(self):
371 def test_delete(self):
366 for d, name in self.dirs_nbs:
372 for d, name in self.dirs_nbs:
367 resp = self.api.delete('%s.ipynb' % name, d)
373 resp = self.api.delete('%s.ipynb' % name, d)
@@ -371,6 +377,20 b' class APITest(NotebookTestBase):'
371 nbs = notebooks_only(self.api.list(d).json())
377 nbs = notebooks_only(self.api.list(d).json())
372 self.assertEqual(len(nbs), 0)
378 self.assertEqual(len(nbs), 0)
373
379
380 def test_delete_dirs(self):
381 # depth-first delete everything, so we don't try to delete empty directories
382 for name in sorted(self.dirs + ['/'], key=len, reverse=True):
383 listing = self.api.list(name).json()['content']
384 for model in listing:
385 self.api.delete(model['name'], model['path'])
386 listing = self.api.list('/').json()['content']
387 self.assertEqual(listing, [])
388
389 def test_delete_non_empty_dir(self):
390 """delete non-empty dir raises 400"""
391 with assert_http_error(400):
392 self.api.delete(u'å b')
393
374 def test_rename(self):
394 def test_rename(self):
375 resp = self.api.rename('a.ipynb', 'foo', 'z.ipynb')
395 resp = self.api.rename('a.ipynb', 'foo', 'z.ipynb')
376 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
396 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
General Comments 0
You need to be logged in to leave comments. Login now