views:

863

answers:

2

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

 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 221, in rmtree
   onerror(os.remove, fullname, sys.exc_info())
 File "C:\Python26\lib\shutil.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)

+5  A: 

Check this question out:

http://stackoverflow.com/questions/1213706/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
    if not os.access(path, os.W_OK):
        # Is the error an access error ?
        os.chmod(path, stat.S_IWUSR)
        func(path)
    else:
        raise
Justin Peel
Heh. I just discovered the `onerror` handler at http://www.voidspace.org.uk/downloads/pathutils.py
Sridhar Ratnakumar
.. discovered that via http://trac.pythonpaste.org/pythonpaste/ticket/359
Sridhar Ratnakumar
+1  A: 

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):

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))            
Epcylon
This is nearly right - Windows only supports `stat.S_IWRITE` (which is what you want anyway) - http://docs.python.org/library/os.html#os.chmod
Daniel G
I did test that `os.chmod(filename, stat.S_IWUSR)` removed the read-only flag, so it does work on WinXP. And considering this is what the docs say about `stat.S_IWRITE`: "Unix V7 synonym for S_IWUSR" (http://docs.python.org/library/stat.html#stat.S_IWRITE), I'm thinking my code is right anyway.
Epcylon