This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: shutil.rmtree fails on readonly files in Windows, onerror not called
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.9
process
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: eryksun, homerun4711
Priority: normal Keywords:

Created on 2021-03-29 12:52 by homerun4711, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (4)
msg389697 - (view) Author: Walter White (homerun4711) Date: 2021-03-29 12:52
shutil.rmtree fails on readonly files in Windows.

Usually people are using the onerror callback to handle file permissions and retry, but that is not possible in this case because it is not triggerd.

onerror is only triggered if a OSError is found. 
In my case the unlink throws a PermissionError

Code shutil.rmdir():

try:
    os.unlink(fullname)
except OSError:
    onerror(os.unlink, fullname, sys.exc_info())


Traceback:


Traceback (most recent call last):

  File "c:\Users\user\test.py", line 121, in <module>
    shutil.rmtree(shutil.rmtree(working_dir),
  File "C:\python-3.9.1.amd64\lib\shutil.py", line 740, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "C:\python-3.9.1.amd64\lib\shutil.py", line 613, in _rmtree_unsafe
    _rmtree_unsafe(fullname, onerror)
  File "C:\python-3.9.1.amd64\lib\shutil.py", line 613, in _rmtree_unsafe
    _rmtree_unsafe(fullname, onerror)
  File "C:\python-3.9.1.amd64\lib\shutil.py", line 618, in _rmtree_unsafe
    onerror(os.unlink, fullname, sys.exc_info())
  File "C:\python-3.9.1.amd64\lib\shutil.py", line 616, in _rmtree_unsafe
    os.unlink(fullname)
	
PermissionError: [WinError 5] Access denied: 'C:\\Users\\user\\somefile.txt'

os.stat:

st_mode=33060
st_ino=34621422136837665
st_dev=3929268297
st_nlink=1
st_uid=0
st_gid=0
msg389698 - (view) Author: Walter White (homerun4711) Date: 2021-03-29 13:01
Just saw:

    class PermissionError(OSError)
msg389702 - (view) Author: Walter White (homerun4711) Date: 2021-03-29 13:34
I tried to add a try/except that handles a PermissionError to
my onerror function. But that did not work.

The doc on rmtree states 

Exceptions raised by onerror will not be caught.

Does this mean I can't use try/exept inside of onerror?
msg389724 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2021-03-29 18:50
> The doc on rmtree states 
> Exceptions raised by onerror will not be caught.
> Does this mean I can't use try/exept inside of onerro

rmtree() does not call onerror() in a try/except statement. An exception raised in onerror() will propagate to the scope that called rmtree().

The documentation has an example onerror() handler for Windows readonly files:

    import os, stat
    import shutil

    def remove_readonly(func, path, _):
        "Clear the readonly bit and reattempt the removal"
        os.chmod(path, stat.S_IWRITE)
        func(path)

    shutil.rmtree(directory, onerror=remove_readonly)

I'd check whether the exception and function are expected values. For example:

    import os, stat
    import shutil

    def remove_readonly(func, path, exc_info):
        "Clear the readonly bit and reattempt the removal"
        # ERROR_ACCESS_DENIED = 5
        if func not in (os.unlink, os.rmdir) or exc_info[1].winerror != 5:
            raise exc_info[1]
        os.chmod(path, stat.S_IWRITE)
        func(path)

    shutil.rmtree(directory, onerror=remove_readonly)
History
Date User Action Args
2022-04-11 14:59:43adminsetgithub: 87823
2021-03-29 18:50:25eryksunsettype: crash -> behavior

messages: + msg389724
nosy: + eryksun
2021-03-29 17:02:31homerun4711setstatus: open -> closed
2021-03-29 13:34:51homerun4711setstatus: closed -> open

messages: + msg389702
2021-03-29 13:01:44homerun4711setstatus: open -> closed
stage: resolved
2021-03-29 13:01:38homerun4711setmessages: + msg389698
2021-03-29 12:52:29homerun4711create