From ad3ab9b8c7c107629714db0de4dd2a40350eb4e8 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Mon, 12 Dec 2016 16:10:21 +0000 Subject: [PATCH] Send files to OS trash mechanism on delete This adds a config option, defaulting to use trash, and adds a dependency on the pure-Python 'send2trash' package. Linux, Mac and Windows should all be supported. Alternatively, we could make it default to hard delete (the current behaviour), and let users opt in to trash behaviour. Then Send2Trash could be a soft dependency. This doesn't touch the UI yet, so you still get a confirmation dialog which inaccurately says it will 'permanently delete' the file. If we want to do this, we'll need some way for the contents manager to pass the UI a hint about whether deleting is permanent or not. Closes gh-165 --- notebook/services/contents/filemanager.py | 14 ++++++++++++++ setup.py | 1 + 2 files changed, 15 insertions(+) diff --git a/notebook/services/contents/filemanager.py b/notebook/services/contents/filemanager.py index cd8634313..ce23544ac 100644 --- a/notebook/services/contents/filemanager.py +++ b/notebook/services/contents/filemanager.py @@ -13,6 +13,7 @@ import warnings import mimetypes import nbformat +from send2trash import send2trash from tornado import web from .filecheckpoints import FileCheckpoints @@ -144,6 +145,11 @@ class FileContentsManager(FileManagerMixin, ContentsManager): def _checkpoints_class_default(self): return FileCheckpoints + delete_to_trash = Bool(True, config=True, + help="""If True (default), deleting files will send them to the + platform's trash/recycle bin, where they can be recovered. If False, + deleting files really deletes them.""") + def is_hidden(self, path): """Does the API style path correspond to a hidden directory or file? @@ -464,6 +470,14 @@ class FileContentsManager(FileManagerMixin, ContentsManager): elif not os.path.isfile(os_path): raise web.HTTPError(404, u'File does not exist: %s' % os_path) + if self.delete_to_trash: + self.log.debug("Sending %s to trash", os_path) + # Looking at the code in send2trash, I don't think the errors it + # raises let us distinguish permission errors from other errors in + # code. So for now, just let them all get logged as server errors. + send2trash(os_path) + return + if os.path.isdir(os_path): self.log.debug("Removing directory %s", os_path) with self.perm_to_403(): diff --git a/setup.py b/setup.py index 151918633..1c946855f 100755 --- a/setup.py +++ b/setup.py @@ -152,6 +152,7 @@ install_requires = [ 'nbformat', 'nbconvert', 'ipykernel', # bless IPython kernel for now + 'Send2Trash', ] extras_require = { ':sys_platform != "win32"': ['terminado>=0.3.3'],