Adding optional parameter to shutil.rmtree to not delete root.

I've been finding that a common scenario is where I want to remove everything in a directory, but leave the (empty) root directory behind, not removing it. So for example, if I have a directory C:\foo and it contains subdirectory C:\foo\bar and file C:\foo\myfile.txt, and I want to remove the subdirectory (and everything in it) and file, leaving only C:\foo behind. (This is useful e.g. when the root directory has special permissions, so it wouldn't be so simple to remove it and recreate it again.) A number of ways to do this have been offered here: http://stackoverflow.com/questions/185936/delete-folder-contents-in-python But it'd be simpler if there were an optional parameter added to shutil.rmtree, called removeroot. It would default to true so as to not break any backward compatibility. If it's set to false, then it leaves the root directory in place. Thanks, Nick

On Thu, Aug 25, 2016 at 2:29 AM, Nick Jacobson via Python-ideas <python-ideas@python.org> wrote:
Here's a Windows workaround that clears the delete disposition after rmtree 'deletes' the directory. A Windows file or directory absolutely cannot be unlinked while there are handle or kernel references to it, and a handle with DELETE access can set and unset the delete disposition. This used to require the system call NtSetInformationFile, but Vista added SetFileInformationByHandle to the Windows API. import contextlib import ctypes import _winapi kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) kernel32.SetFileInformationByHandle # Vista minimum (NT 6.0+) DELETE = 0x00010000 SHARE_ALL = 7 OPEN_EXISTING = 3 BACKUP = 0x02000000 FileDispositionInfo = 4 @contextlib.contextmanager def protect_file(path): hFile = _winapi.CreateFile(path, DELETE, SHARE_ALL, 0, OPEN_EXISTING, BACKUP, 0) try: yield if not kernel32.SetFileInformationByHandle( hFile, FileDispositionInfo, (ctypes.c_ulong * 1)(0), 4): raise ctypes.WinError(ctypes.get_last_error()) finally: kernel32.CloseHandle(hFile) For example: >>> os.listdir('test') ['dir1', 'dir2', 'file'] >>> with protect_file('test'): ... shutil.rmtree('test') ... >>> os.listdir('test') [] Another example: >>> open('file', 'w').close() >>> with protect_file('file'): ... os.remove('file') ... >>> os.path.exists('file') True

On 25 August 2016 at 23:14, Victor Stinner <victor.stinner@gmail.com> wrote:
Maybe add a different function rather add a flag? Something like shutil.remove_dir_files().
The typical Python term for the concept would be "clear", giving shutil.clear_dir(). Like the ".clear()" method on containers, it would delete the contents, but leave the container itself alone. rmtree() could then be a thin wrapper around clear_dir() that also deletes the base directory. Alternatively, if we wanted to stick with the "rm" prefix, we could use "shutil.rmcontents()" as the name. The main downside I'd see to that is that I'd half expect it to work on files as well (truncating them to a length of zero), while clear_dir() is unambiguous. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

+1 for clear_dir() I agree that there's no other obvious meaning that it could have. On Thursday, August 25, 2016 9:44 AM, Nick Coghlan <ncoghlan@gmail.com> wrote: On 25 August 2016 at 23:14, Victor Stinner <victor.stinner@gmail.com> wrote:
Maybe add a different function rather add a flag? Something like shutil.remove_dir_files().
The typical Python term for the concept would be "clear", giving shutil.clear_dir(). Like the ".clear()" method on containers, it would delete the contents, but leave the container itself alone. rmtree() could then be a thin wrapper around clear_dir() that also deletes the base directory. Alternatively, if we wanted to stick with the "rm" prefix, we could use "shutil.rmcontents()" as the name. The main downside I'd see to that is that I'd half expect it to work on files as well (truncating them to a length of zero), while clear_dir() is unambiguous. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Thu, Aug 25, 2016 at 2:29 AM, Nick Jacobson via Python-ideas <python-ideas@python.org> wrote:
Here's a Windows workaround that clears the delete disposition after rmtree 'deletes' the directory. A Windows file or directory absolutely cannot be unlinked while there are handle or kernel references to it, and a handle with DELETE access can set and unset the delete disposition. This used to require the system call NtSetInformationFile, but Vista added SetFileInformationByHandle to the Windows API. import contextlib import ctypes import _winapi kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) kernel32.SetFileInformationByHandle # Vista minimum (NT 6.0+) DELETE = 0x00010000 SHARE_ALL = 7 OPEN_EXISTING = 3 BACKUP = 0x02000000 FileDispositionInfo = 4 @contextlib.contextmanager def protect_file(path): hFile = _winapi.CreateFile(path, DELETE, SHARE_ALL, 0, OPEN_EXISTING, BACKUP, 0) try: yield if not kernel32.SetFileInformationByHandle( hFile, FileDispositionInfo, (ctypes.c_ulong * 1)(0), 4): raise ctypes.WinError(ctypes.get_last_error()) finally: kernel32.CloseHandle(hFile) For example: >>> os.listdir('test') ['dir1', 'dir2', 'file'] >>> with protect_file('test'): ... shutil.rmtree('test') ... >>> os.listdir('test') [] Another example: >>> open('file', 'w').close() >>> with protect_file('file'): ... os.remove('file') ... >>> os.path.exists('file') True

On 25 August 2016 at 23:14, Victor Stinner <victor.stinner@gmail.com> wrote:
Maybe add a different function rather add a flag? Something like shutil.remove_dir_files().
The typical Python term for the concept would be "clear", giving shutil.clear_dir(). Like the ".clear()" method on containers, it would delete the contents, but leave the container itself alone. rmtree() could then be a thin wrapper around clear_dir() that also deletes the base directory. Alternatively, if we wanted to stick with the "rm" prefix, we could use "shutil.rmcontents()" as the name. The main downside I'd see to that is that I'd half expect it to work on files as well (truncating them to a length of zero), while clear_dir() is unambiguous. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

+1 for clear_dir() I agree that there's no other obvious meaning that it could have. On Thursday, August 25, 2016 9:44 AM, Nick Coghlan <ncoghlan@gmail.com> wrote: On 25 August 2016 at 23:14, Victor Stinner <victor.stinner@gmail.com> wrote:
Maybe add a different function rather add a flag? Something like shutil.remove_dir_files().
The typical Python term for the concept would be "clear", giving shutil.clear_dir(). Like the ".clear()" method on containers, it would delete the contents, but leave the container itself alone. rmtree() could then be a thin wrapper around clear_dir() that also deletes the base directory. Alternatively, if we wanted to stick with the "rm" prefix, we could use "shutil.rmcontents()" as the name. The main downside I'd see to that is that I'd half expect it to work on files as well (truncating them to a length of zero), while clear_dir() is unambiguous. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (5)
-
Alexander Belopolsky
-
eryk sun
-
Nick Coghlan
-
Nick Jacobson
-
Victor Stinner