shutil.rmtree fails on Windows with 'Access is denied'

Question:

In Python, when running shutil.rmtree over a folder that contains a read-only file, the following exception is printed:

 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:Python26libshutil.py", line 221, in rmtree
   onerror(os.remove, fullname, sys.exc_info())
 File "C:Python26libshutil.py", line 219, in rmtree
   os.remove(fullname)
WindowsError: [Error 5] Access is denied: 'build\tcl\tcl8.5\msgs\af.msg'

Looking in File Properties dialog I noticed that af.msg file is set to be read-only.

So the question is: what is the simplest workaround/fix to get around this problem – given that my intention is to do an equivalent of rm -rf build/ but on Windows? (without having to use third-party tools like unxutils or cygwin – as this code is targeted to be run on a bare Windows install with Python 2.6 w/ PyWin32 installed)

Asked By: Sridhar Ratnakumar

||

Answers:

Check this question out: What user do python scripts run as in windows?

Apparently the answer is to change the file/folder to not be read-only and then remove it.

Here’s onerror() handler from pathutils.py mentioned by @Sridhar Ratnakumar in comments:

def onerror(func, path, exc_info):
    """
    Error handler for ``shutil.rmtree``.

    If the error is due to an access error (read only file)
    it attempts to add write permission and then retries.

    If the error is for another reason it re-raises the error.
    
    Usage : ``shutil.rmtree(path, onerror=onerror)``
    """
    import stat
    # Is the error an access error?
    if not os.access(path, os.W_OK):
        os.chmod(path, stat.S_IWUSR)
        func(path)
    else:
        raise
Answered By: Justin Peel

I’d say implement your own rmtree with os.walk that ensures access by using os.chmod on each file before trying to delete it.

Something like this (untested):

import os
import stat

def rmtree(top):
    for root, dirs, files in os.walk(top, topdown=False):
        for name in files:
            filename = os.path.join(root, name)
            os.chmod(filename, stat.S_IWUSR)
            os.remove(filename)
        for name in dirs:
            os.rmdir(os.path.join(root, name))
    os.rmdir(top)      
Answered By: Epcylon

Well, the marked solution did not work for me… did this instead:

os.system('rmdir /S /Q "{}"'.format(directory))
Answered By: AlexeiOst
shutil.rmtree(path,ignore_errors=False,onerror=errorRemoveReadonly) 
def errorRemoveReadonly(func, path, exc):
    excvalue = exc[1]
    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
        # change the file to be readable,writable,executable: 0777
        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)  
        # retry
        func(path)
    else:
        raiseenter code here

If ignore_errors is set, errors are ignored; otherwise, if onerror
is set, it is called to handle the error with arguments (func,
path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
path is the argument to that function that caused it to fail; and
exc_info is a tuple returned by sys.exc_info(). If ignore_errors
is false and onerror is None, an exception is raised.enter code here

Answered By: RY_ Zheng

If you run your script using cygwin, you can use subprocess.call

from subprocess import call
call("rm -rf build/", shell=True)

Of course it only works inside the cygwin/bash emulator.

Answered By: besil