shutil.rmtree fails on Windows with 'Access is denied' [duplicate]
Asked Answered
R

5

94

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)

Reshape answered 16/4, 2010 at 21:57 Comment(3)
shutil.rmtree uses os.remove to remove files. os.remove removes read-only files just fine (at least on Unix). os.remove can't remove file on Windows if it is in use.Aymara
Related: How to avoid “WindowsError: [Error 5] Access is denied”Whereupon
As I experienced, perhaps, this error will be appeared if the directory is open and you run the the code and is related to removing process, not the creation step.Rockbound
O
119

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
Onstage answered 16/4, 2010 at 22:21 Comment(5)
Heh. I just discovered the onerror handler at voidspace.org.uk/downloads/pathutils.pyReshape
.. discovered that via trac.pythonpaste.org/pythonpaste/ticket/359Reshape
Even though the comments for this answer state 'change the file/folder to not be read-only', I still received access denied on read-only folders. This implementation worked, though.Kistler
A word of warning to those copy-pasting this function as is, move the import stat out of the function. I was receiving RuntimeError: sys.meta_path must be a list of import hooks when I'd left the import within the function AND the function was within the __del__ method of a class.Inexhaustible
The "else raise" part of the solution will not raise the exception. Coming from the Python documentation: "Exceptions raised by onerror will not be caught." docs.python.org/2/library/shutil.html#shutil.rmtreeAccountable
A
40

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)      
Aqaba answered 16/4, 2010 at 22:22 Comment(8)
This is nearly right - Windows only supports stat.S_IWRITE (which is what you want anyway) - docs.python.org/library/os.html#os.chmodUnfeeling
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" (docs.python.org/library/stat.html#stat.S_IWRITE), I'm thinking my code is right anyway.Aqaba
Great, with file paths that are too long this seems like the only way. A recommendation to commit to or change shutil.rmtree perhaps.Humbert
This works with stat.S_IWRITE in python 2.7 in windows 10 for read-only files.Dagenham
Nice, but fails in presence of nested subfolders ... unless you recourse deletion by replacing "os.rmdir(os.path.join(root, name))" with "rmtree(os.path.join(root, name))"Shadow
Have you tried it and gotten an error? The code walks the tree from the bottom up, so all directories are empty by the time we run os.rmdir on them, assuming everything else works.Aqaba
Be very careful using this function. This function goes to every subfolder and delete the files in there.Differentiable
Maryam Bahrami: Yes, that is the purpose of the rmtree function. If this is not what you want, why are you using it?Aqaba
O
26

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

os.system('rmdir /S /Q "{}"'.format(directory))
Ooze answered 9/6, 2011 at 21:2 Comment(2)
This removed the directory itself. Can you please tell how to remove all dir and files inside a directory? For example if I give path : myproject/dir1/ then it removes dir1 but I want to delete everything which is under dir1.Gravestone
Personally, I find it easier just to delete the directory and recreate it (although you do lose the timestamp that way)Instrumentalism
B
2
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

Brelje answered 12/2, 2015 at 11:53 Comment(0)
V
-6

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.

Vernitavernoleninsk answered 20/11, 2015 at 10:2 Comment(7)
Calling rm -rf on Windows? I don't think so.Hillie
Very strange. I use a unix-like console emulator for Windows (cmder). The subprocess.call approach works when I run the script from that console, but not if I run it from the default "Command Prompt"Vaporize
Have you tried before downvoting? I confirm it works under WindowsVernitavernoleninsk
@besil, yes, call('rm -rf "C:\\Temp\\tmp7cm15k\\"', shell=True) results in 'rm' is not recognized as an internal or external command, operable program or batch file.Hillie
mh, I think it works for me because I use Cygwin as terminal emulator instead of command promptVernitavernoleninsk
@besil: qualify your suggestion to running under cygwin or some other windows-ported unix shell.Ravens
@RichardJessop just use Cygwin instead of Windows Command Prompt for launching your python script. Cygwin has 'rm' and other unix utilities in the path, so when the py script do 'call system rm', rm is effectively foundVernitavernoleninsk

© 2022 - 2024 — McMap. All rights reserved.