I've been trying to find an answer pertaining to how error handling and propagation are handled when using the shutil.rmtree
command inside a try/except statement. I've tried to find an example showing such a practice, but have not been able to find one. So, I'm left wondering if it's even possible. When reading the documentation for the command here I see that it states:
...to remove a directory tree on Windows where some of the files have their read-only bit set. It uses the onerror callback to clear the readonly bit and reattempt the remove. Any subsequent failure will propagate.
Doesn't this mean that if an error occurs within the onerror
function after the initial try to execute the onerror
function, in other words the onerror
function doesn't fix the error and it occurs when the shutil.rmtree
tries to run again, that the error will be raised in the main routine (where the try/except statement is)? Is that what it means by "Any subsequent failure will propagate"?
I'm trying to make sure that if for whatever reason any of the shutil.rmtree
commands fail that the failure will be caught and the code will still continue. I have another script that run after this script to check/correct errors in batches, which is why I'm not directly handling the errors. I just need to make sure this script runs all the way through. Will the code below accomplish this as it's written or is there anything that I need to change to accomplish this?
import shutil
import os
import stat
def remove_readonly(func, path, excinfo):
os.chmod(path, stat.S_IWRITE)
func(path)
try:
#os.chmod is used to turn off Read-Only attribute
os.chmod("Q:/-----.vbs", stat.S_IWRITE)
#os.remove is used to remove individual files
os.remove("Q:/-----.vbs")
except:
pass
#shutil.rmtree is used to remove entire directories
#remove traces of file
try:
shutil.rmtree("Q:/FolderToRemove1", onerror=remove_readonly)
except:
pass
try:
shutil.rmtree("Q:/FolderToRemove2", onerror=remove_readonly)
except:
pass
try:
shutil.rmtree("Q:/FolderToRemove3", onerror=remove_readonly)
except:
pass
try:
shutil.rmtree("Q:/FolderToRemove4", onerror=remove_readonly)
except:
pass
try:
shutil.rmtree("Q:/FolderToRemove5", onerror=remove_readonly)
except:
pass
try:
shutil.rmtree("C:/Users/mhill/Desktop/screenshots", onerror=remove_readonly)
except:
pass
The documentation states that exceptions raised by onerror will not be caught, thus you must handle it yourself.
In terms of your sample code, a blank except
is generally poor design. In particular, it will also catch a KeyboardInterrupt
which is surely not your intent.
Rather, something like this:
for f in ["Q:/FolderToRemove1", "Q:/FolderToRemove2",
"Q:/FolderToRemove3", "Q:/FolderToRemove4",
"Q:/FolderToRemove5", "C:/Users/mhill/Desktop/screenshots"]:
try:
shutil.rmtree(f, onerror=remove_readonly)
except Exception:
pass