@@ -119,6 +119,16 @@ def _checkpoints_class_default(self):
119119 deleting files really deletes them.""" ,
120120 )
121121
122+ always_delete_dir = Bool (
123+ False ,
124+ config = True ,
125+ help = """If True, deleting non-empty directory will always be allowed.
126+ WARNING this may result in files being definitely removed; e.g. on Windows
127+ if the data size is too big for the trash/recycle bin they will be really
128+ deleted. If False (default), non-empty directory will be send to trash only
129+ if safe. And if ``delete_to_trash`` is True, they won't be deleted.""" ,
130+ )
131+
122132 @default ("files_handler_class" )
123133 def _files_handler_class_default (self ):
124134 return AuthenticatedFileHandler
@@ -331,7 +341,10 @@ def _file_model(self, path, content=True, format=None):
331341 if content :
332342 content , format = self ._read_file (os_path , format )
333343 if model ["mimetype" ] is None :
334- default_mime = {"text" : "text/plain" , "base64" : "application/octet-stream" }[format ]
344+ default_mime = {
345+ "text" : "text/plain" ,
346+ "base64" : "application/octet-stream" ,
347+ }[format ]
335348 model ["mimetype" ] = default_mime
336349
337350 model .update (
@@ -391,7 +404,9 @@ def get(self, path, content=True, type=None, format=None):
391404 if os .path .isdir (os_path ):
392405 if type not in (None , "directory" ):
393406 raise web .HTTPError (
394- 400 , u"%s is a directory, not a %s" % (path , type ), reason = "bad type"
407+ 400 ,
408+ u"%s is a directory, not a %s" % (path , type ),
409+ reason = "bad type" ,
395410 )
396411 model = self ._dir_model (path , content = content )
397412 elif type == "notebook" or (type is None and path .endswith (".ipynb" )):
@@ -494,7 +509,7 @@ def is_non_empty_dir(os_path):
494509 return False
495510
496511 if self .delete_to_trash :
497- if sys .platform == "win32" and is_non_empty_dir (os_path ):
512+ if not self . always_delete_dir and sys .platform == "win32" and is_non_empty_dir (os_path ):
498513 # send2trash can really delete files on Windows, so disallow
499514 # deleting non-empty files. See Github issue 3631.
500515 raise web .HTTPError (400 , u"Directory %s not empty" % os_path )
@@ -507,12 +522,13 @@ def is_non_empty_dir(os_path):
507522 return
508523 else :
509524 self .log .warning (
510- "Skipping trash for %s, on different device " "to home directory" , os_path
525+ "Skipping trash for %s, on different device " "to home directory" ,
526+ os_path ,
511527 )
512528
513529 if os .path .isdir (os_path ):
514530 # Don't permanently delete non-empty directories.
515- if is_non_empty_dir (os_path ):
531+ if not self . always_delete_dir and is_non_empty_dir (os_path ):
516532 raise web .HTTPError (400 , u"Directory %s not empty" % os_path )
517533 self .log .debug ("Removing directory %s" , os_path )
518534 with self .perm_to_403 ():
@@ -649,7 +665,10 @@ async def _file_model(self, path, content=True, format=None):
649665 if content :
650666 content , format = await self ._read_file (os_path , format )
651667 if model ["mimetype" ] is None :
652- default_mime = {"text" : "text/plain" , "base64" : "application/octet-stream" }[format ]
668+ default_mime = {
669+ "text" : "text/plain" ,
670+ "base64" : "application/octet-stream" ,
671+ }[format ]
653672 model ["mimetype" ] = default_mime
654673
655674 model .update (
@@ -709,7 +728,9 @@ async def get(self, path, content=True, type=None, format=None):
709728 if os .path .isdir (os_path ):
710729 if type not in (None , "directory" ):
711730 raise web .HTTPError (
712- 400 , u"%s is a directory, not a %s" % (path , type ), reason = "bad type"
731+ 400 ,
732+ u"%s is a directory, not a %s" % (path , type ),
733+ reason = "bad type" ,
713734 )
714735 model = await self ._dir_model (path , content = content )
715736 elif type == "notebook" or (type is None and path .endswith (".ipynb" )):
@@ -813,7 +834,11 @@ async def is_non_empty_dir(os_path):
813834 return False
814835
815836 if self .delete_to_trash :
816- if sys .platform == "win32" and await is_non_empty_dir (os_path ):
837+ if (
838+ not self .always_delete_dir
839+ and sys .platform == "win32"
840+ and await is_non_empty_dir (os_path )
841+ ):
817842 # send2trash can really delete files on Windows, so disallow
818843 # deleting non-empty files. See Github issue 3631.
819844 raise web .HTTPError (400 , u"Directory %s not empty" % os_path )
@@ -826,12 +851,13 @@ async def is_non_empty_dir(os_path):
826851 return
827852 else :
828853 self .log .warning (
829- "Skipping trash for %s, on different device " "to home directory" , os_path
854+ "Skipping trash for %s, on different device " "to home directory" ,
855+ os_path ,
830856 )
831857
832858 if os .path .isdir (os_path ):
833859 # Don't permanently delete non-empty directories.
834- if await is_non_empty_dir (os_path ):
860+ if not self . always_delete_dir and await is_non_empty_dir (os_path ):
835861 raise web .HTTPError (400 , u"Directory %s not empty" % os_path )
836862 self .log .debug ("Removing directory %s" , os_path )
837863 with self .perm_to_403 ():
0 commit comments