Shutl.rmtree does not work with Windows library

So, I am creating a simple script that supports some documents on my second hard drive (you never know what might happen!). So, I used the function shutil.copytreeto replicate my data on the second disk. It works great and that is not a problem.

I use the function shutil.rmtreeto delete the tree if the destination already exists. I will show you my code:

import shutil
import os

def overwrite(src, dest):
    if(not os.path.exists(src)):
        print(src, "does not exist, so nothing may be copied.")
        return

    if(os.path.exists(dest)):
        shutil.rmtree(dest)

    shutil.copytree(src, dest)
    print(dest, "overwritten with data from", src)
    print("")

overwrite(r"C:\Users\Centurion\Dropbox\Documents", r"D:\Backup\Dropbox Documents")
overwrite(r"C:\Users\Centurion\Pictures", r"D:\Backup\All Pictures")

print("Press ENTER to continue...")
input()

As you can see, a simple script. Now when I run the script for the first time, everything is fine. Pictures and documents are copied to my disk D:. However, when I run the second time, this is my conclusion:

C:\Users\Centurion\Programming\Python>python cpdocsnpics.py
D:\Backup\Dropbox Documents overwritten with data from C:\Users\Centurion\Dropbox\Documents

Traceback (most recent call last):
  File "cpdocsnpics.py", line 17, in <module>
    overwrite(r"C:\Users\Centurion\Pictures", r"D:\Backup\All Pictures")
  File "cpdocsnpics.py", line 10, in overwrite
    shutil.rmtree(dest)
  File "C:\Python34\lib\shutil.py", line 477, in rmtree
    return _rmtree_unsafe(path, onerror)
  File "C:\Python34\lib\shutil.py", line 376, in _rmtree_unsafe
    onerror(os.rmdir, path, sys.exc_info())
  File "C:\Python34\lib\shutil.py", line 374, in _rmtree_unsafe
    os.rmdir(path)
PermissionError: [WinError 5] Access is denied: 'D:\\Backup\\All Pictures'

Pictures ; , - , .

?

+4
2

. /dirs readonly. "dest" , rmtree . , "", , "dest" ( ), . , . "" , onerror shutil.rmtree. , , :

def readonly_handler(func, path, execinfo): 
    os.chmod(path, 128) #or os.chmod(path, stat.S_IWRITE) from "stat" module
    func(path)

python, onerror , : function, path excinfo. .

def overwrite(src, dest):
    if(not os.path.exists(src)):
        print(src, "does not exist, so nothing may be copied.")
        return

    if(os.path.exists(dest)):  
        shutil.rmtree(dest, onerror=readonly_handler)

    shutil.copytree(src, dest)
    print(dest, "overwritten with data from", src)
    print("")

, , , , !

: (Python Windows) shutil.rmtree , , Python 3.5 (. issue 19643).

+3

, , shutil.rmtree Windows ( Windows 7). shutil.rmtree shutil.copytree , ( 1 ), , EACCES ENOTEMPTY. , shutil.rmtree .

TL; DR: - , , , , Windows, , , . .

, .

, , , . , . rmtree, :

def removetree(tgt):
    def error_handler(func, path, execinfo):
        e = execinfo[1]
        if e.errno == errno.ENOENT or not os.path.exists(path):
            return              # path does not exist - treat as success
        if func in (os.rmdir, os.remove) and e.errno == errno.EACCES:
            os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
            func(path)          # read-only file; make writable and retry
        raise e
    tmp = os.path.join(os.path.dirname(tgt),"_removetree_tmp")
    os.rename(tgt, tmp)
    shutil.rmtree(tmp, onerror=error_handler)
    return

, , os.rename . os.rename, :

