7

I'm using the following code to delete a directory containing a git repo:

import errno
import os
import stat
import shutil


def clear_dir(path):
    shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)


def handle_remove_readonly(func, path, exc):
  excvalue = exc[1]
  if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
      os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
      func(path)
  else:
      raise

This code should deal well with read-only files. I can delete the directory/folder from Windows Explorer, but when I run the following code:

if __name__ == '__main__':
    clear_dir(r'c:\path\to\ci-monitor')

I get the following error:

  File "C:\Users\m45914\code\ci-monitor\utils\filehandling.py", line 8, in clear_dir                              
    shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly)                                      
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 488, in rmtree                
    return _rmtree_unsafe(path, onerror)                                                                          
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 378, in _rmtree_unsafe        
    _rmtree_unsafe(fullname, onerror)                                                                             
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 378, in _rmtree_unsafe        
    _rmtree_unsafe(fullname, onerror)                                                                             
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 378, in _rmtree_unsafe        
    _rmtree_unsafe(fullname, onerror)                                                                             
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 378, in _rmtree_unsafe        
    _rmtree_unsafe(fullname, onerror)                                                                             
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 383, in _rmtree_unsafe        
    onerror(os.unlink, fullname, sys.exc_info())                                                                  
  File "C:\Users\m45914\AppData\Local\Programs\Python\Python35\lib\shutil.py", line 381, in _rmtree_unsafe        
    os.unlink(fullname)                                                                                           
PermissionError: [WinError 5] Access is denied: 'scratch\\repos\\ci-monitor\\.git\\objects\\pack\\pack-83e55c6964d
21e8be0afb2cbccd887eae3e32bf4.idx'                                                                                

I've tried running the script as administrator (no change.)

The directory being deleted is a git repo, and I am periodically cloning, checking and deleting it. The checks are to make sure there are no unmerged release and hotfix branches in the repo.

Anyone got any ideas?

Nic
  • 1,518
  • 12
  • 26
  • Why do you assume Access denied is caused by a file being readonly? There are many things that would cause an access violation to be thrown, not least of which are files owned by another user. Permissions are significantly different on windows and a simple `chmod` is rarely sufficient. Chmod's docs say `Although Windows supports chmod(), you can only set the file's read-only flag with it (via the stat.S_IWRITE and stat.S_IREAD constants or a corresponding integer value). All other bits are ignored.` Try [ICacls](https://technet.microsoft.com/en-us/library/cc753525(v=ws.11).aspx) instead – Basic Sep 19 '16 at 06:59
  • Not sure but it looks like you're being too restrictive with your function name check's – jthill Sep 19 '16 at 22:12
  • Possible duplicate of [shutil.rmtree fails on Windows with 'Access is denied'](https://stackoverflow.com/questions/2656322/shutil-rmtree-fails-on-windows-with-access-is-denied) – Ocaso Protal Aug 30 '17 at 09:35
  • Does this answer your question? [How to remove git repository, in python, on windows](https://stackoverflow.com/questions/58878089/how-to-remove-git-repository-in-python-on-windows) – tejasvi88 Aug 09 '21 at 02:05

2 Answers2

2

If that file is being used by another process then it would not be possible to delete it. cross check it by using 'unlocker' OR any other similar software.

saurabh baid
  • 1,819
  • 1
  • 14
  • 26
  • 2
    Thanks for your answer, but if that was the case I would not be able to delete it in Windows Explorer – Nic Sep 19 '16 at 06:58
  • 1
    Did you try solution mentioned here. It says `rmtree` has issues in deleting readonly files. It also suggest a solution for that. http://stackoverflow.com/questions/1213706/what-user-do-python-scripts-run-as-in-windows – saurabh baid Sep 19 '16 at 07:13
  • Thanks Saurabh, my code is adapted from the post you mention – Nic Sep 19 '16 at 07:31
  • 1
    If it's a sharing issue, `ERROR_ACCESS_DENIED` (5) implies that a process has the file memory-mapped (executable or data), and you would at least be able to rename the file to another directory on the same volume. Most sharing problems are instead due to basic access sharing (e.g. `FILE_SHARE_DELETE`) and show up as `ERROR_SHARING_VIOLATION` (32), which isn't the case here. – Eryk Sun Sep 19 '16 at 12:59
  • 2
    Another possibility is that the file was opened with `FILE_SHARE_DELETE` and then deleted without closing the handle. Windows doesn't unlink a file from a directory until all references are closed. If a file is currently 'deleted' (that's just a flag on the file control block), it's not possible to open a new handle for any access. Deleting a file requires opening a handle with `DELETE` access, so `DeleteFile` will fail with `ERROR_ACCESS_DENIED`. The parent directory also can't be deleted until the file gets unlinked. – Eryk Sun Sep 19 '16 at 13:10
2

I faced with the same issue. I was able to resolve it by adding os.unlink to the list of funcs:

if func in (os.rmdir, os.remove, os.unlink) and excvalue.errno == errno.EACCES:
C. Fennell
  • 992
  • 12
  • 20