Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(distutil + shutil) * copytree =?

Tags:

python

I'd like the ignore pattern that shutil's copytree function provides. And I want the src tree to replace all existent files/folders in the dest directory like distutil.dir_util.copy_tree.

I'm a fairly new Python user, and can't seem to find any posts on this.

like image 569
Michael Avatar asked Sep 25 '11 11:09

Michael


People also ask

How does Shutil Copytree work?

shutil. copytree() method recursively copies an entire directory tree rooted at source (src) to the destination directory. The destination directory, named by (dst) must not already exist. It will be created during copying.

What is the difference between Shutil copy () and Shutil Copytree ()?

While shutil. copy() will copy a single file, shutil. copytree() will copy an entire folder and every folder and file contained in it.

What is the difference between Shutil copy () and Shutil Copytree ()? What function is used to rename files give examples of each function?

The shutil. copy() function will copy a single file, while shutil. copytree() will copy an entire folder, along with all its contents.

What is Shutil used for?

copy() method in Python is used to copy the content of source file to destination file or directory. It also preserves the file's permission mode but other metadata of the file like the file's creation and modification times is not preserved.


3 Answers

I was going to say this earler but I didn't. http://docs.python.org/library/shutil.html has a version of the copytree function. I looked at it to see if it would just replace existing files and from what I could tell it would overwrite existing files, but it fails if any of the directories already exist. Due to os.mkdirs failing if the directories already exist.

The needed imports:

import os
import os.path
import shutil

taking _mkdir from http://code.activestate.com/recipes/82465-a-friendly-mkdir/ (a commenter over there mentions that os.mkdirs has most of the same behavior but doesn't notice that _mkdir doesn't fail if any of the directories to be made already exist)

def _mkdir(newdir):
    """works the way a good mkdir should :)
        - already exists, silently complete
        - regular file in the way, raise an exception
        - parent directory(ies) does not exist, make them as well
    """
    if os.path.isdir(newdir):
        pass
    elif os.path.isfile(newdir):
        raise OSError("a file with the same name as the desired " \
                      "dir, '%s', already exists." % newdir)
    else:
        head, tail = os.path.split(newdir)
        if head and not os.path.isdir(head):
            _mkdir(head)
        #print "_mkdir %s" % repr(newdir)
        if tail:
            os.mkdir(newdir)

Although it doesn't take a mode argument like os.mkdirs, copytree doesn't use that so it isn't needed.

And then change copytree to call _mkdir instead of os.mkdirs:

def copytree(src, dst, symlinks=False):
    """Recursively copy a directory tree using copy2().

    The destination directory must not already exist.
    If exception(s) occur, an Error is raised with a list of reasons.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    # os.makedirs(dst)
    _mkdir(dst) # XXX
    errors = []
    for name in names:
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks)
            else:
                shutil.copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            errors.append((srcname, dstname, str(why)))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except Error, err:
            errors.extend(err.args[0])
    try:
        shutil.copystat(src, dst)
    except WindowsError:
        # can't copy file access times on Windows
        pass
like image 177
Dan D. Avatar answered Oct 04 '22 21:10

Dan D.


Just extending Dan's answer by incorporating the ignore argument and adding shutil to the 'Error' class (for copytree):

def copytree(src, dst, symlinks=False, ignore=None):
    """Recursively copy a directory tree using copy2().

    The destination directory must not already exist.
    If exception(s) occur, an Error is raised with a list of reasons.

    If the optional symlinks flag is true, symbolic links in the
    source tree result in symbolic links in the destination tree; if
    it is false, the contents of the files pointed to by symbolic
    links are copied.

    XXX Consider this example code rather than the ultimate tool.

    """
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()

    _mkdir(dst) # XXX
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks, ignore)
            else:
                shutil.copy2(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            errors.append((srcname, dstname, str(why)))
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except shutil.Error, err:
            errors.extend(err.args[0])
    try:
        shutil.copystat(src, dst)
    except WindowsError:
        # can't copy file access times on Windows
        pass
like image 34
Peter Avatar answered Oct 04 '22 19:10

Peter


This is a wokaround with both distutils and shutil:

ignorePatterns=('.git')
shutil.copytree(src, "__TMP__", ignore=shutil.ignore_patterns(ignorePatterns))
distutils.dir_util.copy_tree("__TMP__", dest)
distutils.dir_util.remove_tree("__TMP__")

Note: This is not an efficient method, because it copies same files twice.

like image 37
Jahid Avatar answered Oct 04 '22 20:10

Jahid