def removetree(tgt):
    def error_handler(func, path, execinfo):
        # figure out recovery based on error...
        e = execinfo[1]
        if e.errno == errno.ENOENT or not os.path.exists(path):
            return              # path does not exist
        if func in (os.rmdir, os.remove) and e.errno == errno.EACCES:
            os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
            func(path)          # read-only file; make writable and retry
        raise e
    # Rename target directory to temporary value, then remove it
    count = 0 
    while count < 10:           # prevents indefinite loop
        count += 1
        tmp = os.path.join(os.path.dirname(tgt),"_removetree_tmp_%d"%(count))
        try:
            os.rename(tgt, tmp)
            shutil.rmtree(tmp, onerror=error_handler)
            break
        except OSError as e:
            time.sleep(1)       # Give file system some time to catch up
            if e.errno in [errno.EACCES, errno.ENOTEMPTY]:
                continue        # Try another temp name
            if e.errno == errno.EEXIST:
                shutil.rmtree(tmp, ignore_errors=True)  # Try to clean up old files
                continue        # Try another temp name
            if e.errno == errno.ENOENT:
                break           # 'src' does not exist(?)
            raise               # Other error - propagate
    return

, . , , , . , , , , ( , Windows, ):

def renametree_temp(src):
    """
    Rename tree to temporary name, and return that name, or 
    None if the source directory does not exist.
    """
    count = 0 
    while count < 10:      # prevents indefinite loop
        count += 1
        tmp = os.path.join(os.path.dirname(src),"_removetree_tmp_%d"%(count))
        try:
            os.rename(src, tmp)
            return tmp      # Success!
        except OSError as e:
            time.sleep(1)
            if e.errno == errno.EACCES:
                log.warning("util.renametree_temp: %s EACCES, retrying"%tmp)
                continue    # Try another temp name
            if e.errno == errno.ENOTEMPTY:
                log.warning("util.renametree_temp: %s ENOTEMPTY, retrying"%tmp)
                continue    # Try another temp name
            if e.errno == errno.EEXIST:
                log.warning("util.renametree_temp: %s EEXIST, retrying"%tmp)
                shutil.rmtree(tmp, ignore_errors=True)  # Try to clean up old files
                continue    # Try another temp name
            if e.errno == errno.ENOENT:
                log.warning("util.renametree_temp: %s ENOENT, skipping"%tmp)
                break       # 'src' does not exist(?)
            raise           # Other error: propagaee
    return None

def removetree(tgt):
    """
    Work-around for python problem with shutils tree remove functions on Windows.
    See:
        https://stackoverflow.com/questions/23924223/
        /questions/13263/what-user-do-python-scripts-run-as-in-windows
        /questions/13265/deleting-directory-in-python
        http://bugs.python.org/issue19643
    """
    # shutil.rmtree error handler that attempts recovery from attempts 
    # on Windows to remove a read-only file or directory (see links above).
    def error_handler(func, path, execinfo):
        e = execinfo[1]
        if e.errno == errno.ENOENT or not os.path.exists(path):
            return          # path does not exist: nothing to do
        if func in (os.rmdir, os.remove) and e.errno == errno.EACCES:
            try:
                os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
            except Exception as che:
                log.warning("util.removetree: chmod failed: %s"%che)
            try:
                func(path)
            except Exception as rfe:
                log.warning("util.removetree: 'func' retry failed: %s"%rfe)
                if not os.path.exists(path):
                    return      # Gone, assume all is well
                raise
        if e.errno == errno.ENOTEMPTY:
            log.warning("util.removetree: Not empty: %s, %s"%(path, tgt))
            time.sleep(1)
            removetree(path)    # Retry complete removal
            return
        log.warning("util.removetree: rmtree path: %s, error: %s"%(path, repr(execinfo)))
        raise e
    # Try renaming to a new directory first, so that the tgt is immediately 
    # available for re-use.
    tmp = renametree_temp(tgt)
    if tmp:
        shutil.rmtree(tmp, onerror=error_handler)
    return

( python, Windows?, ​​ Python. , , , .)

+4

Source: https://habr.com/ru/post/1542407/


All Articles